Tweaked the sizeof syntax and some multi-statement lines.
[rsync/rsync.git] / authenticate.c
CommitLineData
4a13b9d5
MP
1/* -*- c-file-style: "linux"; -*-
2
3 Copyright (C) 1998-2000 by Andrew Tridgell
31593dd6
AT
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
38cab94d
WD
23extern char *password_file;
24extern int am_root;
25
bcb7e502
AT
26/***************************************************************************
27encode a buffer using base64 - simple and slow algorithm. null terminates
28the result.
29 ***************************************************************************/
57385128 30void base64_encode(char *buf, int len, char *out)
bcb7e502
AT
31{
32 char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
33 int bit_offset, byte_offset, idx, i;
34 unsigned char *d = (unsigned char *)buf;
bcb7e502
AT
35 int bytes = (len*8 + 5)/6;
36
37 memset(out, 0, bytes+1);
38
57385128 39 for (i = 0; i < bytes; i++) {
bcb7e502
AT
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
58c9b4b7 60 memset(input, 0, sizeof input);
bcb7e502 61
37f9805d 62 strlcpy((char *)input, addr, 17);
3060d4aa 63 sys_gettimeofday(&tv);
bcb7e502
AT
64 SIVAL(input, 16, tv.tv_sec);
65 SIVAL(input, 20, tv.tv_usec);
66 SIVAL(input, 24, getpid());
67
68 sum_init();
58c9b4b7 69 sum_update(input, sizeof input);
bcb7e502
AT
70 sum_end(challenge);
71}
72
73
38cab94d
WD
74/* Return the secret for a user from the secret file, null terminated.
75 * Maximum length is len (not counting the null). */
bcb7e502
AT
76static int get_secret(int module, char *user, char *secret, int len)
77{
78 char *fname = lp_secrets_file(module);
d1be2312 79 STRUCT_STAT st;
38cab94d
WD
80 int fd, ok = 1;
81 char ch, *p;
bcb7e502 82
38cab94d
WD
83 if (!fname || !*fname)
84 return 0;
bcb7e502 85
38cab94d
WD
86 if ((fd = open(fname, O_RDONLY)) < 0)
87 return 0;
bcb7e502 88
d1be2312 89 if (do_stat(fname, &st) == -1) {
a039749b 90 rsyserr(FERROR, errno, "stat(%s)", fname);
d1be2312 91 ok = 0;
3ca8e68f
DD
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 }
d1be2312
DD
100 }
101 if (!ok) {
102 rprintf(FERROR,"continuing without secrets file\n");
103 close(fd);
104 return 0;
105 }
106
38cab94d
WD
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;
bcb7e502 129 }
bcb7e502
AT
130 }
131
38cab94d
WD
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';
bcb7e502 143 close(fd);
bcb7e502 144
bcb7e502
AT
145 return 1;
146}
147
65575e96
AT
148static char *getpassf(char *filename)
149{
65575e96 150 STRUCT_STAT st;
38cab94d
WD
151 char buffer[512], *p;
152 int fd, n, ok = 1;
153 char *envpw = getenv("RSYNC_PASSWORD");
65575e96 154
38cab94d
WD
155 if (!filename)
156 return NULL;
65575e96 157
38cab94d 158 if ((fd = open(filename,O_RDONLY)) < 0) {
4a13b9d5 159 rsyserr(FERROR, errno, "could not open password file \"%s\"",filename);
38cab94d
WD
160 if (envpw)
161 rprintf(FERROR, "falling back to RSYNC_PASSWORD environment variable.\n");
65575e96
AT
162 return NULL;
163 }
164
165 if (do_stat(filename, &st) == -1) {
a039749b 166 rsyserr(FERROR, errno, "stat(%s)", filename);
65575e96
AT
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;
38cab94d 171 } else if (am_root && st.st_uid != 0) {
65575e96
AT
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");
38cab94d
WD
177 if (envpw)
178 rprintf(FERROR, "using RSYNC_PASSWORD environment variable.\n");
65575e96
AT
179 close(fd);
180 return NULL;
181 }
182
38cab94d
WD
183 if (envpw)
184 rprintf(FERROR, "RSYNC_PASSWORD environment variable ignored\n");
65575e96 185
38cab94d
WD
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);
65575e96
AT
192 }
193
194 return NULL;
195}
196
bcb7e502 197/* generate a 16 byte hash from a password and challenge */
6e4fb64e 198static void generate_hash(char *in, char *challenge, char *out)
bcb7e502
AT
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
d0d56395
AT
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*/
973007da 219char *auth_server(int f_in, int f_out, int module, char *addr, char *leader)
bcb7e502
AT
220{
221 char *users = lp_auth_users(module);
222 char challenge[16];
223 char b64_challenge[30];
e42c9458 224 char line[MAXPATHLEN];
d0d56395 225 static char user[100];
bcb7e502
AT
226 char secret[100];
227 char pass[30];
228 char pass2[30];
c8e78d87 229 char *tok;
bcb7e502
AT
230
231 /* if no auth list then allow anyone in! */
58c9b4b7
WD
232 if (!users || !*users)
233 return "";
bcb7e502
AT
234
235 gen_challenge(addr, challenge);
236
237 base64_encode(challenge, 16, b64_challenge);
238
973007da 239 io_printf(f_out, "%s%s\n", leader, b64_challenge);
bcb7e502 240
58c9b4b7 241 if (!read_line(f_in, line, sizeof line - 1))
d0d56395 242 return NULL;
bcb7e502 243
58c9b4b7
WD
244 memset(user, 0, sizeof user);
245 memset(pass, 0, sizeof pass);
bcb7e502 246
58c9b4b7 247 if (sscanf(line,"%99s %29s", user, pass) != 2)
d0d56395 248 return NULL;
5d78a102 249
c8e78d87 250 users = strdup(users);
58c9b4b7
WD
251 if (!users)
252 return NULL;
c8e78d87
AT
253
254 for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
58c9b4b7
WD
255 if (wildmatch(tok, user))
256 break;
c8e78d87
AT
257 }
258 free(users);
259
58c9b4b7 260 if (!tok)
d0d56395 261 return NULL;
c8e78d87 262
58c9b4b7
WD
263 memset(secret, 0, sizeof secret);
264 if (!get_secret(module, user, secret, sizeof secret - 1)) {
265 memset(secret, 0, sizeof secret);
d0d56395 266 return NULL;
bcb7e502
AT
267 }
268
269 generate_hash(secret, b64_challenge, pass2);
58c9b4b7 270 memset(secret, 0, sizeof secret);
bcb7e502 271
d0d56395
AT
272 if (strcmp(pass, pass2) == 0)
273 return user;
274
275 return NULL;
bcb7e502
AT
276}
277
278
279void auth_client(int fd, char *user, char *challenge)
280{
281 char *pass;
282 char pass2[30];
283
ef383c0d 284 if (!user || !*user)
4b2f6a7c 285 user = "nobody";
bcb7e502 286
58c9b4b7
WD
287 if (!(pass = getpassf(password_file))
288 && !(pass = getenv("RSYNC_PASSWORD"))) {
64bd7568 289 /* XXX: cyeoh says that getpass is deprecated, because
908f5a9f
MP
290 * it may return a truncated password on some systems,
291 * and it is not in the LSB.
292 *
293 * Andrew Klein says that getpassphrase() is present
294 * on Solaris and reads up to 256 characters.
295 *
296 * OpenBSD has a readpassphrase() that might be more suitable.
297 */
bcb7e502
AT
298 pass = getpass("Password: ");
299 }
300
38cab94d 301 if (!pass)
bcb7e502 302 pass = "";
bcb7e502
AT
303
304 generate_hash(pass, challenge, pass2);
bcb7e502
AT
305 io_printf(fd, "%s %s\n", user, pass2);
306}
307
65575e96 308