Diff for /embedaddon/rsync/authenticate.c between versions 1.1.1.1 and 1.1.1.4

version 1.1.1.1, 2012/02/17 15:09:30 version 1.1.1.4, 2021/03/17 00:32:36
Line 2 Line 2
  * Support rsync daemon authentication.   * Support rsync daemon authentication.
  *   *
  * Copyright (C) 1998-2000 Andrew Tridgell   * Copyright (C) 1998-2000 Andrew Tridgell
 * Copyright (C) 2002-2009 Wayne Davison * Copyright (C) 2002-2020 Wayne Davison
  *   *
  * This program is free software; you can redistribute it and/or modify   * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by   * it under the terms of the GNU General Public License as published by
Line 19 Line 19
  */   */
   
 #include "rsync.h"  #include "rsync.h"
   #include "itypes.h"
   #include "ifuncs.h"
   
   extern int read_only;
 extern char *password_file;  extern char *password_file;
   
 /***************************************************************************  /***************************************************************************
Line 69  static void gen_challenge(const char *addr, char *chal Line 72  static void gen_challenge(const char *addr, char *chal
         SIVAL(input, 20, tv.tv_usec);          SIVAL(input, 20, tv.tv_usec);
         SIVAL(input, 24, getpid());          SIVAL(input, 24, getpid());
   
        sum_init(0);        sum_init(-1, 0);
         sum_update(input, sizeof input);          sum_update(input, sizeof input);
         len = sum_end(digest);          len = sum_end(digest);
   
         base64_encode(digest, len, challenge, 0);          base64_encode(digest, len, challenge, 0);
 }  }
   
   /* Generate an MD4 hash created from the combination of the password
    * and the challenge string and return it base64-encoded. */
   static void generate_hash(const char *in, const char *challenge, char *out)
   {
           char buf[MAX_DIGEST_LEN];
           int len;
   
           sum_init(-1, 0);
           sum_update(in, strlen(in));
           sum_update(challenge, strlen(challenge));
           len = sum_end(buf);
   
           base64_encode(buf, len, out, 0);
   }
   
 /* Return the secret for a user from the secret file, null terminated.  /* Return the secret for a user from the secret file, null terminated.
  * Maximum length is len (not counting the null). */   * Maximum length is len (not counting the null). */
