1 /* -*- c-file-style: "linux"; -*-
3 Copyright (C) 1998-2000 by Andrew Tridgell
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* support rsync authentication */
23 extern char *password_file;
26 /***************************************************************************
27 encode a buffer using base64 - simple and slow algorithm. null terminates
29 ***************************************************************************/
30 void base64_encode(char *buf, int len, char *out)
32 char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
33 int bit_offset, byte_offset, idx, i;
34 unsigned char *d = (unsigned char *)buf;
35 int bytes = (len*8 + 5)/6;
37 memset(out, 0, bytes+1);
39 for (i = 0; i < bytes; i++) {
40 byte_offset = (i*6)/8;
43 idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
45 idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
46 if (byte_offset+1 < len) {
47 idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
54 /* create a 16 byte challenge buffer */
55 static void gen_challenge(char *addr, char *challenge)
60 memset(input, 0, sizeof(input));
62 strlcpy((char *)input, addr, 17);
63 sys_gettimeofday(&tv);
64 SIVAL(input, 16, tv.tv_sec);
65 SIVAL(input, 20, tv.tv_usec);
66 SIVAL(input, 24, getpid());
69 sum_update(input, sizeof(input));
74 /* Return the secret for a user from the secret file, null terminated.
75 * Maximum length is len (not counting the null). */
76 static int get_secret(int module, char *user, char *secret, int len)
78 char *fname = lp_secrets_file(module);
83 if (!fname || !*fname)
86 if ((fd = open(fname, O_RDONLY)) < 0)
89 if (do_stat(fname, &st) == -1) {
90 rsyserr(FERROR, errno, "stat(%s)", fname);
92 } else if (lp_strict_modes(module)) {
93 if ((st.st_mode & 06) != 0) {
94 rprintf(FERROR,"secrets file must not be other-accessible (see strict modes option)\n");
96 } else if (am_root && (st.st_uid != 0)) {
97 rprintf(FERROR,"secrets file must be owned by root when running as root (see strict modes)\n");
102 rprintf(FERROR,"continuing without secrets file\n");
108 /* Reject attempt to match a comment. */
113 /* Try to find a line that starts with the user name and a ':'. */
116 if (read(fd, &ch, 1) != 1) {
125 else if (!*p && ch == ':')
132 /* Slurp the secret into the "secret" buffer. */
135 if (read(fd, p, 1) != 1 || *p == '\n')
148 static char *getpassf(char *filename)
151 char buffer[512], *p;
153 char *envpw = getenv("RSYNC_PASSWORD");
158 if ((fd = open(filename,O_RDONLY)) < 0) {
159 rsyserr(FERROR, errno, "could not open password file \"%s\"",filename);
161 rprintf(FERROR, "falling back to RSYNC_PASSWORD environment variable.\n");
165 if (do_stat(filename, &st) == -1) {
166 rsyserr(FERROR, errno, "stat(%s)", filename);
168 } else if ((st.st_mode & 06) != 0) {
169 rprintf(FERROR,"password file must not be other-accessible\n");
171 } else if (am_root && st.st_uid != 0) {
172 rprintf(FERROR,"password file must be owned by root when running as root\n");
176 rprintf(FERROR,"continuing without password file\n");
178 rprintf(FERROR, "using RSYNC_PASSWORD environment variable.\n");
184 rprintf(FERROR, "RSYNC_PASSWORD environment variable ignored\n");
186 n = read(fd, buffer, sizeof buffer - 1);
190 if ((p = strtok(buffer, "\n\r")) != NULL)
197 /* generate a 16 byte hash from a password and challenge */
198 static void generate_hash(char *in, char *challenge, char *out)
203 sum_update(in, strlen(in));
204 sum_update(challenge, strlen(challenge));
207 base64_encode(buf, 16, out);
210 /* possible negotiate authentication with the client. Use "leader" to
211 start off the auth if necessary
213 return NULL if authentication failed
215 return "" if anonymous access
217 otherwise return username
219 char *auth_server(int f_in, int f_out, int module, char *addr, char *leader)
221 char *users = lp_auth_users(module);
223 char b64_challenge[30];
224 char line[MAXPATHLEN];
225 static char user[100];
231 /* if no auth list then allow anyone in! */
232 if (!users || !*users) return "";
234 gen_challenge(addr, challenge);
236 base64_encode(challenge, 16, b64_challenge);
238 io_printf(f_out, "%s%s\n", leader, b64_challenge);
240 if (!read_line(f_in, line, sizeof(line)-1)) {
244 memset(user, 0, sizeof(user));
245 memset(pass, 0, sizeof(pass));
247 if (sscanf(line,"%99s %29s", user, pass) != 2) {
251 users = strdup(users);
252 if (!users) return NULL;
254 for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
255 if (wildmatch(tok, user)) break;
263 memset(secret, 0, sizeof(secret));
264 if (!get_secret(module, user, secret, sizeof(secret)-1)) {
265 memset(secret, 0, sizeof(secret));
269 generate_hash(secret, b64_challenge, pass2);
270 memset(secret, 0, sizeof(secret));
272 if (strcmp(pass, pass2) == 0)
279 void auth_client(int fd, char *user, char *challenge)
287 if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
288 /* XXX: cyeoh says that getpass is deprecated, because
289 * it may return a truncated password on some systems,
290 * and it is not in the LSB.
292 * Andrew Klein says that getpassphrase() is present
293 * on Solaris and reads up to 256 characters.
295 * OpenBSD has a readpassphrase() that might be more suitable.
297 pass = getpass("Password: ");
303 generate_hash(pass, challenge, pass2);
304 io_printf(fd, "%s %s\n", user, pass2);