Print strerror when a system error occurs; add a new function rsyserr
[rsync/rsync.git] / authenticate.c
CommitLineData
31593dd6
AT
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
bcb7e502
AT
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;
bcb7e502
AT
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
37f9805d 58 strlcpy((char *)input, addr, 17);
3060d4aa 59 sys_gettimeofday(&tv);
bcb7e502
AT
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;
e42c9458
AT
76 char line[MAXPATHLEN];
77 char *p, *pass=NULL;
d1be2312
DD
78 STRUCT_STAT st;
79 int ok = 1;
80 extern int am_root;
bcb7e502
AT
81
82 if (!fname || !*fname) return 0;
83
84 fd = open(fname,O_RDONLY);
85 if (fd == -1) return 0;
86
d1be2312 87 if (do_stat(fname, &st) == -1) {
a039749b 88 rsyserr(FERROR, errno, "stat(%s)", fname);
d1be2312 89 ok = 0;
3ca8e68f
DD
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 }
d1be2312
DD
98 }
99 if (!ok) {
100 rprintf(FERROR,"continuing without secrets file\n");
101 close(fd);
102 return 0;
103 }
104
bcb7e502
AT
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
1a016bfd 131 strlcpy(secret, pass, len);
bcb7e502
AT
132 return 1;
133}
134
65575e96
AT
135static char *getpassf(char *filename)
136{
137 char buffer[100];
65575e96
AT
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) {
a039749b 153 rsyserr(FERROR, errno, "stat(%s)", filename);
65575e96
AT
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';
ae682c3e 172 if (read(fd,buffer,sizeof(buffer)-1) > 0)
65575e96 173 {
379e689d 174 char *p = strtok(buffer,"\n\r");
65575e96 175 close(fd);
379e689d
AT
176 if (p) p = strdup(p);
177 return p;
65575e96
AT
178 }
179
180 return NULL;
181}
182
bcb7e502 183/* generate a 16 byte hash from a password and challenge */
6e4fb64e 184static void generate_hash(char *in, char *challenge, char *out)
bcb7e502
AT
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
d0d56395
AT
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*/
205char *auth_server(int fd, int module, char *addr, char *leader)
bcb7e502
AT
206{
207 char *users = lp_auth_users(module);
208 char challenge[16];
209 char b64_challenge[30];
e42c9458 210 char line[MAXPATHLEN];
d0d56395 211 static char user[100];
bcb7e502
AT
212 char secret[100];
213 char pass[30];
214 char pass2[30];
c8e78d87 215 char *tok;
bcb7e502
AT
216
217 /* if no auth list then allow anyone in! */
d0d56395 218 if (!users || !*users) return "";
bcb7e502
AT
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)) {
d0d56395 227 return NULL;
bcb7e502
AT
228 }
229
230 memset(user, 0, sizeof(user));
231 memset(pass, 0, sizeof(pass));
232
233 if (sscanf(line,"%99s %29s", user, pass) != 2) {
d0d56395 234 return NULL;
bcb7e502
AT
235 }
236
c8e78d87 237 users = strdup(users);
d0d56395 238 if (!users) return NULL;
c8e78d87
AT
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) {
d0d56395 246 return NULL;
c8e78d87
AT
247 }
248
bcb7e502
AT
249 memset(secret, 0, sizeof(secret));
250 if (!get_secret(module, user, secret, sizeof(secret)-1)) {
251 memset(secret, 0, sizeof(secret));
d0d56395 252 return NULL;
bcb7e502
AT
253 }
254
255 generate_hash(secret, b64_challenge, pass2);
256 memset(secret, 0, sizeof(secret));
257
d0d56395
AT
258 if (strcmp(pass, pass2) == 0)
259 return user;
260
261 return NULL;
bcb7e502
AT
262}
263
264
265void auth_client(int fd, char *user, char *challenge)
266{
267 char *pass;
268 char pass2[30];
65575e96 269 extern char *password_file;
bcb7e502
AT
270
271 if (!user || !*user) return;
272
65575e96 273 if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
bcb7e502
AT
274 pass = getpass("Password: ");
275 }
276
277 if (!pass || !*pass) {
278 pass = "";
279 }
280
281 generate_hash(pass, challenge, pass2);
bcb7e502
AT
282 io_printf(fd, "%s %s\n", user, pass2);
283}
284
65575e96 285