Print strerror when a system error occurs; add a new function rsyserr
[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                 rsyserr(FERROR, errno, "stat(%s)", fname);
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 fd=0;
139         STRUCT_STAT st;
140         int ok = 1;
141         extern int am_root;
142         char *envpw=getenv("RSYNC_PASSWORD");
143
144         if (!filename) return NULL;
145
146         if ( (fd=open(filename,O_RDONLY)) == -1) {
147                 rprintf(FERROR,"could not open password file \"%s\"\n",filename);
148                 if (envpw) rprintf(FERROR,"falling back to RSYNC_PASSWORD environment variable.\n");    
149                 return NULL;
150         }
151         
152         if (do_stat(filename, &st) == -1) {
153                 rsyserr(FERROR, errno, "stat(%s)", filename);
154                 ok = 0;
155         } else if ((st.st_mode & 06) != 0) {
156                 rprintf(FERROR,"password file must not be other-accessible\n");
157                 ok = 0;
158         } else if (am_root && (st.st_uid != 0)) {
159                 rprintf(FERROR,"password file must be owned by root when running as root\n");
160                 ok = 0;
161         }
162         if (!ok) {
163                 rprintf(FERROR,"continuing without password file\n");
164                 if (envpw) rprintf(FERROR,"using RSYNC_PASSWORD environment variable.\n");
165                 close(fd);
166                 return NULL;
167         }
168
169         if (envpw) rprintf(FERROR,"RSYNC_PASSWORD environment variable ignored\n");
170
171         buffer[sizeof(buffer)-1]='\0';
172         if (read(fd,buffer,sizeof(buffer)-1) > 0)
173         {
174                 char *p = strtok(buffer,"\n\r");
175                 close(fd);
176                 if (p) p = strdup(p);
177                 return p;
178         }       
179
180         return NULL;
181 }
182
183 /* generate a 16 byte hash from a password and challenge */
184 static void generate_hash(char *in, char *challenge, char *out)
185 {
186         char buf[16];
187
188         sum_init();
189         sum_update(in, strlen(in));
190         sum_update(challenge, strlen(challenge));
191         sum_end(buf);
192
193         base64_encode(buf, 16, out);
194 }
195
196 /* possible negotiate authentication with the client. Use "leader" to
197    start off the auth if necessary 
198
199    return NULL if authentication failed
200
201    return "" if anonymous access
202
203    otherwise return username
204 */
205 char *auth_server(int fd, int module, char *addr, char *leader)
206 {
207         char *users = lp_auth_users(module);
208         char challenge[16];
209         char b64_challenge[30];
210         char line[MAXPATHLEN];
211         static char user[100];
212         char secret[100];
213         char pass[30];
214         char pass2[30];
215         char *tok;
216
217         /* if no auth list then allow anyone in! */
218         if (!users || !*users) return "";
219
220         gen_challenge(addr, challenge);
221         
222         base64_encode(challenge, 16, b64_challenge);
223
224         io_printf(fd,"%s%s\n", leader, b64_challenge);
225
226         if (!read_line(fd, line, sizeof(line)-1)) {
227                 return NULL;
228         }
229
230         memset(user, 0, sizeof(user));
231         memset(pass, 0, sizeof(pass));
232
233         if (sscanf(line,"%99s %29s", user, pass) != 2) {
234                 return NULL;
235         }
236
237         users = strdup(users);
238         if (!users) return NULL;
239
240         for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
241                 if (strcmp(tok, user) == 0) break;
242         }
243         free(users);
244
245         if (!tok) {
246                 return NULL;
247         }
248         
249         memset(secret, 0, sizeof(secret));
250         if (!get_secret(module, user, secret, sizeof(secret)-1)) {
251                 memset(secret, 0, sizeof(secret));
252                 return NULL;
253         }
254
255         generate_hash(secret, b64_challenge, pass2);
256         memset(secret, 0, sizeof(secret));
257         
258         if (strcmp(pass, pass2) == 0)
259                 return user;
260
261         return NULL;
262 }
263
264
265 void auth_client(int fd, char *user, char *challenge)
266 {
267         char *pass;
268         char pass2[30];
269         extern char *password_file;
270
271         if (!user || !*user) return;
272
273         if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
274                 pass = getpass("Password: ");
275         }
276
277         if (!pass || !*pass) {
278                 pass = "";
279         }
280
281         generate_hash(pass, challenge, pass2);
282         io_printf(fd, "%s %s\n", user, pass2);
283 }
284
285