added --existing option, similar to one suggested by Gildas Quiniou <gildas@stip.fr>
[rsync/rsync.git] / authenticate.c
... / ...
CommitLineData
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/***************************************************************************
23encode a buffer using base64 - simple and slow algorithm. null terminates
24the result.
25 ***************************************************************************/
26static 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 */
51static 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 */
72static 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
135static 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 */
185static 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*/
206char *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
266void 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