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 sys_gettimeofday(&tv);
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 (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");
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");
100 rprintf(FERROR,"continuing without secrets file\n");
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));
114 if (line[i] == '\r') continue;
115 if (line[i] == '\n') break;
119 if (line[0] == '#') continue;
120 p = strchr(line,':');
123 if (strcmp(user, line)) continue;
129 if (!found) return 0;
131 strlcpy(secret, pass, len);
135 static char *getpassf(char *filename)
142 char *envpw=getenv("RSYNC_PASSWORD");
144 if (!filename) return NULL;
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");
152 if (do_stat(filename, &st) == -1) {
153 rprintf(FERROR,"stat(%s) : %s\n", filename, strerror(errno));
155 } else if ((st.st_mode & 06) != 0) {
156 rprintf(FERROR,"password file must not be other-accessible\n");
158 } else if (am_root && (st.st_uid != 0)) {
159 rprintf(FERROR,"password file must be owned by root when running as root\n");
163 rprintf(FERROR,"continuing without password file\n");
164 if (envpw) rprintf(FERROR,"using RSYNC_PASSWORD environment variable.\n");
169 if (envpw) rprintf(FERROR,"RSYNC_PASSWORD environment variable ignored\n");
171 buffer[sizeof(buffer)-1]='\0';
172 if (read(fd,buffer,sizeof(buffer)-1) > 0)
174 char *p = strtok(buffer,"\n\r");
176 if (p) p = strdup(p);
183 /* generate a 16 byte hash from a password and challenge */
184 static void generate_hash(char *in, char *challenge, char *out)
189 sum_update(in, strlen(in));
190 sum_update(challenge, strlen(challenge));
193 base64_encode(buf, 16, out);
196 /* possible negotiate authentication with the client. Use "leader" to
197 start off the auth if necessary
199 return NULL if authentication failed
201 return "" if anonymous access
203 otherwise return username
205 char *auth_server(int fd, int module, char *addr, char *leader)
207 char *users = lp_auth_users(module);
209 char b64_challenge[30];
210 char line[MAXPATHLEN];
211 static char user[100];
217 /* if no auth list then allow anyone in! */
218 if (!users || !*users) return "";
220 gen_challenge(addr, challenge);
222 base64_encode(challenge, 16, b64_challenge);
224 io_printf(fd,"%s%s\n", leader, b64_challenge);
226 if (!read_line(fd, line, sizeof(line)-1)) {
230 memset(user, 0, sizeof(user));
231 memset(pass, 0, sizeof(pass));
233 if (sscanf(line,"%99s %29s", user, pass) != 2) {
237 users = strdup(users);
238 if (!users) return NULL;
240 for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
241 if (strcmp(tok, user) == 0) break;
249 memset(secret, 0, sizeof(secret));
250 if (!get_secret(module, user, secret, sizeof(secret)-1)) {
251 memset(secret, 0, sizeof(secret));
255 generate_hash(secret, b64_challenge, pass2);
256 memset(secret, 0, sizeof(secret));
258 if (strcmp(pass, pass2) == 0)
265 void auth_client(int fd, char *user, char *challenge)
269 extern char *password_file;
271 if (!user || !*user) return;
273 if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
274 pass = getpass("Password: ");
277 if (!pass || !*pass) {
281 generate_hash(pass, challenge, pass2);
282 io_printf(fd, "%s %s\n", user, pass2);