Annotation of embedaddon/rsync/authenticate.c, revision 1.1.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>