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 /***************************************************************************
24 encode a buffer using base64 - simple and slow algorithm. null terminates
26 ***************************************************************************/
27 void base64_encode(char *buf, int len, char *out)
29 char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
30 int bit_offset, byte_offset, idx, i;
31 unsigned char *d = (unsigned char *)buf;
32 int bytes = (len*8 + 5)/6;
34 memset(out, 0, bytes+1);
36 for (i=0;i<bytes;i++) {
37 byte_offset = (i*6)/8;
40 idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
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)));
51 /* create a 16 byte challenge buffer */
52 static void gen_challenge(char *addr, char *challenge)
57 memset(input, 0, sizeof(input));
59 strlcpy((char *)input, addr, 17);
60 sys_gettimeofday(&tv);
61 SIVAL(input, 16, tv.tv_sec);
62 SIVAL(input, 20, tv.tv_usec);
63 SIVAL(input, 24, getpid());
66 sum_update(input, sizeof(input));
71 /* return the secret for a user from the sercret file. maximum length
72 is len. null terminate it */
73 static int get_secret(int module, char *user, char *secret, int len)
75 char *fname = lp_secrets_file(module);
77 char line[MAXPATHLEN];
83 if (!fname || !*fname) return 0;
85 fd = open(fname,O_RDONLY);
86 if (fd == -1) return 0;
88 if (do_stat(fname, &st) == -1) {
89 rsyserr(FERROR, errno, "stat(%s)", fname);
91 } else if (lp_strict_modes(module)) {
92 if ((st.st_mode & 06) != 0) {
93 rprintf(FERROR,"secrets file must not be other-accessible (see strict modes option)\n");
95 } else if (am_root && (st.st_uid != 0)) {
96 rprintf(FERROR,"secrets file must be owned by root when running as root (see strict modes)\n");
101 rprintf(FERROR,"continuing without secrets file\n");
108 memset(line, 0, sizeof(line));
109 while (i<(sizeof(line)-1)) {
110 if (read(fd, &line[i], 1) != 1) {
111 memset(line, 0, sizeof(line));
115 if (line[i] == '\r') continue;
116 if (line[i] == '\n') break;
120 if (line[0] == '#') continue;
121 p = strchr(line,':');
124 if (strcmp(user, line)) continue;
130 if (!found) return 0;
132 strlcpy(secret, pass, len);
136 static char *getpassf(char *filename)
143 char *envpw=getenv("RSYNC_PASSWORD");
145 if (!filename) return NULL;
147 if ( (fd=open(filename,O_RDONLY)) == -1) {
148 rsyserr(FERROR, errno, "could not open password file \"%s\"",filename);
149 if (envpw) rprintf(FERROR,"falling back to RSYNC_PASSWORD environment variable.\n");
153 if (do_stat(filename, &st) == -1) {
154 rsyserr(FERROR, errno, "stat(%s)", filename);
156 } else if ((st.st_mode & 06) != 0) {
157 rprintf(FERROR,"password file must not be other-accessible\n");
159 } else if (am_root && (st.st_uid != 0)) {
160 rprintf(FERROR,"password file must be owned by root when running as root\n");
164 rprintf(FERROR,"continuing without password file\n");
165 if (envpw) rprintf(FERROR,"using RSYNC_PASSWORD environment variable.\n");
170 if (envpw) rprintf(FERROR,"RSYNC_PASSWORD environment variable ignored\n");
172 buffer[sizeof(buffer)-1]='\0';
173 if (read(fd,buffer,sizeof(buffer)-1) > 0)
175 char *p = strtok(buffer,"\n\r");
177 if (p) p = strdup(p);
184 /* generate a 16 byte hash from a password and challenge */
185 static void generate_hash(char *in, char *challenge, char *out)
190 sum_update(in, strlen(in));
191 sum_update(challenge, strlen(challenge));
194 base64_encode(buf, 16, out);
197 /* possible negotiate authentication with the client. Use "leader" to
198 start off the auth if necessary
200 return NULL if authentication failed
202 return "" if anonymous access
204 otherwise return username
206 As a side effect, updates the server's copy of the encryption key,
207 in case we later decide to use a cyphered connection.
209 char *auth_server(int fd, int module, char *addr, char *leader)
211 char *users = lp_auth_users(module);
213 char b64_challenge[30];
214 char line[MAXPATHLEN];
215 static char user[100];
221 /* if no auth list then allow anyone in! */
222 if (!users || !*users) return "";
224 gen_challenge(addr, challenge);
226 base64_encode(challenge, 16, b64_challenge);
228 io_printf(fd,"%s%s\n", leader, b64_challenge);
230 if (!read_line(fd, line, sizeof(line)-1)) {
234 memset(user, 0, sizeof(user));
235 memset(pass, 0, sizeof(pass));
237 if (sscanf(line,"%99s %29s", user, pass) != 2) {
241 users = strdup(users);
242 if (!users) return NULL;
244 for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
245 if (strcmp(tok, user) == 0) break;
253 memset(secret, 0, sizeof(secret));
254 if (!get_secret(module, user, secret, sizeof(secret)-1)) {
255 memset(secret, 0, sizeof(secret));
259 generate_hash(secret, b64_challenge, pass2);
261 privacy_create_key(user, secret, b64_challenge);
263 memset(secret, 0, sizeof(secret));
265 if (strcmp(pass, pass2) == 0)
273 * Generate the client's response to the specified challenge.
274 * The password comes from either the local secret file, or the
275 * environment, or failing that from stdin.
277 * As a side effect, this creates the client's copy of the privacy
278 * key, so that if we later start encrypting we'll have the secret
281 void auth_client(int fd, char *user, char *challenge)
285 extern char *password_file;
287 if (!user || !*user) return;
289 if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
290 pass = getpass("Password: ");
293 if (!pass || !*pass) {
297 generate_hash(pass, challenge, pass2);
298 io_printf(fd, "%s %s\n", user, pass2);
300 privacy_create_key(user, pass, challenge);