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