added --password-file patch from Alex Schlessinger <alex@inconnect.com>
[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 ((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
133static 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 */
181static 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*/
202char *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
262void 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