authenticate.c

説明を見る。
00001 /* -*- c-file-style: "linux"; -*-
00002 
00003    Copyright (C) 1998-2000 by Andrew Tridgell
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published by
00007    the Free Software Foundation; either version 2 of the License, or
00008    (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013    GNU General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program; if not, write to the Free Software
00017    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018 */
00019 
00020 /* support rsync authentication */
00021 #include "rsync.h"
00022 
00023 extern char *password_file;
00024 
00025 /***************************************************************************
00026 encode a buffer using base64 - simple and slow algorithm. null terminates
00027 the result.
00028   ***************************************************************************/
00029 void base64_encode(char *buf, int len, char *out, int pad)
00030 {
00031         char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00032         int bit_offset, byte_offset, idx, i;
00033         unsigned char *d = (unsigned char *)buf;
00034         int bytes = (len*8 + 5)/6;
00035 
00036         for (i = 0; i < bytes; i++) {
00037                 byte_offset = (i*6)/8;
00038                 bit_offset = (i*6)%8;
00039                 if (bit_offset < 3) {
00040                         idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
00041                 } else {
00042                         idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
00043                         if (byte_offset+1 < len) {
00044                                 idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
00045                         }
00046                 }
00047                 out[i] = b64[idx];
00048         }
00049 
00050         while (pad && (i % 4))
00051                 out[i++] = '=';
00052 
00053         out[i] = '\0';
00054 }
00055 
00056 /* Generate a challenge buffer and return it base64-encoded. */
00057 static void gen_challenge(char *addr, char *challenge)
00058 {
00059         char input[32];
00060         char md4_out[MD4_SUM_LENGTH];
00061         struct timeval tv;
00062 
00063         memset(input, 0, sizeof input);
00064 
00065         strlcpy((char *)input, addr, 17);
00066         sys_gettimeofday(&tv);
00067         SIVAL(input, 16, tv.tv_sec);
00068         SIVAL(input, 20, tv.tv_usec);
00069         SIVAL(input, 24, getpid());
00070 
00071         sum_init(0);
00072         sum_update(input, sizeof input);
00073         sum_end(md4_out);
00074 
00075         base64_encode(md4_out, MD4_SUM_LENGTH, challenge, 0);
00076 }
00077 
00078 
00079 /* Return the secret for a user from the secret file, null terminated.
00080  * Maximum length is len (not counting the null). */
00081 static int get_secret(int module, char *user, char *secret, int len)
00082 {
00083         char *fname = lp_secrets_file(module);
00084         STRUCT_STAT st;
00085         int fd, ok = 1;
00086         char ch, *p;
00087 
00088         if (!fname || !*fname)
00089                 return 0;
00090 
00091         if ((fd = open(fname, O_RDONLY)) < 0)
00092                 return 0;
00093 
00094         if (do_stat(fname, &st) == -1) {
00095                 rsyserr(FLOG, errno, "stat(%s)", fname);
00096                 ok = 0;
00097         } else if (lp_strict_modes(module)) {
00098                 if ((st.st_mode & 06) != 0) {
00099                         rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
00100                         ok = 0;
00101                 } else if (MY_UID() == 0 && st.st_uid != 0) {
00102                         rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
00103                         ok = 0;
00104                 }
00105         }
00106         if (!ok) {
00107                 rprintf(FLOG, "continuing without secrets file\n");
00108                 close(fd);
00109                 return 0;
00110         }
00111 
00112         if (*user == '#') {
00113                 /* Reject attempt to match a comment. */
00114                 close(fd);
00115                 return 0;
00116         }
00117 
00118         /* Try to find a line that starts with the user name and a ':'. */
00119         p = user;
00120         while (1) {
00121                 if (read(fd, &ch, 1) != 1) {
00122                         close(fd);
00123                         return 0;
00124                 }
00125                 if (ch == '\n')
00126                         p = user;
00127                 else if (p) {
00128                         if (*p == ch)
00129                                 p++;
00130                         else if (!*p && ch == ':')
00131                                 break;
00132                         else
00133                                 p = NULL;
00134                 }
00135         }
00136 
00137         /* Slurp the secret into the "secret" buffer. */
00138         p = secret;
00139         while (len > 0) {
00140                 if (read(fd, p, 1) != 1 || *p == '\n')
00141                         break;
00142                 if (*p == '\r')
00143                         continue;
00144                 p++;
00145                 len--;
00146         }
00147         *p = '\0';
00148         close(fd);
00149 
00150         return 1;
00151 }
00152 
00153 static char *getpassf(char *filename)
00154 {
00155         STRUCT_STAT st;
00156         char buffer[512], *p;
00157         int fd, n, ok = 1;
00158         char *envpw = getenv("RSYNC_PASSWORD");
00159 
00160         if (!filename)
00161                 return NULL;
00162 
00163         if ((fd = open(filename,O_RDONLY)) < 0) {
00164                 rsyserr(FERROR, errno, "could not open password file \"%s\"",
00165                         filename);
00166                 if (envpw)
00167                         rprintf(FERROR, "falling back to RSYNC_PASSWORD environment variable.\n");
00168                 return NULL;
00169         }
00170 
00171         if (do_stat(filename, &st) == -1) {
00172                 rsyserr(FERROR, errno, "stat(%s)", filename);
00173                 ok = 0;
00174         } else if ((st.st_mode & 06) != 0) {
00175                 rprintf(FERROR,"password file must not be other-accessible\n");
00176                 ok = 0;
00177         } else if (MY_UID() == 0 && st.st_uid != 0) {
00178                 rprintf(FERROR,"password file must be owned by root when running as root\n");
00179                 ok = 0;
00180         }
00181         if (!ok) {
00182                 rprintf(FERROR,"continuing without password file\n");
00183                 if (envpw)
00184                         rprintf(FERROR, "using RSYNC_PASSWORD environment variable.\n");
00185                 close(fd);
00186                 return NULL;
00187         }
00188 
00189         if (envpw)
00190                 rprintf(FERROR, "RSYNC_PASSWORD environment variable ignored\n");
00191 
00192         n = read(fd, buffer, sizeof buffer - 1);
00193         close(fd);
00194         if (n > 0) {
00195                 buffer[n] = '\0';
00196                 if ((p = strtok(buffer, "\n\r")) != NULL)
00197                         return strdup(p);
00198         }
00199 
00200         return NULL;
00201 }
00202 
00203 /* Generate an MD4 hash created from the combination of the password
00204  * and the challenge string and return it base64-encoded. */
00205 static void generate_hash(char *in, char *challenge, char *out)
00206 {
00207         char buf[MD4_SUM_LENGTH];
00208 
00209         sum_init(0);
00210         sum_update(in, strlen(in));
00211         sum_update(challenge, strlen(challenge));
00212         sum_end(buf);
00213 
00214         base64_encode(buf, MD4_SUM_LENGTH, out, 0);
00215 }
00216 
00217 /* Possibly negotiate authentication with the client.  Use "leader" to
00218  * start off the auth if necessary.
00219  *
00220  * Return NULL if authentication failed.  Return "" if anonymous access.
00221  * Otherwise return username.
00222  */
00223 char *auth_server(int f_in, int f_out, int module, char *host, char *addr,
00224                   char *leader)
00225 {
00226         char *users = lp_auth_users(module);
00227         char challenge[MD4_SUM_LENGTH*2];
00228         char line[BIGPATHBUFLEN];
00229         char secret[512];
00230         char pass2[MD4_SUM_LENGTH*2];
00231         char *tok, *pass;
00232 
00233         /* if no auth list then allow anyone in! */
00234         if (!users || !*users)
00235                 return "";
00236 
00237         gen_challenge(addr, challenge);
00238 
00239         io_printf(f_out, "%s%s\n", leader, challenge);
00240 
00241         if (!read_line(f_in, line, sizeof line - 1)
00242          || (pass = strchr(line, ' ')) == NULL) {
00243                 rprintf(FLOG, "auth failed on module %s from %s (%s): "
00244                         "invalid challenge response\n",
00245                         lp_name(module), host, addr);
00246                 return NULL;
00247         }
00248         *pass++ = '\0';
00249 
00250         if (!(users = strdup(users)))
00251                 out_of_memory("auth_server");
00252 
00253         for (tok = strtok(users, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
00254                 if (wildmatch(tok, line))
00255                         break;
00256         }
00257         free(users);
00258 
00259         if (!tok) {
00260                 rprintf(FLOG, "auth failed on module %s from %s (%s): "
00261                         "unauthorized user\n",
00262                         lp_name(module), host, addr);
00263                 return NULL;
00264         }
00265 
00266         memset(secret, 0, sizeof secret);
00267         if (!get_secret(module, line, secret, sizeof secret - 1)) {
00268                 memset(secret, 0, sizeof secret);
00269                 rprintf(FLOG, "auth failed on module %s from %s (%s): "
00270                         "missing secret for user \"%s\"\n",
00271                         lp_name(module), host, addr, line);
00272                 return NULL;
00273         }
00274 
00275         generate_hash(secret, challenge, pass2);
00276         memset(secret, 0, sizeof secret);
00277 
00278         if (strcmp(pass, pass2) != 0) {
00279                 rprintf(FLOG, "auth failed on module %s from %s (%s): "
00280                         "password mismatch\n",
00281                         lp_name(module), host, addr);
00282                 return NULL;
00283         }
00284 
00285         return strdup(line);
00286 }
00287 
00288 
00289 void auth_client(int fd, char *user, char *challenge)
00290 {
00291         char *pass;
00292         char pass2[MD4_SUM_LENGTH*2];
00293 
00294         if (!user || !*user)
00295                 user = "nobody";
00296 
00297         if (!(pass = getpassf(password_file))
00298          && !(pass = getenv("RSYNC_PASSWORD"))) {
00299                 /* XXX: cyeoh says that getpass is deprecated, because
00300                  * it may return a truncated password on some systems,
00301                  * and it is not in the LSB.
00302                  *
00303                  * Andrew Klein says that getpassphrase() is present
00304                  * on Solaris and reads up to 256 characters.
00305                  *
00306                  * OpenBSD has a readpassphrase() that might be more suitable.
00307                  */
00308                 pass = getpass("Password: ");
00309         }
00310 
00311         if (!pass)
00312                 pass = "";
00313 
00314         generate_hash(pass, challenge, pass2);
00315         io_printf(fd, "%s %s\n", user, pass2);
00316 }

rsyncに対してSat Dec 5 19:45:40 2009に生成されました。  doxygen 1.4.7