Implement --privacy option, though apparently not working yet.
[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 /***************************************************************************
24 encode a buffer using base64 - simple and slow algorithm. null terminates
25 the result.
26   ***************************************************************************/
27 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;
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 */
52 static void gen_challenge(char *addr, char *challenge)
53 {
54         char input[32];
55         struct timeval tv;
56
57         memset(input, 0, sizeof(input));
58
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());
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 */
73 static int get_secret(int module, char *user, char *secret, int len)
74 {
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;
82
83         if (!fname || !*fname) return 0;
84
85         fd = open(fname,O_RDONLY);
86         if (fd == -1) return 0;
87
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;
98                 }
99         }
100         if (!ok) {
101                 rprintf(FERROR,"continuing without secrets file\n");
102                 close(fd);
103                 return 0;
104         }
105
106         while (!found) {
107                 int i = 0;
108                 memset(line, 0, sizeof(line));
109                 while (i<(sizeof(line)-1)) {
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
132         strlcpy(secret, pass, len);
133         return 1;
134 }
135
136 static char *getpassf(char *filename)
137 {
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");
144
145         if (!filename) return NULL;
146
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;
151         }
152         
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;
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';
173         if (read(fd,buffer,sizeof(buffer)-1) > 0)
174         {
175                 char *p = strtok(buffer,"\n\r");
176                 close(fd);
177                 if (p) p = strdup(p);
178                 return p;
179         }       
180
181         return NULL;
182 }
183
184 /* generate a 16 byte hash from a password and challenge */
185 static void generate_hash(char *in, char *challenge, char *out)
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
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
206    As a side effect, updates the server's copy of the encryption key,
207    in case we later decide to use a cyphered connection.
208 */
209 char *auth_server(int fd, int module, char *addr, char *leader)
210 {
211         char *users = lp_auth_users(module);
212         char challenge[16];
213         char b64_challenge[30];
214         char line[MAXPATHLEN];
215         static char user[100];
216         char secret[100];
217         char pass[30];
218         char pass2[30];
219         char *tok;
220
221         /* if no auth list then allow anyone in! */
222         if (!users || !*users) return "";
223
224         gen_challenge(addr, challenge);
225         
226         base64_encode(challenge, 16, b64_challenge);
227
228         io_printf(fd,"%s%s\n", leader, b64_challenge);
229
230         if (!read_line(fd, line, sizeof(line)-1)) {
231                 return NULL;
232         }
233
234         memset(user, 0, sizeof(user));
235         memset(pass, 0, sizeof(pass));
236
237         if (sscanf(line,"%99s %29s", user, pass) != 2) {
238                 return NULL;
239         }
240
241         users = strdup(users);
242         if (!users) return NULL;
243
244         for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
245                 if (strcmp(tok, user) == 0) break;
246         }
247         free(users);
248
249         if (!tok) {
250                 return NULL;
251         }
252         
253         memset(secret, 0, sizeof(secret));
254         if (!get_secret(module, user, secret, sizeof(secret)-1)) {
255                 memset(secret, 0, sizeof(secret));
256                 return NULL;
257         }
258
259         generate_hash(secret, b64_challenge, pass2);
260
261         privacy_create_key(user, secret, b64_challenge);
262
263         memset(secret, 0, sizeof(secret));
264         
265         if (strcmp(pass, pass2) == 0)
266                 return user;
267
268         return NULL;
269 }
270
271
272 /*
273  * Generate the client's response to the specified challenge.
274  * The password comes from either the local secret file, or the
275  * environment, or failing that from stdin.
276  *
277  * As a side effect, this creates the client's copy of the privacy
278  * key, so that if we later start encrypting we'll have the secret
279  * already in place.
280  */
281 void auth_client(int fd, char *user, char *challenge)
282 {
283         char *pass;
284         char pass2[30];
285         extern char *password_file;
286
287         if (!user || !*user) return;
288
289         if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
290                 pass = getpass("Password: ");
291         }
292
293         if (!pass || !*pass) {
294                 pass = "";
295         }
296
297         generate_hash(pass, challenge, pass2);
298         io_printf(fd, "%s %s\n", user, pass2);
299
300         privacy_create_key(user, pass, challenge);
301 }
302
303