made the "max connections" and "lock file" local rather than global
[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 /* generate a 16 byte hash from a password and challenge */
134 static void generate_hash(char *in, char *challenge, char *out)
135 {
136         char buf[16];
137
138         sum_init();
139         sum_update(in, strlen(in));
140         sum_update(challenge, strlen(challenge));
141         sum_end(buf);
142
143         base64_encode(buf, 16, out);
144 }
145
146 /* possible negotiate authentication with the client. Use "leader" to
147    start off the auth if necessary 
148
149    return NULL if authentication failed
150
151    return "" if anonymous access
152
153    otherwise return username
154 */
155 char *auth_server(int fd, int module, char *addr, char *leader)
156 {
157         char *users = lp_auth_users(module);
158         char challenge[16];
159         char b64_challenge[30];
160         char line[MAXPATHLEN];
161         static char user[100];
162         char secret[100];
163         char pass[30];
164         char pass2[30];
165         char *tok;
166
167         /* if no auth list then allow anyone in! */
168         if (!users || !*users) return "";
169
170         gen_challenge(addr, challenge);
171         
172         base64_encode(challenge, 16, b64_challenge);
173
174         io_printf(fd,"%s%s\n", leader, b64_challenge);
175
176         if (!read_line(fd, line, sizeof(line)-1)) {
177                 return NULL;
178         }
179
180         memset(user, 0, sizeof(user));
181         memset(pass, 0, sizeof(pass));
182
183         if (sscanf(line,"%99s %29s", user, pass) != 2) {
184                 return NULL;
185         }
186
187         users = strdup(users);
188         if (!users) return NULL;
189
190         for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
191                 if (strcmp(tok, user) == 0) break;
192         }
193         free(users);
194
195         if (!tok) {
196                 return NULL;
197         }
198         
199         memset(secret, 0, sizeof(secret));
200         if (!get_secret(module, user, secret, sizeof(secret)-1)) {
201                 memset(secret, 0, sizeof(secret));
202                 return NULL;
203         }
204
205         generate_hash(secret, b64_challenge, pass2);
206         memset(secret, 0, sizeof(secret));
207         
208         if (strcmp(pass, pass2) == 0)
209                 return user;
210
211         return NULL;
212 }
213
214
215 void auth_client(int fd, char *user, char *challenge)
216 {
217         char *pass;
218         char pass2[30];
219
220         if (!user || !*user) return;
221
222         if (!(pass=getenv("RSYNC_PASSWORD"))) {
223                 pass = getpass("Password: ");
224         }
225
226         if (!pass || !*pass) {
227                 pass = "";
228         }
229
230         generate_hash(pass, challenge, pass2);
231         
232         io_printf(fd, "%s %s\n", user, pass2);
233 }
234