went back to non-blokcing IO
[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         sys_gettimeofday(&tv);
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 (lp_strict_modes(module)) {
91                 if ((st.st_mode & 06) != 0) {
92                         rprintf(FERROR,"secrets file must not be other-accessible (see strict modes option)\n");
93                         ok = 0;
94                 } else if (am_root && (st.st_uid != 0)) {
95                         rprintf(FERROR,"secrets file must be owned by root when running as root (see strict modes)\n");
96                         ok = 0;
97                 }
98         }
99         if (!ok) {
100                 rprintf(FERROR,"continuing without secrets file\n");
101                 close(fd);
102                 return 0;
103         }
104
105         while (!found) {
106                 int i = 0;
107                 memset(line, 0, sizeof(line));
108                 while (i<(sizeof(line)-1)) {
109                         if (read(fd, &line[i], 1) != 1) {
110                                 memset(line, 0, sizeof(line));
111                                 close(fd);
112                                 return 0;
113                         }
114                         if (line[i] == '\r') continue;
115                         if (line[i] == '\n') break;
116                         i++;
117                 }
118                 line[i] = 0;
119                 if (line[0] == '#') continue;
120                 p = strchr(line,':');
121                 if (!p) continue;
122                 *p = 0;
123                 if (strcmp(user, line)) continue;
124                 pass = p+1;
125                 found = 1;
126         }
127
128         close(fd);
129         if (!found) return 0;
130
131         strlcpy(secret, pass, len);
132         return 1;
133 }
134
135 static char *getpassf(char *filename)
136 {
137         char buffer[100];
138         int len=0;
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                 rprintf(FERROR,"could not open password file \"%s\"\n",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                 rprintf(FERROR,"stat(%s) : %s\n", filename, strerror(errno));
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 ( (len=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 fd, 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(fd,"%s%s\n", leader, b64_challenge);
226
227         if (!read_line(fd, 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 (strcmp(tok, user) == 0) 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                 pass = getpass("Password: ");
276         }
277
278         if (!pass || !*pass) {
279                 pass = "";
280         }
281
282         generate_hash(pass, challenge, pass2);
283         io_printf(fd, "%s %s\n", user, pass2);
284 }
285
286