static int get_secret(int module, const char *user, char *secret, int len)static const char *check_secret(int module, const char *user, const char *group,
                                 const char *challenge, const char *pass)
 {  {
           char line[1024];
           char pass2[MAX_DIGEST_LEN*2];
         const char *fname = lp_secrets_file(module);          const char *fname = lp_secrets_file(module);
         STRUCT_STAT st;          STRUCT_STAT st;
        int fd, ok = 1;        int ok = 1;
        const char *p;        int user_len = strlen(user);
        char ch, *s;        int group_len = group ? strlen(group) : 0;
         char *err;
         FILE *fh;
   
        if (!fname || !*fname)        if (!fname || !*fname || (fh = fopen(fname, "r")) == NULL)
                return 0;                return "no secrets file";
   
        if ((fd = open(fname, O_RDONLY)) < 0)        if (do_fstat(fileno(fh), &st) == -1) {
                return 0;                rsyserr(FLOG, errno, "fstat(%s)", fname);
 
        if (do_stat(fname, &st) == -1) { 
                rsyserr(FLOG, errno, "stat(%s)", fname); 
                 ok = 0;                  ok = 0;
         } else if (lp_strict_modes(module)) {          } else if (lp_strict_modes(module)) {
                 if ((st.st_mode & 06) != 0) {                  if ((st.st_mode & 06) != 0) {
                         rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");                          rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
                         ok = 0;                          ok = 0;
                } else if (MY_UID() == 0 && st.st_uid != 0) {                } else if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
                         rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");                          rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
                         ok = 0;                          ok = 0;
                 }                  }
         }          }
         if (!ok) {          if (!ok) {
                rprintf(FLOG, "continuing without secrets file\n");                fclose(fh);
                close(fd);                return "ignoring secrets file";
                return 0; 
         }          }
   
         if (*user == '#') {          if (*user == '#') {
                 /* Reject attempt to match a comment. */                  /* Reject attempt to match a comment. */
                close(fd);                fclose(fh);
                return 0;                return "invalid username";
         }          }
   
        /* Try to find a line that starts with the user name and a ':'. */        /* Try to find a line that starts with the user (or @group) name and a ':'. */
        p = user;        err = "secret not found";
        while (1) {        while ((user || group) && fgets(line, sizeof line, fh) != NULL) {
                if (read(fd, &ch, 1) != 1) {                const char **ptr, *s = strtok(line, "\n\r");
                        close(fd);                int len;
                        return 0;                if (!s)
                         continue;
                 if (*s == '@') {
                         ptr = &group;
                         len = group_len;
                         s++;
                 } else {
                         ptr = &user;
                         len = user_len;
                 }                  }
                if (ch == '\n')                if (!*ptr || strncmp(s, *ptr, len) != 0 || s[len] != ':')
                        p = user;                        continue;
                else if (p) {                generate_hash(s+len+1, challenge, pass2);
                        if (*p == ch)                if (strcmp(pass, pass2) == 0) {
                                p++;                        err = NULL;
                        else if (!*p && ch == ':')                        break;
                                break; 
                        else 
                                p = NULL; 
                 }                  }
                   err = "password mismatch";
                   *ptr = NULL; /* Don't look for name again. */
         }          }
   
        /* Slurp the secret into the "secret" buffer. */        fclose(fh);
        s = secret; 
        while (len > 0) { 
                if (read(fd, s, 1) != 1 || *s == '\n') 
                        break; 
                if (*s == '\r') 
                        continue; 
                s++; 
                len--; 
        } 
        *s = '\0'; 
        close(fd); 
   
        return 1;        force_memzero(line, sizeof line);
         force_memzero(pass2, sizeof pass2);
 
         return err;
 }  }
   
 static const char *getpassf(const char *filename)  static const char *getpassf(const char *filename)
 {  {
         STRUCT_STAT st;          STRUCT_STAT st;
         char buffer[512], *p;          char buffer[512], *p;
        int fd, n;        int n;
   
         if (!filename)          if (!filename)
                 return NULL;                  return NULL;
   
        if ((fd = open(filename,O_RDONLY)) < 0) {        if (strcmp(filename, "-") == 0) {
                rsyserr(FERROR, errno, "could not open password file %s", filename);                n = fgets(buffer, sizeof buffer, stdin) == NULL ? -1 : (int)strlen(buffer);
                exit_cleanup(RERR_SYNTAX);        } else {
        }                int fd;
   
        if (do_stat(filename, &st) == -1) {                if ((fd = open(filename,O_RDONLY)) < 0) {
                rsyserr(FERROR, errno, "stat(%s)", filename);                        rsyserr(FERROR, errno, "could not open password file %s", filename);
                exit_cleanup(RERR_SYNTAX);                        exit_cleanup(RERR_SYNTAX);
                 }
 
                 if (do_stat(filename, &st) == -1) {
                         rsyserr(FERROR, errno, "stat(%s)", filename);
                         exit_cleanup(RERR_SYNTAX);
                 }
                 if ((st.st_mode & 06) != 0) {
                         rprintf(FERROR, "ERROR: password file must not be other-accessible\n");
                         exit_cleanup(RERR_SYNTAX);
                 }
                 if (MY_UID() == ROOT_UID && st.st_uid != ROOT_UID) {
                         rprintf(FERROR, "ERROR: password file must be owned by root when running as root\n");
                         exit_cleanup(RERR_SYNTAX);
                 }
 
                 n = read(fd, buffer, sizeof buffer - 1);
                 close(fd);
         }          }
         if ((st.st_mode & 06) != 0) {  
                 rprintf(FERROR, "ERROR: password file must not be other-accessible\n");  
                 exit_cleanup(RERR_SYNTAX);  
         }  
         if (MY_UID() == 0 && st.st_uid != 0) {  
                 rprintf(FERROR, "ERROR: password file must be owned by root when running as root\n");  
                 exit_cleanup(RERR_SYNTAX);  
         }  
   
         n = read(fd, buffer, sizeof buffer - 1);  
         close(fd);  
         if (n > 0) {          if (n > 0) {
                 buffer[n] = '\0';                  buffer[n] = '\0';
                 if ((p = strtok(buffer, "\n\r")) != NULL)                  if ((p = strtok(buffer, "\n\r")) != NULL)
Line 191  static const char *getpassf(const char *filename) Line 215  static const char *getpassf(const char *filename)
         exit_cleanup(RERR_SYNTAX);          exit_cleanup(RERR_SYNTAX);
 }  }
   
 /* Generate an MD4 hash created from the combination of the password  
  * and the challenge string and return it base64-encoded. */  
 static void generate_hash(const char *in, const char *challenge, char *out)  
 {  
         char buf[MAX_DIGEST_LEN];  
         int len;  
   
         sum_init(0);  
         sum_update(in, strlen(in));  
         sum_update(challenge, strlen(challenge));  
         len = sum_end(buf);  
   
         base64_encode(buf, len, out, 0);  
 }  
   
 /* Possibly negotiate authentication with the client.  Use "leader" to  /* Possibly negotiate authentication with the client.  Use "leader" to
  * start off the auth if necessary.   * start off the auth if necessary.
  *   *
Line 218  char *auth_server(int f_in, int f_out, int module, con Line 227  char *auth_server(int f_in, int f_out, int module, con
         char *users = lp_auth_users(module);          char *users = lp_auth_users(module);
         char challenge[MAX_DIGEST_LEN*2];          char challenge[MAX_DIGEST_LEN*2];
         char line[BIGPATHBUFLEN];          char line[BIGPATHBUFLEN];
        char secret[512];        const char **auth_uid_groups = NULL;
        char pass2[MAX_DIGEST_LEN*2];        int auth_uid_groups_cnt = -1;
         const char *err = NULL;
         int group_match = -1;
         char *tok, *pass;          char *tok, *pass;
           char opt_ch = '\0';
   
         /* if no auth list then allow anyone in! */          /* if no auth list then allow anyone in! */
         if (!users || !*users)          if (!users || !*users)
Line 230  char *auth_server(int f_in, int f_out, int module, con Line 242  char *auth_server(int f_in, int f_out, int module, con
   
         io_printf(f_out, "%s%s\n", leader, challenge);          io_printf(f_out, "%s%s\n", leader, challenge);
   
        if (!read_line_old(f_in, line, sizeof line)        if (!read_line_old(f_in, line, sizeof line, 0)
          || (pass = strchr(line, ' ')) == NULL) {           || (pass = strchr(line, ' ')) == NULL) {
                 rprintf(FLOG, "auth failed on module %s from %s (%s): "                  rprintf(FLOG, "auth failed on module %s from %s (%s): "
                         "invalid challenge response\n",                          "invalid challenge response\n",
Line 239  char *auth_server(int f_in, int f_out, int module, con Line 251  char *auth_server(int f_in, int f_out, int module, con
         }          }
         *pass++ = '\0';          *pass++ = '\0';
   
        if (!(users = strdup(users)))        users = strdup(users);
                out_of_memory("auth_server"); 
   
         for (tok = strtok(users, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {          for (tok = strtok(users, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
                if (wildmatch(tok, line))                char *opts;
                        break;                /* See if the user appended :deny, :ro, or :rw. */
                 if ((opts = strchr(tok, ':')) != NULL) {
                         *opts++ = '\0';
                         opt_ch = isUpper(opts) ? toLower(opts) : *opts;
                         if (opt_ch == 'r') { /* handle ro and rw */
                                 opt_ch = isUpper(opts+1) ? toLower(opts+1) : opts[1];
                                 if (opt_ch == 'o')
                                         opt_ch = 'r';
                                 else if (opt_ch != 'w')
                                         opt_ch = '\0';
                         } else if (opt_ch != 'd') /* if it's not deny, ignore it */
                                 opt_ch = '\0';
                 } else
                         opt_ch = '\0';
                 if (*tok != '@') {
                         /* Match the username */
                         if (wildmatch(tok, line))
                                 break;
                 } else {
 #ifdef HAVE_GETGROUPLIST
                         int j;
                         /* See if authorizing user is a real user, and if so, see
                          * if it is in a group that matches tok+1 wildmat. */
                         if (auth_uid_groups_cnt < 0) {
                                 item_list gid_list = EMPTY_ITEM_LIST;
                                 uid_t auth_uid;
                                 if (!user_to_uid(line, &auth_uid, False)
                                  || getallgroups(auth_uid, &gid_list) != NULL)
                                         auth_uid_groups_cnt = 0;
                                 else {
                                         gid_t *gid_array = gid_list.items;
                                         auth_uid_groups_cnt = gid_list.count;
                                         auth_uid_groups = new_array(const char *, auth_uid_groups_cnt);
                                         for (j = 0; j < auth_uid_groups_cnt; j++)
                                                 auth_uid_groups[j] = gid_to_group(gid_array[j]);
                                 }
                         }
                         for (j = 0; j < auth_uid_groups_cnt; j++) {
                                 if (auth_uid_groups[j] && wildmatch(tok+1, auth_uid_groups[j])) {
                                         group_match = j;
                                         break;
                                 }
                         }
                         if (group_match >= 0)
                                 break;
 #else
                         rprintf(FLOG, "your computer doesn't support getgrouplist(), so no @group authorization is possible.\n");
 #endif
                 }
         }          }
   
         free(users);          free(users);
   
        if (!tok) {        if (!tok)
                rprintf(FLOG, "auth failed on module %s from %s (%s): "                err = "no matching rule";
                        "unauthorized user\n",        else if (opt_ch == 'd')
                        lp_name(module), host, addr);                err = "denied by rule";
                return NULL;        else {
                 const char *group = group_match >= 0 ? auth_uid_groups[group_match] : NULL;
                 err = check_secret(module, line, group, challenge, pass);
         }          }
   
        memset(secret, 0, sizeof secret);        force_memzero(challenge, sizeof challenge);
        if (!get_secret(module, line, secret, sizeof secret - 1)) {        force_memzero(pass, strlen(pass));
                memset(secret, 0, sizeof secret);
                rprintf(FLOG, "auth failed on module %s from %s (%s): "        if (auth_uid_groups) {
                        "missing secret for user \"%s\"\n",                int j;
                        lp_name(module), host, addr, line);                for (j = 0; j < auth_uid_groups_cnt; j++) {
                return NULL;                        if (auth_uid_groups[j])
                                 free((char*)auth_uid_groups[j]);
                 }
                 free(auth_uid_groups);
         }          }
   
        generate_hash(secret, challenge, pass2);        if (err) {
        memset(secret, 0, sizeof secret);                rprintf(FLOG, "auth failed on module %s from %s (%s) for %s: %s\n",
                        lp_name(module), host, addr, line, err);
        if (strcmp(pass, pass2) != 0) { 
                rprintf(FLOG, "auth failed on module %s from %s (%s): " 
                        "password mismatch\n", 
                        lp_name(module), host, addr); 
                 return NULL;                  return NULL;
         }          }
   
           if (opt_ch == 'r')
                   read_only = 1;
           else if (opt_ch == 'w')
                   read_only = 0;
   
         return strdup(line);          return strdup(line);
 }  }
   
Line 290  void auth_client(int fd, const char *user, const char  Line 356  void auth_client(int fd, const char *user, const char 
                 /* XXX: cyeoh says that getpass is deprecated, because                  /* XXX: cyeoh says that getpass is deprecated, because
                  * it may return a truncated password on some systems,                   * it may return a truncated password on some systems,
                  * and it is not in the LSB.                   * and it is not in the LSB.
                 *                 *
                 * Andrew Klein says that getpassphrase() is present                 * Andrew Klein says that getpassphrase() is present
                 * on Solaris and reads up to 256 characters.                 * on Solaris and reads up to 256 characters.
                 *                 *
                 * OpenBSD has a readpassphrase() that might be more suitable.                 * OpenBSD has a readpassphrase() that might be more suitable.
                 */                 */
                 pass = getpass("Password: ");                  pass = getpass("Password: ");
         }          }
   

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.4


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>