Changed some names since "depth" wasn't really the right term.
[rsync.git] / authenticate.c
blob7da25d41afe90f3376b1efec493fc0f1f19e5c6f
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 */
21 #include "rsync.h"
23 /***************************************************************************
24 encode a buffer using base64 - simple and slow algorithm. null terminates
25 the result.
26 ***************************************************************************/
27 static 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;
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)));
47 out[i] = b64[idx];
51 /* create a 16 byte challenge buffer */
52 static void gen_challenge(char *addr, char *challenge)
54 char input[32];
55 struct timeval tv;
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());
65 sum_init();
66 sum_update(input, sizeof(input));
67 sum_end(challenge);
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);
76 int fd, found=0;
77 char line[MAXPATHLEN];
78 char *p, *pass=NULL;
79 STRUCT_STAT st;
80 int ok = 1;
81 extern int am_root;
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);
90 ok = 0;
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");
94 ok = 0;
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");
97 ok = 0;
100 if (!ok) {
101 rprintf(FERROR,"continuing without secrets file\n");
102 close(fd);
103 return 0;
106 while (!found) {
107 int i = 0;
108 memset(line, 0, sizeof line);
109 while ((size_t) i < (sizeof(line)-1)) {
110 if (read(fd, &line[i], 1) != 1) {
111 memset(line, 0, sizeof(line));
112 close(fd);
113 return 0;
115 if (line[i] == '\r') continue;
116 if (line[i] == '\n') break;
117 i++;
119 line[i] = 0;
120 if (line[0] == '#') continue;
121 p = strchr(line,':');
122 if (!p) continue;
123 *p = 0;
124 if (strcmp(user, line)) continue;
125 pass = p+1;
126 found = 1;
129 close(fd);
130 if (!found) return 0;
132 strlcpy(secret, pass, len);
133 return 1;
136 static char *getpassf(char *filename)
138 char buffer[100];
139 int fd=0;
140 STRUCT_STAT st;
141 int ok = 1;
142 extern int am_root;
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");
150 return NULL;
153 if (do_stat(filename, &st) == -1) {
154 rsyserr(FERROR, errno, "stat(%s)", filename);
155 ok = 0;
156 } else if ((st.st_mode & 06) != 0) {
157 rprintf(FERROR,"password file must not be other-accessible\n");
158 ok = 0;
159 } else if (am_root && (st.st_uid != 0)) {
160 rprintf(FERROR,"password file must be owned by root when running as root\n");
161 ok = 0;
163 if (!ok) {
164 rprintf(FERROR,"continuing without password file\n");
165 if (envpw) rprintf(FERROR,"using RSYNC_PASSWORD environment variable.\n");
166 close(fd);
167 return NULL;
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");
176 close(fd);
177 if (p) p = strdup(p);
178 return p;
181 return NULL;
184 /* generate a 16 byte hash from a password and challenge */
185 static void generate_hash(char *in, char *challenge, char *out)
187 char buf[16];
189 sum_init();
190 sum_update(in, strlen(in));
191 sum_update(challenge, strlen(challenge));
192 sum_end(buf);
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 char *auth_server(int f_in, int f_out, int module, char *addr, char *leader)
208 char *users = lp_auth_users(module);
209 char challenge[16];
210 char b64_challenge[30];
211 char line[MAXPATHLEN];
212 static char user[100];
213 char secret[100];
214 char pass[30];
215 char pass2[30];
216 char *tok;
218 /* if no auth list then allow anyone in! */
219 if (!users || !*users) return "";
221 gen_challenge(addr, challenge);
223 base64_encode(challenge, 16, b64_challenge);
225 io_printf(f_out, "%s%s\n", leader, b64_challenge);
227 if (!read_line(f_in, line, sizeof(line)-1)) {
228 return NULL;
231 memset(user, 0, sizeof(user));
232 memset(pass, 0, sizeof(pass));
234 if (sscanf(line,"%99s %29s", user, pass) != 2) {
235 return NULL;
238 users = strdup(users);
239 if (!users) return NULL;
241 for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
242 if (fnmatch(tok, user, 0) == 0) break;
244 free(users);
246 if (!tok) {
247 return NULL;
250 memset(secret, 0, sizeof(secret));
251 if (!get_secret(module, user, secret, sizeof(secret)-1)) {
252 memset(secret, 0, sizeof(secret));
253 return NULL;
256 generate_hash(secret, b64_challenge, pass2);
257 memset(secret, 0, sizeof(secret));
259 if (strcmp(pass, pass2) == 0)
260 return user;
262 return NULL;
266 void auth_client(int fd, char *user, char *challenge)
268 char *pass;
269 char pass2[30];
270 extern char *password_file;
272 if (!user || !*user) return;
274 if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
275 /* XXX: cyeoh says that getpass is deprecated, because
276 * it may return a truncated password on some systems,
277 * and it is not in the LSB.
279 * Andrew Klein says that getpassphrase() is present
280 * on Solaris and reads up to 256 characters.
282 * OpenBSD has a readpassphrase() that might be more suitable.
284 pass = getpass("Password: ");
287 if (!pass || !*pass) {
288 pass = "";
291 generate_hash(pass, challenge, pass2);
292 io_printf(fd, "%s %s\n", user, pass2);