1 /* -*- c-file-style: "linux"; -*-
3 Copyright (C) 1998-2000 by Andrew Tridgell
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* support rsync authentication */
23 #if defined(__CYGWIN__) || defined(WIN32)
24 /* This, along with the "rt" for stdio, means that people can have
25 * CRLF in configuration files without troubles. */
26 # define CONFIG_OPEN_MODE (O_RDONLY|O_TEXT)
28 # define CONFIG_OPEN_MODE O_RDONLY
31 /***************************************************************************
32 encode a buffer using base64 - simple and slow algorithm. null terminates
34 ***************************************************************************/
35 static void base64_encode(char *buf, int len, char *out)
37 char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
38 int bit_offset, byte_offset, idx, i;
39 unsigned char *d = (unsigned char *)buf;
40 int bytes = (len*8 + 5)/6;
42 memset(out, 0, bytes+1);
44 for (i=0;i<bytes;i++) {
45 byte_offset = (i*6)/8;
48 idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
50 idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
51 if (byte_offset+1 < len) {
52 idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
59 /* create a 16 byte challenge buffer */
60 static void gen_challenge(char *addr, char *challenge)
65 memset(input, 0, sizeof(input));
67 strlcpy((char *)input, addr, 17);
68 sys_gettimeofday(&tv);
69 SIVAL(input, 16, tv.tv_sec);
70 SIVAL(input, 20, tv.tv_usec);
71 SIVAL(input, 24, getpid());
74 sum_update(input, sizeof(input));
79 /* return the secret for a user from the sercret file. maximum length
80 is len. null terminate it */
81 static int get_secret(int module, char *user, char *secret, int len)
83 char *fname = lp_secrets_file(module);
85 char line[MAXPATHLEN];
91 if (!fname || !*fname) return 0;
93 fd = open(fname, CONFIG_OPEN_MODE);
94 if (fd == -1) return 0;
96 if (do_stat(fname, &st) == -1) {
97 rsyserr(FERROR, errno, "stat(%s)", fname);
99 } else if (lp_strict_modes(module)) {
100 /* Under CYGWIN, we don't want to force attribute
101 check. It can be made to work if env var CYGWIN
102 contains "ntea" (nt extended attribute mode), but
103 frankly, it is a MAJOR pain in the ass to get
104 working especially when trying to run rsync as a NT
105 service. Besides, CYGWIN permissions won't give us
106 any added security -- the security should be
107 handled with NTFS ACL's. */
109 if ((st.st_mode & 06) != 0) {
110 rprintf(FERROR,"secrets file must not be other-accessible (see strict modes option)\n");
112 } else if (am_root && (st.st_uid != 0)) {
113 rprintf(FERROR,"secrets file must be owned by root when running as root (see strict modes)\n");
119 rprintf(FERROR,"continuing without secrets file\n");
126 memset(line, 0, sizeof line);
127 while ((size_t) i < (sizeof(line)-1)) {
128 if (read(fd, &line[i], 1) != 1) {
129 memset(line, 0, sizeof(line));
133 if (line[i] == '\r') continue;
134 if (line[i] == '\n') break;
138 if (line[0] == '#') continue;
139 p = strchr(line,':');
142 if (strcmp(user, line)) continue;
148 if (!found) return 0;
150 strlcpy(secret, pass, len);
154 static char *getpassf(char *filename)
161 char *envpw=getenv("RSYNC_PASSWORD");
163 if (!filename) return NULL;
165 if ( (fd=open(filename, CONFIG_OPEN_MODE)) == -1) {
166 rsyserr(FERROR, errno, "could not open password file \"%s\"",filename);
167 if (envpw) rprintf(FERROR,"falling back to RSYNC_PASSWORD environment variable.\n");
171 if (do_stat(filename, &st) == -1) {
172 rsyserr(FERROR, errno, "stat(%s)", filename);
176 /* Under CYGWIN, we don't want to force attribute check. It
177 can be made to work if env var CYGWIN contains "ntea" (nt
178 extended attribute mode), but frankly, it is a MAJOR pain
179 in the ass to get working especially when trying to run
180 rsync as a NT service. Besides, CYGWIN permissions won't
181 give us any added security -- the security should be
182 handled with NTFS ACL's. */
183 } else if ((st.st_mode & 06) != 0) {
184 rprintf(FERROR,"password file must not be other-accessible\n");
186 } else if (am_root && (st.st_uid != 0)) {
187 rprintf(FERROR,"password file must be owned by root when running as root\n");
192 rprintf(FERROR,"continuing without password file\n");
193 if (envpw) rprintf(FERROR,"using RSYNC_PASSWORD environment variable.\n");
198 if (envpw) rprintf(FERROR,"RSYNC_PASSWORD environment variable ignored\n");
200 buffer[sizeof(buffer)-1]='\0';
201 if (read(fd,buffer,sizeof(buffer)-1) > 0)
203 char *p = strtok(buffer,"\n\r");
205 if (p) p = strdup(p);
212 /* generate a 16 byte hash from a password and challenge */
213 static void generate_hash(char *in, char *challenge, char *out)
218 sum_update(in, strlen(in));
219 sum_update(challenge, strlen(challenge));
222 base64_encode(buf, 16, out);
225 /* possible negotiate authentication with the client. Use "leader" to
226 start off the auth if necessary
228 return NULL if authentication failed
230 return "" if anonymous access
232 otherwise return username
234 char *auth_server(int fd, int module, char *addr, char *leader)
236 char *users = lp_auth_users(module);
238 char b64_challenge[30];
239 char line[MAXPATHLEN];
240 static char user[100];
246 /* if no auth list then allow anyone in! */
247 if (!users || !*users) return "";
249 gen_challenge(addr, challenge);
251 base64_encode(challenge, 16, b64_challenge);
253 io_printf(fd,"%s%s\n", leader, b64_challenge);
255 if (!read_line(fd, line, sizeof(line)-1)) {
259 memset(user, 0, sizeof(user));
260 memset(pass, 0, sizeof(pass));
262 if (sscanf(line,"%99s %29s", user, pass) != 2) {
266 users = strdup(users);
267 if (!users) return NULL;
269 for (tok=strtok(users," ,\t"); tok; tok = strtok(NULL," ,\t")) {
270 if (fnmatch(tok, user, 0) == 0) break;
278 memset(secret, 0, sizeof(secret));
279 if (!get_secret(module, user, secret, sizeof(secret)-1)) {
280 memset(secret, 0, sizeof(secret));
284 generate_hash(secret, b64_challenge, pass2);
285 memset(secret, 0, sizeof(secret));
287 if (strcmp(pass, pass2) == 0)
294 void auth_client(int fd, char *user, char *challenge)
298 extern char *password_file;
300 if (!user || !*user) return;
302 if (!(pass=getpassf(password_file)) && !(pass=getenv("RSYNC_PASSWORD"))) {
303 /* XXX: cyeoh says that getpass is deprecated, because
304 it may return a truncated password on some systems,
305 and it is not in the LSB. */
306 pass = getpass("Password: ");
309 if (!pass || !*pass) {
313 generate_hash(pass, challenge, pass2);
314 io_printf(fd, "%s %s\n", user, pass2);