ebf23d6ce97cfdfa679e3b6e3c43900b426a511a
[rsync.git] / authenticate.c
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
23 #if defined(__CYGWIN__) || defined(WIN32)
24 /* This, along with the "rt" for stdio, means that people can have
25  * CRLF in configuration files without troubles. */
26 #  define CONFIG_OPEN_MODE (O_RDONLY|O_TEXT)
27 #else
28 #  define CONFIG_OPEN_MODE O_RDONLY
29 #endif
30
31 /***************************************************************************
32 encode a buffer using base64 - simple and slow algorithm. null terminates
33 the result.
34   ***************************************************************************/
35 static void base64_encode(char *buf, int len, char *out)
36 {
37         char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
38         int bit_offset, byte_offset, idx, i;
39         unsigned char *d = (unsigned char *)buf;
40         int bytes = (len*8 + 5)/6;
41
42         memset(out, 0, bytes+1);
43
44         for (i=0;i<bytes;i++) {
45                 byte_offset = (i*6)/8;
46                 bit_offset = (i*6)%8;
47                 if (bit_offset < 3) {
48                         idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
49                 } else {
50                         idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
51                         if (byte_offset+1 < len) {
52                                 idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
53                         }
54                 }
55                 out[i] = b64[idx];
56         }
57 }
58
59 /* create a 16 byte challenge buffer */
60 static void gen_challenge(char *addr, char *challenge)
61 {
62         char input[32];
63         struct timeval tv;
64
65         memset(input, 0, sizeof(input));
66
67         strlcpy((char *)input, addr, 17);
68         sys_gettimeofday(&tv);
69         SIVAL(input, 16, tv.tv_sec);
70         SIVAL(input, 20, tv.tv_usec);
71         SIVAL(input, 24, getpid());
72
73         sum_init();
74         sum_update(input, sizeof(input));
75         sum_end(challenge);
76 }
77
78
79 /* return the secret for a user from the sercret file. maximum length
80    is len. null terminate it */
81 static int get_secret(int module, char *user, char *secret, int len)
82 {
83         char *fname = lp_secrets_file(module);
84         int fd, found=0;
85         char line[MAXPATHLEN];
86         char *p, *pass=NULL;
87         STRUCT_STAT st;
88         int ok = 1;
89         extern int am_root;
90
91         if (!fname || !*fname) return 0;
92
93         fd = open(fname, CONFIG_OPEN_MODE);
94         if (fd == -1) return 0;
95
96         if (do_stat(fname, &st) == -1) {
97                 rsyserr(FERROR, errno, "stat(%s)", fname);
98                 ok = 0;
99         } else if (lp_strict_modes(module)) {
100                 /* Under CYGWIN, we don't want to force attribute
101                    check. It can be made to work if env var CYGWIN
102                    contains "ntea" (nt extended attribute mode), but
103                    frankly, it is a MAJOR pain in the ass to get
104                    working especially when trying to run rsync as a NT
105                    service. Besides, CYGWIN permissions won't give us
106                    any added security -- the security should be
107                    handled with NTFS ACL's. */
108 #ifndef __CYGWIN__
109                 if ((st.st_mode & 06) != 0) {
110                         rprintf(FERROR,"secrets file must not be other-accessible (see strict modes option)\n");
111                         ok = 0;
112                 } else if (am_root && (st.st_uid != 0)) {
113                         rprintf(FERROR,"secrets file must be owned by root when running as root (see strict modes)\n");
114                         ok = 0;
115                 }
116 #endif
117         }
118         if (!ok) {
119                 rprintf(FERROR,"continuing without secrets file\n");
120                 close(fd);
121                 return 0;
122         }
123
124         while (!found) {
125                 int i = 0;
126                 memset(line, 0, sizeof line);
127                 while ((size_t) i < (sizeof(line)-1)) {
128                         if (read(fd, &line[i], 1) != 1) {
129                                 memset(line, 0, sizeof(line));
130                                 close(fd);
131                                 return 0;
132                         }
133                         if (line[i] == '\r') continue;
134                         if (line[i] == '\n') break;
135                         i++;
136                 }
137                 line[i] = 0;
138                 if (line[0] == '#') continue;
139                 p = strchr(line,':');
140                 if (!p) continue;
141                 *p = 0;
142                 if (strcmp(user, line)) continue;
143                 pass = p+1;
144                 found = 1;
145         }
146
147         close(fd);
148         if (!found) return 0;
149
150         strlcpy(secret, pass, len);
151         return 1;
152 }
153
154 static char *getpassf(char *filename)
155 {
156         char buffer[100];
157         int fd=0;
158         STRUCT_STAT st;
159         int ok = 1;
160         extern int am_root;
161         char *envpw=getenv("RSYNC_PASSWORD");
162
163         if (!filename) return NULL;
164
165         if ( (fd=open(filename, CONFIG_OPEN_MODE)) == -1) {
166                 rsyserr(FERROR, errno, "could not open password file \"%s\"",filename);
167                 if (envpw) rprintf(FERROR,"falling back to RSYNC_PASSWORD environment variable.\n");    
168                 return NULL;
169         }
170         
171         if (do_stat(filename, &st) == -1) {
172                 rsyserr(FERROR, errno, "stat(%s)", filename);
173                 ok = 0;
174         } 
175 #ifndef __CYGWIN__
176         /* Under CYGWIN, we don't want to force attribute check. It
177            can be made to work if env var CYGWIN contains "ntea" (nt
178            extended attribute mode), but frankly, it is a MAJOR pain
179            in the ass to get working especially when trying to run
180            rsync as a NT service. Besides, CYGWIN permissions won't
181            give us any added security -- the security should be
182            handled with NTFS ACL's. */
183         } else if ((st.st_mode & 06) != 0) {
184                 rprintf(FERROR,"password file must not be other-accessible\n");
185                 ok = 0;
186         } else if (am_root && (st.st_uid != 0)) {
187                 rprintf(FERROR,"password file must be owned by root when running as root\n");
188                 ok = 0;
189         }
190 #endif
191         if (!ok) {
192                 rprintf(FERROR,"continuing without password file\n");
193                 if (envpw) rprintf(FERROR,"using RSYNC_PASSWORD environment variable.\n");
194                 close(fd);
195                 return NULL;
196         }
197
198         if (envpw) rprintf(FERROR,"RSYNC_PASSWORD environment variable ignored\n");
199
200         buffer[sizeof(buffer)-1]='\0';
201         if (read(fd,buffer,sizeof(buffer)-1) > 0)
202         {
203                 char *p = strtok(buffer,"\n\r");
204                 close(fd);
205                 if (p) p = strdup(p);
206                 return p;
207         }       
208
209         return NULL;
210 }
211
212 /* generate a 16 byte hash from a password and challenge */
213 static void generate_hash(char *in, char *challenge, char *out)
214 {
215         char buf[16];
216
217         sum_init();
218         sum_update(in, strlen(in));
219         sum_update(challenge, strlen(challenge));
220         sum_end(buf);
221
222         base64_encode(buf, 16, out);
223 }
224
225 /* possible negotiate authentication with the client. Use "leader" to
226    start off the auth if necessary 
227
228    return NULL if authentication failed
229
230    return "" if anonymous access
231
232    otherwise return username
233 */
234 char *auth_server(int fd, int module, char *addr, char *leader)
235 {
236         char *users = lp_auth_users(module);
237         char challenge[16];
238         char b64_challenge[30];
239         char line[MAXPATHLEN];
240         static char user[100];
241         char secret[100];
242         char pass[30];
243         char pass2[30];
244         char *tok;
245
246         /* if no auth list then allow anyone in! */
247         if (!users || !*users) return "";
248
249         gen_challenge(addr, challenge);
250         
251         base64_encode(challenge, 16, b64_challenge);
252
253         io_printf(fd,"%s%s\n", leader, b64_challenge);
254
255         if (!read_line(fd, line, sizeof(line)-1)) {
256                 return NULL;
257         }
258
259         memset(user, 0, sizeof(user));
260         memset(pass, 0, sizeof(pass));
261
262         if (sscanf(line,"%99s %29s", user, pass) != 2) {
263                 return NULL;
264         }
265         
266         users = strdup(users);
267         if (!users) return NULL;
268
269         for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
270                 if (fnmatch(tok, user, 0) == 0) break;
271         }
272         free(users);
273
274         if (!tok) {
275                 return NULL;
276         }
277         
278         memset(secret, 0, sizeof(secret));
279         if (!get_secret(module, user, secret, sizeof(secret)-1)) {
280                 memset(secret, 0, sizeof(secret));
281                 return NULL;
282         }
283
284         generate_hash(secret, b64_challenge, pass2);
285         memset(secret, 0, sizeof(secret));
286         
287         if (strcmp(pass, pass2) == 0)
288                 return user;
289
290         return NULL;
291 }
292
293
294 void auth_client(int fd, char *user, char *challenge)
295 {
296         char *pass;
297         char pass2[30];
298         extern char *password_file;
299
300         if (!user || !*user) return;
301
302         if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
303                 /* XXX: cyeoh says that getpass is deprecated, because
304                    it may return a truncated password on some systems,
305                    and it is not in the LSB. */
306                 pass = getpass("Password: ");
307         }
308
309         if (!pass || !*pass) {
310                 pass = "";
311         }
312
313         generate_hash(pass, challenge, pass2);
314         io_printf(fd, "%s %s\n", user, pass2);
315 }
316
317