Annotation of embedaddon/rsync/authenticate.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Support rsync daemon authentication.
! 3: *
! 4: * Copyright (C) 1998-2000 Andrew Tridgell
! 5: * Copyright (C) 2002-2009 Wayne Davison
! 6: *
! 7: * This program is free software; you can redistribute it and/or modify
! 8: * it under the terms of the GNU General Public License as published by
! 9: * the Free Software Foundation; either version 3 of the License, or
! 10: * (at your option) any later version.
! 11: *
! 12: * This program is distributed in the hope that it will be useful,
! 13: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 15: * GNU General Public License for more details.
! 16: *
! 17: * You should have received a copy of the GNU General Public License along
! 18: * with this program; if not, visit the http://fsf.org website.
! 19: */
! 20:
! 21: #include "rsync.h"
! 22:
! 23: extern char *password_file;
! 24:
! 25: /***************************************************************************
! 26: encode a buffer using base64 - simple and slow algorithm. null terminates
! 27: the result.
! 28: ***************************************************************************/
! 29: void base64_encode(const char *buf, int len, char *out, int pad)
! 30: {
! 31: char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
! 32: int bit_offset, byte_offset, idx, i;
! 33: const uchar *d = (const uchar *)buf;
! 34: int bytes = (len*8 + 5)/6;
! 35:
! 36: for (i = 0; i < bytes; i++) {
! 37: byte_offset = (i*6)/8;
! 38: bit_offset = (i*6)%8;
! 39: if (bit_offset < 3) {
! 40: idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
! 41: } else {
! 42: idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
! 43: if (byte_offset+1 < len) {
! 44: idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
! 45: }
! 46: }
! 47: out[i] = b64[idx];
! 48: }
! 49:
! 50: while (pad && (i % 4))
! 51: out[i++] = '=';
! 52:
! 53: out[i] = '\0';
! 54: }
! 55:
! 56: /* Generate a challenge buffer and return it base64-encoded. */
! 57: static void gen_challenge(const char *addr, char *challenge)
! 58: {
! 59: char input[32];
! 60: char digest[MAX_DIGEST_LEN];
! 61: struct timeval tv;
! 62: int len;
! 63:
! 64: memset(input, 0, sizeof input);
! 65:
! 66: strlcpy(input, addr, 17);
! 67: sys_gettimeofday(&tv);
! 68: SIVAL(input, 16, tv.tv_sec);
! 69: SIVAL(input, 20, tv.tv_usec);
! 70: SIVAL(input, 24, getpid());
! 71:
! 72: sum_init(0);
! 73: sum_update(input, sizeof input);
! 74: len = sum_end(digest);
! 75:
! 76: base64_encode(digest, len, challenge, 0);
! 77: }
! 78:
! 79:
! 80: /* Return the secret for a user from the secret file, null terminated.
! 81: * Maximum length is len (not counting the null). */
! 82: static int get_secret(int module, const char *user, char *secret, int len)
! 83: {
! 84: const char *fname = lp_secrets_file(module);
! 85: STRUCT_STAT st;
! 86: int fd, ok = 1;
! 87: const char *p;
! 88: char ch, *s;
! 89:
! 90: if (!fname || !*fname)
! 91: return 0;
! 92:
! 93: if ((fd = open(fname, O_RDONLY)) < 0)
! 94: return 0;
! 95:
! 96: if (do_stat(fname, &st) == -1) {
! 97: rsyserr(FLOG, errno, "stat(%s)", fname);
! 98: ok = 0;
! 99: } else if (lp_strict_modes(module)) {
! 100: if ((st.st_mode & 06) != 0) {
! 101: rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
! 102: ok = 0;
! 103: } else if (MY_UID() == 0 && st.st_uid != 0) {
! 104: rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
! 105: ok = 0;
! 106: }
! 107: }
! 108: if (!ok) {
! 109: rprintf(FLOG, "continuing without secrets file\n");
! 110: close(fd);
! 111: return 0;
! 112: }
! 113:
! 114: if (*user == '#') {
! 115: /* Reject attempt to match a comment. */
! 116: close(fd);
! 117: return 0;
! 118: }
! 119:
! 120: /* Try to find a line that starts with the user name and a ':'. */
! 121: p = user;
! 122: while (1) {
! 123: if (read(fd, &ch, 1) != 1) {
! 124: close(fd);
! 125: return 0;
! 126: }
! 127: if (ch == '\n')
! 128: p = user;
! 129: else if (p) {
! 130: if (*p == ch)
! 131: p++;
! 132: else if (!*p && ch == ':')
! 133: break;
! 134: else
! 135: p = NULL;
! 136: }
! 137: }
! 138:
! 139: /* Slurp the secret into the "secret" buffer. */
! 140: s = secret;
! 141: while (len > 0) {
! 142: if (read(fd, s, 1) != 1 || *s == '\n')
! 143: break;
! 144: if (*s == '\r')
! 145: continue;
! 146: s++;
! 147: len--;
! 148: }
! 149: *s = '\0';
! 150: close(fd);
! 151:
! 152: return 1;
! 153: }
! 154:
! 155: static const char *getpassf(const char *filename)
! 156: {
! 157: STRUCT_STAT st;
! 158: char buffer[512], *p;
! 159: int fd, n;
! 160:
! 161: if (!filename)
! 162: return NULL;
! 163:
! 164: if ((fd = open(filename,O_RDONLY)) < 0) {
! 165: rsyserr(FERROR, errno, "could not open password file %s", filename);
! 166: exit_cleanup(RERR_SYNTAX);
! 167: }
! 168:
! 169: if (do_stat(filename, &st) == -1) {
! 170: rsyserr(FERROR, errno, "stat(%s)", filename);
! 171: exit_cleanup(RERR_SYNTAX);
! 172: }
! 173: if ((st.st_mode & 06) != 0) {
! 174: rprintf(FERROR, "ERROR: password file must not be other-accessible\n");
! 175: exit_cleanup(RERR_SYNTAX);
! 176: }
! 177: if (MY_UID() == 0 && st.st_uid != 0) {
! 178: rprintf(FERROR, "ERROR: password file must be owned by root when running as root\n");
! 179: exit_cleanup(RERR_SYNTAX);
! 180: }
! 181:
! 182: n = read(fd, buffer, sizeof buffer - 1);
! 183: close(fd);
! 184: if (n > 0) {
! 185: buffer[n] = '\0';
! 186: if ((p = strtok(buffer, "\n\r")) != NULL)
! 187: return strdup(p);
! 188: }
! 189:
! 190: rprintf(FERROR, "ERROR: failed to read a password from %s\n", filename);
! 191: exit_cleanup(RERR_SYNTAX);
! 192: }
! 193:
! 194: /* Generate an MD4 hash created from the combination of the password
! 195: * and the challenge string and return it base64-encoded. */
! 196: static void generate_hash(const char *in, const char *challenge, char *out)
! 197: {
! 198: char buf[MAX_DIGEST_LEN];
! 199: int len;
! 200:
! 201: sum_init(0);
! 202: sum_update(in, strlen(in));
! 203: sum_update(challenge, strlen(challenge));
! 204: len = sum_end(buf);
! 205:
! 206: base64_encode(buf, len, out, 0);
! 207: }
! 208:
! 209: /* Possibly negotiate authentication with the client. Use "leader" to
! 210: * start off the auth if necessary.
! 211: *
! 212: * Return NULL if authentication failed. Return "" if anonymous access.
! 213: * Otherwise return username.
! 214: */
! 215: char *auth_server(int f_in, int f_out, int module, const char *host,
! 216: const char *addr, const char *leader)
! 217: {
! 218: char *users = lp_auth_users(module);
! 219: char challenge[MAX_DIGEST_LEN*2];
! 220: char line[BIGPATHBUFLEN];
! 221: char secret[512];
! 222: char pass2[MAX_DIGEST_LEN*2];
! 223: char *tok, *pass;
! 224:
! 225: /* if no auth list then allow anyone in! */
! 226: if (!users || !*users)
! 227: return "";
! 228:
! 229: gen_challenge(addr, challenge);
! 230:
! 231: io_printf(f_out, "%s%s\n", leader, challenge);
! 232:
! 233: if (!read_line_old(f_in, line, sizeof line)
! 234: || (pass = strchr(line, ' ')) == NULL) {
! 235: rprintf(FLOG, "auth failed on module %s from %s (%s): "
! 236: "invalid challenge response\n",
! 237: lp_name(module), host, addr);
! 238: return NULL;
! 239: }
! 240: *pass++ = '\0';
! 241:
! 242: if (!(users = strdup(users)))
! 243: out_of_memory("auth_server");
! 244:
! 245: for (tok = strtok(users, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
! 246: if (wildmatch(tok, line))
! 247: break;
! 248: }
! 249: free(users);
! 250:
! 251: if (!tok) {
! 252: rprintf(FLOG, "auth failed on module %s from %s (%s): "
! 253: "unauthorized user\n",
! 254: lp_name(module), host, addr);
! 255: return NULL;
! 256: }
! 257:
! 258: memset(secret, 0, sizeof secret);
! 259: if (!get_secret(module, line, secret, sizeof secret - 1)) {
! 260: memset(secret, 0, sizeof secret);
! 261: rprintf(FLOG, "auth failed on module %s from %s (%s): "
! 262: "missing secret for user \"%s\"\n",
! 263: lp_name(module), host, addr, line);
! 264: return NULL;
! 265: }
! 266:
! 267: generate_hash(secret, challenge, pass2);
! 268: memset(secret, 0, sizeof secret);
! 269:
! 270: if (strcmp(pass, pass2) != 0) {
! 271: rprintf(FLOG, "auth failed on module %s from %s (%s): "
! 272: "password mismatch\n",
! 273: lp_name(module), host, addr);
! 274: return NULL;
! 275: }
! 276:
! 277: return strdup(line);
! 278: }
! 279:
! 280: void auth_client(int fd, const char *user, const char *challenge)
! 281: {
! 282: const char *pass;
! 283: char pass2[MAX_DIGEST_LEN*2];
! 284:
! 285: if (!user || !*user)
! 286: user = "nobody";
! 287:
! 288: if (!(pass = getpassf(password_file))
! 289: && !(pass = getenv("RSYNC_PASSWORD"))) {
! 290: /* XXX: cyeoh says that getpass is deprecated, because
! 291: * it may return a truncated password on some systems,
! 292: * and it is not in the LSB.
! 293: *
! 294: * Andrew Klein says that getpassphrase() is present
! 295: * on Solaris and reads up to 256 characters.
! 296: *
! 297: * OpenBSD has a readpassphrase() that might be more suitable.
! 298: */
! 299: pass = getpass("Password: ");
! 300: }
! 301:
! 302: if (!pass)
! 303: pass = "";
! 304:
! 305: generate_hash(pass, challenge, pass2);
! 306: io_printf(fd, "%s %s\n", user, pass2);
! 307: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>