2 Copyright (C) Andrew Tridgell 1998
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.
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.
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.
19 /* support rsync authentication */
22 /***************************************************************************
23 encode a buffer using base64 - simple and slow algorithm. null terminates
25 ***************************************************************************/
26 static void base64_encode(char *buf, int len, char *out)
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;
33 memset(out, 0, bytes+1);
35 for (i=0;i<bytes;i++) {
36 byte_offset = (i*6)/8;
39 idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
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)));
50 /* create a 16 byte challenge buffer */
51 static void gen_challenge(char *addr, char *challenge)
56 memset(input, 0, sizeof(input));
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());
65 sum_update(input, sizeof(input));
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)
74 char *fname = lp_secrets_file(module);
76 char line[MAXPATHLEN];
82 if (!fname || !*fname) return 0;
84 fd = open(fname,O_RDONLY);
85 if (fd == -1) return 0;
87 if (do_stat(fname, &st) == -1) {
88 rprintf(FERROR,"stat(%s) : %s\n", fname, strerror(errno));
90 } else if ((st.st_mode & 06) != 0) {
91 rprintf(FERROR,"secrets file must not be other-accessible\n");
93 } else if (am_root && (st.st_uid != 0)) {
94 rprintf(FERROR,"secrets file must be owned by root when running as root\n");
98 rprintf(FERROR,"continuing without secrets file\n");
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));
112 if (line[i] == '\r') continue;
113 if (line[i] == '\n') break;
117 if (line[0] == '#') continue;
118 p = strchr(line,':');
121 if (strcmp(user, line)) continue;
127 if (!found) return 0;
129 strlcpy(secret, pass, len);
133 static char *getpassf(char *filename)
141 char *envpw=getenv("RSYNC_PASSWORD");
143 if (!filename) return NULL;
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");
151 if (do_stat(filename, &st) == -1) {
152 rprintf(FERROR,"stat(%s) : %s\n", filename, strerror(errno));
154 } else if ((st.st_mode & 06) != 0) {
155 rprintf(FERROR,"password file must not be other-accessible\n");
157 } else if (am_root && (st.st_uid != 0)) {
158 rprintf(FERROR,"password file must be owned by root when running as root\n");
162 rprintf(FERROR,"continuing without password file\n");
163 if (envpw) rprintf(FERROR,"using RSYNC_PASSWORD environment variable.\n");
168 if (envpw) rprintf(FERROR,"RSYNC_PASSWORD environment variable ignored\n");
170 buffer[sizeof(buffer)-1]='\0';
171 if ( (len=read(fd,buffer,sizeof(buffer)-1)) > 0)
173 char *p = strtok(buffer,"\n\r");
175 if (p) p = strdup(p);
182 /* generate a 16 byte hash from a password and challenge */
183 static void generate_hash(char *in, char *challenge, char *out)
188 sum_update(in, strlen(in));
189 sum_update(challenge, strlen(challenge));
192 base64_encode(buf, 16, out);
195 /* possible negotiate authentication with the client. Use "leader" to
196 start off the auth if necessary
198 return NULL if authentication failed
200 return "" if anonymous access
202 otherwise return username
204 char *auth_server(int fd, int module, char *addr, char *leader)
206 char *users = lp_auth_users(module);
208 char b64_challenge[30];
209 char line[MAXPATHLEN];
210 static char user[100];
216 /* if no auth list then allow anyone in! */
217 if (!users || !*users) return "";
219 gen_challenge(addr, challenge);
221 base64_encode(challenge, 16, b64_challenge);
223 io_printf(fd,"%s%s\n", leader, b64_challenge);
225 if (!read_line(fd, line, sizeof(line)-1)) {
229 memset(user, 0, sizeof(user));
230 memset(pass, 0, sizeof(pass));
232 if (sscanf(line,"%99s %29s", user, pass) != 2) {
236 users = strdup(users);
237 if (!users) return NULL;
239 for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
240 if (strcmp(tok, user) == 0) break;
248 memset(secret, 0, sizeof(secret));
249 if (!get_secret(module, user, secret, sizeof(secret)-1)) {
250 memset(secret, 0, sizeof(secret));
254 generate_hash(secret, b64_challenge, pass2);
255 memset(secret, 0, sizeof(secret));
257 if (strcmp(pass, pass2) == 0)
264 void auth_client(int fd, char *user, char *challenge)
268 extern char *password_file;
270 if (!user || !*user) return;
272 if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
273 pass = getpass("Password: ");
276 if (!pass || !*pass) {
280 generate_hash(pass, challenge, pass2);
281 io_printf(fd, "%s %s\n", user, pass2);