Logo Search packages:      
Sourcecode: dcc version File versions  Download package

load_ids.c

/* Distributed Checksum Clearinghouse
 *
 * client-ID and password parsing
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.28 $Revision$
 */

#include "dcc_clnt.h"
#include "dcc_ids.h"
#include "dcc_heap_debug.h"


const char *ids_nm = IDS_NM_DEF;

time_t ids_mtime;


/* authenticated client database
 * assume there will be about 8000 clients and servers known to each server */
#define ID_TBL_LEN 4001             /* must be prime */
#define ID_TBL_MAX (ID_TBL_LEN*4)
#define ID_HASH(id) id_tbl_hash[id % ID_TBL_LEN]
static ID_TBL *id_tbl_hash[ID_TBL_LEN];
static int id_tbl_len = 0;
static ID_TBL *id_tbl_free;


/* find an ID_TBL entry */
ID_TBL *
find_id_tbl(DCC_CLNT_ID id)
{
      ID_TBL *tp;

      for (tp = ID_HASH(id); tp != 0; tp = tp->fwd) {
            if (tp->id == id)
                  return tp;
      }
      return 0;
}



u_char
parse_dccd_delay(DCC_EMSG emsg, time_t *delay_usp, u_int *delay_inflatep,
             const char *val,
             const char *fnm, int lineno)
{
      time_t delay_ms;
      u_long l;
      char *p1, *p2;

      delay_ms = strtoul(val, &p1, 0);

      if (delay_ms > DCC_ANON_DELAY_MAX/1000) {
            dcc_pemsg(EX_DATAERR, emsg, "invalid delay \"%s\"%s", val,
                    fnm_lineno(fnm, lineno));
            return 0;
      } else if (*p1 == '\0') {
            *delay_inflatep = DCC_ANON_INFLATE_OFF;
      } else if (*p1 != ',' && *p1 != '*') {
            dcc_pemsg(EX_DATAERR, emsg, "unrecognized delay \"%s\"%s", val,
                    fnm_lineno(fnm, lineno));
            return 0;
      } else {
            l = strtoul(++p1, &p2, 0);
            if (*p2 != '\0') {
                  dcc_pemsg(EX_DATAERR, emsg,
                          "unrecognized delay inflation \"%s\"%s",
                          p1,
                          fnm_lineno(fnm, lineno));
                  return 0;
            }
            if (l == 0)
                  l = DCC_ANON_INFLATE_OFF;
            *delay_inflatep = l;
      }

      *delay_usp = delay_ms*1000;
      return 1;
}



