fixed bug where strtok() could return NULL in getpassf().
[rsync/rsync.git] / authenticate.c
1 /* 
2    Copyright (C) Andrew Tridgell 1998
3    
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8    
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13    
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 /* support rsync authentication */
20 #include "rsync.h"
21
22 /***************************************************************************
23 encode a buffer using base64 - simple and slow algorithm. null terminates
24 the result.
25   ***************************************************************************/
26 static void base64_encode(char *buf, int len, char *out)
27 {
28         char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
29         int bit_offset, byte_offset, idx, i;
30         unsigned char *d = (unsigned char *)buf;
31         int bytes = (len*8 + 5)/6;
32
33         memset(out, 0, bytes+1);
34
35         for (i=0;i<bytes;i++) {
36                 byte_offset = (i*6)/8;
37                 bit_offset = (i*6)%8;
38                 if (bit_offset < 3) {
39                         idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
40                 } else {
41                         idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
42                         if (byte_offset+1 < len) {
43                                 idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
44                         }
45                 }
46                 out[i] = b64[idx];
47         }
48 }
49
50 /* create a 16 byte challenge buffer */
51 static void gen_challenge(char *addr, char *challenge)
52 {
53         char input[32];
54         struct timeval tv;
55
56         memset(input, 0, sizeof(input));
57
58         strlcpy((char *)input, addr, 17);
59         gettimeofday(&tv, NULL);
60         SIVAL(input, 16, tv.tv_sec);
61         SIVAL(input, 20, tv.tv_usec);
62         SIVAL(input, 24, getpid());
63
64         sum_init();
65         sum_update(input, sizeof(input));
66         sum_end(challenge);
67 }
68
69
70 /* return the secret for a user from the sercret file. maximum length
71    is len. null terminate it */
72 static int get_secret(int module, char *user, char *secret, int len)
73 {
74         char *fname = lp_secrets_file(module);
75         int fd, found=0;
76         char line[MAXPATHLEN];
77         char *p, *pass=NULL;
78         STRUCT_STAT st;
79         int ok = 1;
80         extern int am_root;
81
82         if (!fname || !*fname) return 0;
83
84         fd = open(fname,O_RDONLY);
85         if (fd == -1) return 0;
86
87         if (do_stat(fname, &st) == -1) {
88                 rprintf(FERROR,"stat(%s) : %s\n", fname, strerror(errno));
89                 ok = 0;
90         } else if ((st.st_mode & 06) != 0) {
91                 rprintf(FERROR,"secrets file must not be other-accessible\n");
92                 ok = 0;
93         } else if (am_root && (st.st_uid != 0)) {
94                 rprintf(FERROR,"secrets file must be owned by root when running as root\n");
95                 ok = 0;
96         }
97         if (!ok) {
98                 rprintf(FERROR,"continuing without secrets file\n");
99                 close(fd);
100                 return 0;
101         }
102
103         while (!found) {
104                 int i = 0;
105                 memset(line, 0, sizeof(line));
106                 while (i<(sizeof(line)-1)) {
107                         if (read(fd, &line[i], 1) != 1) {
108                                 memset(line, 0, sizeof(line));
109                                 close(fd);
110                                 return 0;
111                         }
112                         if (line[i] == '\r') continue;
113                         if (line[i] == '\n') break;
114                         i++;
115                 }
116                 line[i] = 0;
117                 if (line[0] == '#') continue;
118                 p = strchr(line,':');
119                 if (!p) continue;
120                 *p = 0;
121                 if (strcmp(user, line)) continue;
122                 pass = p+1;
123                 found = 1;
124         }
125
126         close(fd);
127         if (!found) return 0;
128
129         strlcpy(secret, pass, len);
130         return 1;
131 }
132
133 static char *getpassf(char *filename)
134 {
135         char buffer[100];
136         int len=0;
137         int fd=0;
138         STRUCT_STAT st;
139         int ok = 1;
140         extern int am_root;
141         char *envpw=getenv("RSYNC_PASSWORD");
142
143         if (!filename) return NULL;
144
145         if ( (fd=open(filename,O_RDONLY)) == -1) {
146                 rprintf(FERROR,"could not open password file \"%s\"\n",filename);
147                 if (envpw) rprintf(FERROR,"falling back to RSYNC_PASSWORD environment variable.\n");    
148                 return NULL;
149         }
150         
151         if (do_stat(filename, &st) == -1) {
152                 rprintf(FERROR,"stat(%s) : %s\n", filename, strerror(errno));
153                 ok = 0;
154         } else if ((st.st_mode & 06) != 0) {
155                 rprintf(FERROR,"password file must not be other-accessible\n");
156                 ok = 0;
157         } else if (am_root && (st.st_uid != 0)) {
158                 rprintf(FERROR,"password file must be owned by root when running as root\n");
159                 ok = 0;
160         }
161         if (!ok) {
162                 rprintf(FERROR,"continuing without password file\n");
163                 if (envpw) rprintf(FERROR,"using RSYNC_PASSWORD environment variable.\n");
164                 close(fd);
165                 return NULL;
166         }
167
168         if (envpw) rprintf(FERROR,"RSYNC_PASSWORD environment variable ignored\n");
169
170         buffer[sizeof(buffer)-1]='\0';
171         if ( (len=read(fd,buffer,sizeof(buffer)-1)) > 0)
172         {
173                 char *p = strtok(buffer,"\n\r");
174                 close(fd);
175                 if (p) p = strdup(p);
176                 return p;
177         }       
178
179         return NULL;
180 }
181
182 /* generate a 16 byte hash from a password and challenge */
183 static void generate_hash(char *in, char *challenge, char *out)
184 {
185         char buf[16];
186
187         sum_init();
188         sum_update(in, strlen(in));
189         sum_update(challenge, strlen(challenge));
190         sum_end(buf);
191
192         base64_encode(buf, 16, out);
193 }
194
195 /* possible negotiate authentication with the client. Use "leader" to
196    start off the auth if necessary 
197
198    return NULL if authentication failed
199
200    return "" if anonymous access
201
202    otherwise return username
203 */
204 char *auth_server(int fd, int module, char *addr, char *leader)
205 {
206         char *users = lp_auth_users(module);
207         char challenge[16];
208         char b64_challenge[30];
209         char line[MAXPATHLEN];
210         static char user[100];
211         char secret[100];
212         char pass[30];
213         char pass2[30];
214         char *tok;
215
216         /* if no auth list then allow anyone in! */
217         if (!users || !*users) return "";
218
219         gen_challenge(addr, challenge);
220         
221         base64_encode(challenge, 16, b64_challenge);
222
223         io_printf(fd,"%s%s\n", leader, b64_challenge);
224
225         if (!read_line(fd, line, sizeof(line)-1)) {
226                 return NULL;
227         }
228
229         memset(user, 0, sizeof(user));
230         memset(pass, 0, sizeof(pass));
231
232         if (sscanf(line,"%99s %29s", user, pass) != 2) {
233                 return NULL;
234         }
235
236         users = strdup(users);
237         if (!users) return NULL;
238
239         for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
240                 if (strcmp(tok, user) == 0) break;
241         }
242         free(users);
243
244         if (!tok) {
245                 return NULL;
246         }
247         
248         memset(secret, 0, sizeof(secret));
249         if (!get_secret(module, user, secret, sizeof(secret)-1)) {
250                 memset(secret, 0, sizeof(secret));
251                 return NULL;
252         }
253
254         generate_hash(secret, b64_challenge, pass2);
255         memset(secret, 0, sizeof(secret));
256         
257         if (strcmp(pass, pass2) == 0)
258                 return user;
259
260         return NULL;
261 }
262
263
264 void auth_client(int fd, char *user, char *challenge)
265 {
266         char *pass;
267         char pass2[30];
268         extern char *password_file;
269
270         if (!user || !*user) return;
271
272         if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
273                 pass = getpass("Password: ");
274         }
275
276         if (!pass || !*pass) {
277                 pass = "";
278         }
279
280         generate_hash(pass, challenge, pass2);
281         io_printf(fd, "%s %s\n", user, pass2);
282 }
283
284