Made base64_encode() non-static.
[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 ((size_t) 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 char *auth_server(int f_in, int f_out, int module, char *addr, char *leader)
207 {
208         char *users = lp_auth_users(module);
209         char challenge[16];
210         char b64_challenge[30];
211         char line[MAXPATHLEN];
212         static char user[100];
213         char secret[100];
214         char pass[30];
215         char pass2[30];
216         char *tok;
217
218         /* if no auth list then allow anyone in! */
219         if (!users || !*users) return "";
220
221         gen_challenge(addr, challenge);
222         
223         base64_encode(challenge, 16, b64_challenge);
224
225         io_printf(f_out, "%s%s\n", leader, b64_challenge);
226
227         if (!read_line(f_in, line, sizeof(line)-1)) {
228                 return NULL;
229         }
230
231         memset(user, 0, sizeof(user));
232         memset(pass, 0, sizeof(pass));
233
234         if (sscanf(line,"%99s %29s", user, pass) != 2) {
235                 return NULL;
236         }
237         
238         users = strdup(users);
239         if (!users) return NULL;
240
241         for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
242                 if (wildmatch(tok, user)) break;
243         }
244         free(users);
245
246         if (!tok) {
247                 return NULL;
248         }
249         
250         memset(secret, 0, sizeof(secret));
251         if (!get_secret(module, user, secret, sizeof(secret)-1)) {
252                 memset(secret, 0, sizeof(secret));
253                 return NULL;
254         }
255
256         generate_hash(secret, b64_challenge, pass2);
257         memset(secret, 0, sizeof(secret));
258         
259         if (strcmp(pass, pass2) == 0)
260                 return user;
261
262         return NULL;
263 }
264
265
266 void auth_client(int fd, char *user, char *challenge)
267 {
268         char *pass;
269         char pass2[30];
270         extern char *password_file;
271
272         if (!user || !*user) return;
273
274         if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
275                 /* XXX: cyeoh says that getpass is deprecated, because
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                  */
284                 pass = getpass("Password: ");
285         }
286
287         if (!pass || !*pass) {
288                 pass = "";
289         }
290
291         generate_hash(pass, challenge, pass2);
292         io_printf(fd, "%s %s\n", user, pass2);
293 }
294
295