Changed lib/fnmatch.o to lib/wildmatch.o.
[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
bcb7e502
AT
23/***************************************************************************
24encode a buffer using base64 - simple and slow algorithm. null terminates
25the result.
26 ***************************************************************************/
27static void base64_encode(char *buf, int len, char *out)
28{
29 char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
30 int bit_offset, byte_offset, idx, i;
31 unsigned char *d = (unsigned char *)buf;
bcb7e502
AT
32 int bytes = (len*8 + 5)/6;
33
34 memset(out, 0, bytes+1);
35
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)));
45 }
46 }
47 out[i] = b64[idx];
48 }
49}
50
51/* create a 16 byte challenge buffer */
52static void gen_challenge(char *addr, char *challenge)
53{
54 char input[32];
55 struct timeval tv;
56
57 memset(input, 0, sizeof(input));
58
37f9805d 59 strlcpy((char *)input, addr, 17);
3060d4aa 60 sys_gettimeofday(&tv);
bcb7e502
AT
61 SIVAL(input, 16, tv.tv_sec);
62 SIVAL(input, 20, tv.tv_usec);
63 SIVAL(input, 24, getpid());
64
65 sum_init();
66 sum_update(input, sizeof(input));
67 sum_end(challenge);
68}
69
70
71/* return the secret for a user from the sercret file. maximum length
72 is len. null terminate it */
73static int get_secret(int module, char *user, char *secret, int len)
74{
75 char *fname = lp_secrets_file(module);
76 int fd, found=0;
e42c9458
AT
77 char line[MAXPATHLEN];
78 char *p, *pass=NULL;
d1be2312
DD
79 STRUCT_STAT st;
80 int ok = 1;
81 extern int am_root;
bcb7e502
AT
82
83 if (!fname || !*fname) return 0;
84
73ff7209 85 fd = open(fname,O_RDONLY);
bcb7e502
AT
86 if (fd == -1) return 0;
87
d1be2312 88 if (do_stat(fname, &st) == -1) {
a039749b 89 rsyserr(FERROR, errno, "stat(%s)", fname);
d1be2312 90 ok = 0;
3ca8e68f
DD
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;
98 }
d1be2312
DD
99 }
100 if (!ok) {
101 rprintf(FERROR,"continuing without secrets file\n");
102 close(fd);
103 return 0;
104 }
105
bcb7e502
AT
106 while (!found) {
107 int i = 0;
707de534
MP
108 memset(line, 0, sizeof line);
109 while ((size_t) i < (sizeof(line)-1)) {
bcb7e502
AT
110 if (read(fd, &line[i], 1) != 1) {
111 memset(line, 0, sizeof(line));
112 close(fd);
113 return 0;
114 }
115 if (line[i] == '\r') continue;
116 if (line[i] == '\n') break;
117 i++;
118 }
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;
127 }
128
129 close(fd);
130 if (!found) return 0;
131
1a016bfd 132 strlcpy(secret, pass, len);
bcb7e502
AT
133 return 1;
134}
135
65575e96
AT
136static char *getpassf(char *filename)
137{
138 char buffer[100];
65575e96
AT
139 int fd=0;
140 STRUCT_STAT st;
141 int ok = 1;
142 extern int am_root;
143 char *envpw=getenv("RSYNC_PASSWORD");
144
145 if (!filename) return NULL;
146
73ff7209 147 if ( (fd=open(filename,O_RDONLY)) == -1) {
4a13b9d5 148 rsyserr(FERROR, errno, "could not open password file \"%s\"",filename);
65575e96
AT
149 if (envpw) rprintf(FERROR,"falling back to RSYNC_PASSWORD environment variable.\n");
150 return NULL;
151 }
152
153 if (do_stat(filename, &st) == -1) {
a039749b 154 rsyserr(FERROR, errno, "stat(%s)", filename);
65575e96
AT
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;
162 }
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;
168 }
169
170 if (envpw) rprintf(FERROR,"RSYNC_PASSWORD environment variable ignored\n");
171
172 buffer[sizeof(buffer)-1]='\0';
ae682c3e 173 if (read(fd,buffer,sizeof(buffer)-1) > 0)
65575e96 174 {
379e689d 175 char *p = strtok(buffer,"\n\r");
65575e96 176 close(fd);
379e689d
AT
177 if (p) p = strdup(p);
178 return p;
65575e96
AT
179 }
180
181 return NULL;
182}
183
bcb7e502 184/* generate a 16 byte hash from a password and challenge */
6e4fb64e 185static void generate_hash(char *in, char *challenge, char *out)
bcb7e502
AT
186{
187 char buf[16];
188
189 sum_init();
190 sum_update(in, strlen(in));
191 sum_update(challenge, strlen(challenge));
192 sum_end(buf);
193
194 base64_encode(buf, 16, out);
195}
196
197/* possible negotiate authentication with the client. Use "leader" to
d0d56395
AT
198 start off the auth if necessary
199
200 return NULL if authentication failed
201
202 return "" if anonymous access
203
204 otherwise return username
205*/
973007da 206char *auth_server(int f_in, int f_out, int module, char *addr, char *leader)
bcb7e502
AT
207{
208 char *users = lp_auth_users(module);
209 char challenge[16];
210 char b64_challenge[30];
e42c9458 211 char line[MAXPATHLEN];
d0d56395 212 static char user[100];
bcb7e502
AT
213 char secret[100];
214 char pass[30];
215 char pass2[30];
c8e78d87 216 char *tok;
bcb7e502
AT
217
218 /* if no auth list then allow anyone in! */
d0d56395 219 if (!users || !*users) return "";
bcb7e502
AT
220
221 gen_challenge(addr, challenge);
222
223 base64_encode(challenge, 16, b64_challenge);
224
973007da 225 io_printf(f_out, "%s%s\n", leader, b64_challenge);
bcb7e502 226
973007da 227 if (!read_line(f_in, line, sizeof(line)-1)) {
d0d56395 228 return NULL;
bcb7e502
AT
229 }
230
231 memset(user, 0, sizeof(user));
232 memset(pass, 0, sizeof(pass));
233
234 if (sscanf(line,"%99s %29s", user, pass) != 2) {
d0d56395 235 return NULL;
bcb7e502 236 }
5d78a102 237
c8e78d87 238 users = strdup(users);
d0d56395 239 if (!users) return NULL;
c8e78d87
AT
240
241 for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
5d78a102 242 if (fnmatch(tok, user, 0) == 0) break;
c8e78d87
AT
243 }
244 free(users);
245
246 if (!tok) {
d0d56395 247 return NULL;
c8e78d87
AT
248 }
249
bcb7e502
AT
250 memset(secret, 0, sizeof(secret));
251 if (!get_secret(module, user, secret, sizeof(secret)-1)) {
252 memset(secret, 0, sizeof(secret));
d0d56395 253 return NULL;
bcb7e502
AT
254 }
255
256 generate_hash(secret, b64_challenge, pass2);
257 memset(secret, 0, sizeof(secret));
258
d0d56395
AT
259 if (strcmp(pass, pass2) == 0)
260 return user;
261
262 return NULL;
bcb7e502
AT
263}
264
265
266void auth_client(int fd, char *user, char *challenge)
267{
268 char *pass;
269 char pass2[30];
65575e96 270 extern char *password_file;
bcb7e502
AT
271
272 if (!user || !*user) return;
273
65575e96 274 if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
64bd7568 275 /* XXX: cyeoh says that getpass is deprecated, because
908f5a9f
MP
276 * it may return a truncated password on some systems,
277 * and it is not in the LSB.
278 *
279 * Andrew Klein says that getpassphrase() is present
280 * on Solaris and reads up to 256 characters.
281 *
282 * OpenBSD has a readpassphrase() that might be more suitable.
283 */
bcb7e502
AT
284 pass = getpass("Password: ");
285 }
286
287 if (!pass || !*pass) {
288 pass = "";
289 }
290
291 generate_hash(pass, challenge, pass2);
bcb7e502
AT
292 io_printf(fd, "%s %s\n", user, pass2);
293}
294
65575e96 295