896366aa8ec8c71e5e6b7b18d9ddc15ce6a9217e
[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                 close(fd);
174                 return strdup(strtok(buffer,"\n\r"));
175         }       
176
177         return NULL;
178 }
179
180 /* generate a 16 byte hash from a password and challenge */
181 static void generate_hash(char *in, char *challenge, char *out)
182 {
183         char buf[16];
184
185         sum_init();
186         sum_update(in, strlen(in));
187         sum_update(challenge, strlen(challenge));
188         sum_end(buf);
189
190         base64_encode(buf, 16, out);
191 }
192
193 /* possible negotiate authentication with the client. Use "leader" to
194    start off the auth if necessary 
195
196    return NULL if authentication failed
197
198    return "" if anonymous access
199
200    otherwise return username
201 */
202 char *auth_server(int fd, int module, char *addr, char *leader)
203 {
204         char *users = lp_auth_users(module);
205         char challenge[16];
206         char b64_challenge[30];
207         char line[MAXPATHLEN];
208         static char user[100];
209         char secret[100];
210         char pass[30];
211         char pass2[30];
212         char *tok;
213
214         /* if no auth list then allow anyone in! */
215         if (!users || !*users) return "";
216
217         gen_challenge(addr, challenge);
218         
219         base64_encode(challenge, 16, b64_challenge);
220
221         io_printf(fd,"%s%s\n", leader, b64_challenge);
222
223         if (!read_line(fd, line, sizeof(line)-1)) {
224                 return NULL;
225         }
226
227         memset(user, 0, sizeof(user));
228         memset(pass, 0, sizeof(pass));
229
230         if (sscanf(line,"%99s %29s", user, pass) != 2) {
231                 return NULL;
232         }
233
234         users = strdup(users);
235         if (!users) return NULL;
236
237         for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
238                 if (strcmp(tok, user) == 0) break;
239         }
240         free(users);
241
242         if (!tok) {
243                 return NULL;
244         }
245         
246         memset(secret, 0, sizeof(secret));
247         if (!get_secret(module, user, secret, sizeof(secret)-1)) {
248                 memset(secret, 0, sizeof(secret));
249                 return NULL;
250         }
251
252         generate_hash(secret, b64_challenge, pass2);
253         memset(secret, 0, sizeof(secret));
254         
255         if (strcmp(pass, pass2) == 0)
256                 return user;
257
258         return NULL;
259 }
260
261
262 void auth_client(int fd, char *user, char *challenge)
263 {
264         char *pass;
265         char pass2[30];
266         extern char *password_file;
267
268         if (!user || !*user) return;
269
270         if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
271                 pass = getpass("Password: ");
272         }
273
274         if (!pass || !*pass) {
275                 pass = "";
276         }
277
278         generate_hash(pass, challenge, pass2);
279         io_printf(fd, "%s %s\n", user, pass2);
280 }
281
282