/* (re)load the client-ID and password database */
int                           /* -1=failed, 0=sick file, 1=ok */
load_ids(DCC_EMSG emsg, ID_TBL **tgt_tbl, DCC_CLNT_ID tgt_id)
{
      ID_TBL t, *tp, **tpp;
      FILE *f;
      int lineno, total;
      u_char found_it;
      int status;
      char buf[sizeof(ID_TBL)*2+1];
      const char *bufp;
      char id_buf[30];
      struct stat sb;
      char *p;
      int i;

      if (tgt_tbl)
            *tgt_tbl = 0;

      f = fopen(ids_nm, "r");
      if (!f) {
            dcc_pemsg(EX_NOINPUT, emsg, "fopen(%s): %s",
                    DCC_NM2PATH(ids_nm), ERROR_STR());
            return -1;
      }

      /* the file contains passwords, so refuse to use it if anyone else
       * can read it */
      if (!dcc_ck_private(emsg, &sb, ids_nm, fileno(f))) {
            fclose(f);
            ids_mtime = 0;
            return -1;
      }

      ids_mtime = sb.st_mtime;

      /* Mark the existing IDs so we can delete them if they've
       * disappeared from the ASCII file.  This is more complicated
       * than deleting and recreating the table from scratch, but the
       * results are better when a bad entry is added to the file. */
      for (i = 0; i < DIM(id_tbl_hash); i++) {
            for (tpp = &id_tbl_hash[i]; (tp = *tpp) != 0; tpp = &tp->fwd)
                  tp->flags &= ~ID_FLG_MARKED;
      }

      total = 0;
      lineno = 0;
      status = 1;
      found_it = (tgt_id == DCC_ID_ANON);
      for (;;) {
            /* read and parse a line contain a client-ID and key(s) */
            bufp = fgets(buf, sizeof(buf), f);
            if (!bufp) {
                  if (ferror(f))
                        dcc_pemsg(EX_IOERR, emsg,
                                "fgets(ID file %s): %s",
                                DCC_NM2PATH(ids_nm), ERROR_STR());
                  break;
            }
            ++lineno;

            /* Ignore blank lines and lines starting with '#'.
             * Note that '#' flags a comment only at the start of
             * the line to avoid dealing with the escaping hassles
             * of allowing '#' in passwords. */
            bufp += strspn(bufp, DCC_WHITESPACE);
            if (*bufp == '\0' || *bufp == '#')
                  continue;

            memset(&t, 0, sizeof(t));
            t.delay_inflate = DCC_ANON_INFLATE_OFF;

            /* Each substantive line has the form:
             *
             *    ID[,rpt-ok][,delay=ms] password1 password2
             *
             *  Both passwords are always accepted.  They are intended
             *  to be the previous and current or the current and
             *  next to allow the password to be changed at both the
             *  client and the server without loss of service. */

            bufp = dcc_parse_word(emsg, id_buf, sizeof(id_buf),
                              bufp, "ID", ids_nm, lineno);
            if (!bufp) {
                  if (status > 0)
                        status = 0;
                  continue;
            }

            p = strchr(id_buf, ',');
            if (p)
                  *p++ = '\0';
            t.id = dcc_get_id(emsg, id_buf, ids_nm, lineno);
            if (t.id == DCC_ID_INVALID) {
                  if (status > 0)
                        status = 0;
                  continue;
            }
            if (t.id == DCC_ID_ANON) {
                  dcc_pemsg(EX_DATAERR, emsg, "invalid ID \"%s\"%s",
                          id_buf, fnm_lineno(ids_nm, lineno));
                  if (status > 0)
                        status = 0;
                  continue;
            }

            while (p) {
                  char *p1 = strchr(p, ',');
                  if (p1)
                        *p1++ = '\0';
                  if (t.id >= DCC_CLNT_ID_MIN
                      && t.id <= DCC_CLNT_ID_MAX
                      && (!strcasecmp(p, "rpt-ok")
                        || !strcasecmp(p, "rpt_ok"))) {
                        t.flags |= ID_FLG_RPT_OK;

                  } else if (t.id >= DCC_CLNT_ID_MIN
                           && t.id <= DCC_CLNT_ID_MAX
                           && !CSTRCMP(p, "delay=")) {
                        if (!parse_dccd_delay(emsg, &t.delay_us,
                                          &t.delay_inflate,
                                          p+STRZ("delay="),
                                          ids_nm, lineno))
                              if (status > 0)
                                  status = 0;

                  } else if (status > 0) {
                        dcc_pemsg(EX_DATAERR, emsg,
                                "invalid option \"%s\"%s", p,
                                fnm_lineno(ids_nm, lineno));
                        status = 0;
                  }

                  p = p1;
            }

            bufp = dcc_parse_word(emsg, t.cur_passwd, sizeof(t.cur_passwd),
                              bufp, "current password", ids_nm, lineno);
            if (!bufp) {
                  if (status > 0)
                        status = 0;
                  continue;
            }
            bufp = dcc_parse_word(emsg,
                              t.next_passwd, sizeof(t.next_passwd),
                              bufp, "next password", ids_nm, lineno);
            if (!bufp) {
                  if (status > 0)
                        status = 0;
                  continue;
            }
            if (*bufp != '\0') {
                  dcc_pemsg(EX_DATAERR, emsg,
                          "invalid next password for ID %d%s",
                          t.id, fnm_lineno(ids_nm, lineno));
                  if (status > 0)
                        status = 0;
                  continue;
            }

            /* get rid of the old "unknown" place holding passwords */
            if (!strcmp(t.next_passwd, "unknown"))
                  memset(t.next_passwd, 0, sizeof(t.next_passwd));
            if (!strcmp(t.cur_passwd, "unknown")) {
                  strncpy(t.cur_passwd, t.next_passwd,
                        sizeof(t.cur_passwd));
                  memset(t.next_passwd, 0, sizeof(t.next_passwd));
            }

            /* put the entry into the hash table if it is not already
             * present and update it if it is */
            for (tpp = &ID_HASH(t.id); ; tpp = &tp->fwd) {
                  tp = *tpp;
                  if (!tp) {
                        /* it was not present, so add it */
                        if (!id_tbl_free) {
                              /* make more entries if necessary */
                              i = 16;
                              if (id_tbl_len <= ID_TBL_MAX
                                  && id_tbl_len+i > ID_TBL_MAX)
                                  dcc_error_msg("ID table overflow");
                              id_tbl_len += i;
                              tp = dcc_malloc(i*sizeof(*tp));
                              if (!tp) {
                                  dcc_pemsg(EX_OSERR, emsg,
                                          "malloc(%d IDs) failed",
                                          i);
                                  if (status > 0)
                                    status = 0;
                                  goto bad_id;
                              }
                              do {
                                  tp->fwd = id_tbl_free;
                                  id_tbl_free = tp;
                              } while (++tp, --i > 0);
                        }
                        /* use the next free entry */
                        tp = id_tbl_free;
                        id_tbl_free = tp->fwd;
                        memset(tp, 0, sizeof(*tp));
                        tp->id = t.id;
                        *tpp = tp;
                        break;
                  }

                  if (tp->id == t.id) {
                        /* If the ID is already present,
                         * the file is bad unless the previous
                         * or current line is a mere marker. */
                        if ((tp->flags & ID_FLG_MARKED)
                            && tp->cur_passwd[0] != '\0'
                            && t.cur_passwd[0] != '\0') {
                              dcc_pemsg(EX_DATAERR, emsg,
                                      "duplicate ID %d%s",
                                      t.id,
                                      fnm_lineno(ids_nm, lineno));
                              if (status > 0)
                                  status = 0;
                        }
                        break;
                  }
            }

            if (tp->flags & ID_FLG_MARKED) {
                  if (t.flags & ID_FLG_RPT_OK)
                        tp->flags |= ID_FLG_RPT_OK;
                  if (t.delay_us != 0) {
                        tp->delay_us = t.delay_us;
                        tp->delay_inflate = t.delay_inflate;
                  }
            } else {
                  tp->flags |= ID_FLG_MARKED;
                  tp->flags &= ~ID_FLG_RPT_OK;
                  if (t.flags & ID_FLG_RPT_OK)
                        tp->flags |= ID_FLG_RPT_OK;
                  tp->delay_us = t.delay_us;
                  tp->delay_inflate = t.delay_inflate;
            }

            if (t.cur_passwd[0] != '\0') {
                  ++total;
                  strncpy(tp->cur_passwd, t.cur_passwd,
                        sizeof(tp->cur_passwd));
                  strncpy(tp->next_passwd, t.next_passwd,
                        sizeof(tp->next_passwd));
                  /* remember special password */
                  if (tp->id == tgt_id
                      && t.cur_passwd[0] != '\0') {
                        found_it = 1;
                        if (tgt_tbl)
                              *tgt_tbl = tp;
                  }
            }
bad_id:;
      }
      fclose(f);

      if (status > 0 && !total) {
            dcc_pemsg(EX_DATAERR, emsg, "%s contains no passwords",
                    DCC_NM2PATH(ids_nm));
            status = 0;
      }
      if (!found_it) {
            dcc_pemsg(EX_DATAERR, emsg,
                    "%s does not contain the password for ID %d",
                    DCC_NM2PATH(ids_nm), tgt_id);
            status = -1;
      }

      /* If things are ok so far, forget old entries.
       * This ensures that we do not forget the server
       * password when we are given a sick database */
      if (status > 0) {
            for (i = 0; i < DIM(id_tbl_hash); i++) {
                  tpp = &id_tbl_hash[i];
                  while ((tp = *tpp) != 0) {
                        /* skip entries seen in the file */
                        if (tp->flags & ID_FLG_MARKED) {
                              tpp = &tp->fwd;
                              continue;
                        }
                        if (tp->id == tgt_id) {
                              dcc_pemsg(EX_DATAERR, emsg,
                                      "missing server passwd in %s",
                                      DCC_NM2PATH(ids_nm));
                              tpp = &tp->fwd;
                              status = 0;
                        } else {
                              /* delete other entries */
                              *tpp = tp->fwd;
                              memset(tp, 0, sizeof(*tp));
                              tp->fwd = id_tbl_free;
                              id_tbl_free = tp;
                        }
                  }
            }
      }

      return status;
}



/* load the ids file if it has changed */
u_char                              /* 0=failed, 1=reloaded, 2=up to date */
check_load_ids(DCC_EMSG emsg, DCC_CLNT_ID tgt_id)
{
      struct stat sb;

      if (!dcc_ck_private(emsg, &sb, ids_nm, -1)) {
            ids_mtime = 0;
            return 0;
      }

      if (ids_mtime == sb.st_mtime)
            return 2;

      return load_ids(emsg, 0, tgt_id) > 0;
}

Generated by  Doxygen 1.6.0   Back to index