Changed the order of the msgcode enum so that new items now get added
[rsync/rsync.git] / authenticate.c
... / ...
CommitLineData
1/* -*- c-file-style: "linux"; -*-
2
3 Copyright (C) 1998-2000 by Andrew Tridgell
4
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.
9
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.
14
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.
18*/
19
20/* support rsync authentication */
21#include "rsync.h"
22
23extern char *password_file;
24extern int am_root;
25
26/***************************************************************************
27encode a buffer using base64 - simple and slow algorithm. null terminates
28the result.
29 ***************************************************************************/
30void base64_encode(char *buf, int len, char *out)
31{
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;
36
37 memset(out, 0, bytes+1);
38
39 for (i = 0; i < bytes; i++) {
40 byte_offset = (i*6)/8;
41 bit_offset = (i*6)%8;
42 if (bit_offset < 3) {
43 idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
44 } else {
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)));
48 }
49 }
50 out[i] = b64[idx];
51 }
52}
53
54/* create a 16 byte challenge buffer */
55static void gen_challenge(char *addr, char *challenge)
56{
57 char input[32];
58 struct timeval tv;
59
60 memset(input, 0, sizeof(input));
61
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());
67
68 sum_init();
69 sum_update(input, sizeof(input));
70 sum_end(challenge);
71}
72
73
74/* Return the secret for a user from the secret file, null terminated.
75 * Maximum length is len (not counting the null). */
76static int get_secret(int module, char *user, char *secret, int len)
77{
78 char *fname = lp_secrets_file(module);
79 STRUCT_STAT st;
80 int fd, ok = 1;
81 char ch, *p;
82
83 if (!fname || !*fname)
84 return 0;
85
86 if ((fd = open(fname, O_RDONLY)) < 0)
87 return 0;
88
89 if (do_stat(fname, &st) == -1) {
90 rsyserr(FERROR, errno, "stat(%s)", fname);
91 ok = 0;
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");
95 ok = 0;
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");
98 ok = 0;
99 }
100 }
101 if (!ok) {
102 rprintf(FERROR,"continuing without secrets file\n");
103 close(fd);
104 return 0;
105 }
106
107 if (*user == '#') {
108 /* Reject attempt to match a comment. */
109 close(fd);
110 return 0;
111 }
112
113 /* Try to find a line that starts with the user name and a ':'. */
114 p = user;
115 while (1) {
116 if (read(fd, &ch, 1) != 1) {
117 close(fd);
118 return 0;
119 }
120 if (ch == '\n')
121 p = user;
122 else if (p) {
123 if (*p == ch)
124 p++;
125 else if (!*p && ch == ':')
126 break;
127 else
128 p = NULL;
129 }
130 }
131
132 /* Slurp the secret into the "secret" buffer. */
133 p = secret;
134 while (len > 0) {
135 if (read(fd, p, 1) != 1 || *p == '\n')
136 break;
137 if (*p == '\r')
138 continue;
139 p++;
140 len--;
141 }
142 *p = '\0';
143 close(fd);
144
145 return 1;
146}
147
148static char *getpassf(char *filename)
149{
150 STRUCT_STAT st;
151 char buffer[512], *p;
152 int fd, n, ok = 1;
153 char *envpw = getenv("RSYNC_PASSWORD");
154
155 if (!filename)
156 return NULL;
157
158 if ((fd = open(filename,O_RDONLY)) < 0) {
159 rsyserr(FERROR, errno, "could not open password file \"%s\"",filename);
160 if (envpw)
161 rprintf(FERROR, "falling back to RSYNC_PASSWORD environment variable.\n");
162 return NULL;
163 }
164
165 if (do_stat(filename, &st) == -1) {
166 rsyserr(FERROR, errno, "stat(%s)", filename);
167 ok = 0;
168 } else if ((st.st_mode & 06) != 0) {
169 rprintf(FERROR,"password file must not be other-accessible\n");
170 ok = 0;
171 } else if (am_root && st.st_uid != 0) {
172 rprintf(FERROR,"password file must be owned by root when running as root\n");
173 ok = 0;
174 }
175 if (!ok) {
176 rprintf(FERROR,"continuing without password file\n");
177 if (envpw)
178 rprintf(FERROR, "using RSYNC_PASSWORD environment variable.\n");
179 close(fd);
180 return NULL;
181 }
182
183 if (envpw)
184 rprintf(FERROR, "RSYNC_PASSWORD environment variable ignored\n");
185
186 n = read(fd, buffer, sizeof buffer - 1);
187 close(fd);
188 if (n > 0) {
189 buffer[n] = '\0';
190 if ((p = strtok(buffer, "\n\r")) != NULL)
191 return strdup(p);
192 }
193
194 return NULL;
195}
196
197/* generate a 16 byte hash from a password and challenge */
198static void generate_hash(char *in, char *challenge, char *out)
199{
200 char buf[16];
201
202 sum_init();
203 sum_update(in, strlen(in));
204 sum_update(challenge, strlen(challenge));
205 sum_end(buf);
206
207 base64_encode(buf, 16, out);
208}
209
210/* possible negotiate authentication with the client. Use "leader" to
211 start off the auth if necessary
212
213 return NULL if authentication failed
214
215 return "" if anonymous access
216
217 otherwise return username
218*/
219char *auth_server(int f_in, int f_out, int module, char *addr, char *leader)
220{
221 char *users = lp_auth_users(module);
222 char challenge[16];
223 char b64_challenge[30];
224 char line[MAXPATHLEN];
225 static char user[100];
226 char secret[100];
227 char pass[30];
228 char pass2[30];
229 char *tok;
230
231 /* if no auth list then allow anyone in! */
232 if (!users || !*users) return "";
233
234 gen_challenge(addr, challenge);
235
236 base64_encode(challenge, 16, b64_challenge);
237
238 io_printf(f_out, "%s%s\n", leader, b64_challenge);
239
240 if (!read_line(f_in, line, sizeof(line)-1)) {
241 return NULL;
242 }
243
244 memset(user, 0, sizeof(user));
245 memset(pass, 0, sizeof(pass));
246
247 if (sscanf(line,"%99s %29s", user, pass) != 2) {
248 return NULL;
249 }
250
251 users = strdup(users);
252 if (!users) return NULL;
253
254 for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
255 if (wildmatch(tok, user)) break;
256 }
257 free(users);
258
259 if (!tok) {
260 return NULL;
261 }
262
263 memset(secret, 0, sizeof(secret));
264 if (!get_secret(module, user, secret, sizeof(secret)-1)) {
265 memset(secret, 0, sizeof(secret));
266 return NULL;
267 }
268
269 generate_hash(secret, b64_challenge, pass2);
270 memset(secret, 0, sizeof(secret));
271
272 if (strcmp(pass, pass2) == 0)
273 return user;
274
275 return NULL;
276}
277
278
279void auth_client(int fd, char *user, char *challenge)
280{
281 char *pass;
282 char pass2[30];
283
284 if (!user || !*user)
285 user = "nobody";
286
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.
291 *
292 * Andrew Klein says that getpassphrase() is present
293 * on Solaris and reads up to 256 characters.
294 *
295 * OpenBSD has a readpassphrase() that might be more suitable.
296 */
297 pass = getpass("Password: ");
298 }
299
300 if (!pass)
301 pass = "";
302
303 generate_hash(pass, challenge, pass2);
304 io_printf(fd, "%s %s\n", user, pass2);
305}
306
307