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 extern char *password_file
;
26 /***************************************************************************
27 encode a buffer using base64 - simple and slow algorithm. null terminates
29 ***************************************************************************/
30 void base64_encode(char *buf
, int len
, char *out
)
32 char *b64
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
33 int bit_offset
, byte_offset
, idx
, i
;
34 unsigned char *d
= (unsigned char *)buf
;
35 int bytes
= (len
*8 + 5)/6;
37 memset(out
, 0, bytes
+1);
39 for (i
= 0; i
< bytes
; i
++) {
40 byte_offset
= (i
*6)/8;
43 idx
= (d
[byte_offset
] >> (2-bit_offset
)) & 0x3F;
45 idx
= (d
[byte_offset
] << (bit_offset
-2)) & 0x3F;
46 if (byte_offset
+1 < len
) {
47 idx
|= (d
[byte_offset
+1] >> (8-(bit_offset
-2)));
54 /* create a 16 byte challenge buffer */
55 static void gen_challenge(char *addr
, char *challenge
)
60 memset(input
, 0, sizeof input
);
62 strlcpy((char *)input
, addr
, 17);
63 sys_gettimeofday(&tv
);
64 SIVAL(input
, 16, tv
.tv_sec
);
65 SIVAL(input
, 20, tv
.tv_usec
);
66 SIVAL(input
, 24, getpid());
69 sum_update(input
, sizeof input
);
74 /* Return the secret for a user from the secret file, null terminated.
75 * Maximum length is len (not counting the null). */
76 static int get_secret(int module
, char *user
, char *secret
, int len
)
78 char *fname
= lp_secrets_file(module
);
83 if (!fname
|| !*fname
)
86 if ((fd
= open(fname
, O_RDONLY
)) < 0)
89 if (do_stat(fname
, &st
) == -1) {
90 rsyserr(FLOG
, errno
, "stat(%s)", safe_fname(fname
));
92 } else if (lp_strict_modes(module
)) {
93 if ((st
.st_mode
& 06) != 0) {
94 rprintf(FLOG
, "secrets file must not be other-accessible (see strict modes option)\n");
96 } else if (am_root
&& (st
.st_uid
!= 0)) {
97 rprintf(FLOG
, "secrets file must be owned by root when running as root (see strict modes)\n");
102 rprintf(FLOG
, "continuing without secrets file\n");
108 /* Reject attempt to match a comment. */
113 /* Try to find a line that starts with the user name and a ':'. */
116 if (read(fd
, &ch
, 1) != 1) {
125 else if (!*p
&& ch
== ':')
132 /* Slurp the secret into the "secret" buffer. */
135 if (read(fd
, p
, 1) != 1 || *p
== '\n')
148 static char *getpassf(char *filename
)
151 char buffer
[512], *p
;
153 char *envpw
= getenv("RSYNC_PASSWORD");
158 if ((fd
= open(filename
,O_RDONLY
)) < 0) {
159 rsyserr(FERROR
, errno
, "could not open password file \"%s\"",
160 safe_fname(filename
));
162 rprintf(FERROR
, "falling back to RSYNC_PASSWORD environment variable.\n");
166 if (do_stat(filename
, &st
) == -1) {
167 rsyserr(FERROR
, errno
, "stat(%s)", safe_fname(filename
));
169 } else if ((st
.st_mode
& 06) != 0) {
170 rprintf(FERROR
,"password file must not be other-accessible\n");
172 } else if (am_root
&& st
.st_uid
!= 0) {
173 rprintf(FERROR
,"password file must be owned by root when running as root\n");
177 rprintf(FERROR
,"continuing without password file\n");
179 rprintf(FERROR
, "using RSYNC_PASSWORD environment variable.\n");
185 rprintf(FERROR
, "RSYNC_PASSWORD environment variable ignored\n");
187 n
= read(fd
, buffer
, sizeof buffer
- 1);
191 if ((p
= strtok(buffer
, "\n\r")) != NULL
)
198 /* generate a 16 byte hash from a password and challenge */
199 static void generate_hash(char *in
, char *challenge
, char *out
)
204 sum_update(in
, strlen(in
));
205 sum_update(challenge
, strlen(challenge
));
208 base64_encode(buf
, 16, out
);
211 /* Possibly negotiate authentication with the client. Use "leader" to
212 * start off the auth if necessary.
214 * Return NULL if authentication failed. Return "" if anonymous access.
215 * Otherwise return username.
217 char *auth_server(int f_in
, int f_out
, int module
, char *addr
, char *leader
)
219 char *users
= lp_auth_users(module
);
221 char b64_challenge
[30];
222 char line
[MAXPATHLEN
];
223 static char user
[100];
229 /* if no auth list then allow anyone in! */
230 if (!users
|| !*users
)
233 gen_challenge(addr
, challenge
);
235 base64_encode(challenge
, 16, b64_challenge
);
237 io_printf(f_out
, "%s%s\n", leader
, b64_challenge
);
239 if (!read_line(f_in
, line
, sizeof line
- 1))
242 memset(user
, 0, sizeof user
);
243 memset(pass
, 0, sizeof pass
);
245 if (sscanf(line
,"%99s %29s", user
, pass
) != 2)
248 users
= strdup(users
);
252 for (tok
=strtok(users
," ,\t"); tok
; tok
= strtok(NULL
," ,\t")) {
253 if (wildmatch(tok
, user
))
261 memset(secret
, 0, sizeof secret
);
262 if (!get_secret(module
, user
, secret
, sizeof secret
- 1)) {
263 memset(secret
, 0, sizeof secret
);
267 generate_hash(secret
, b64_challenge
, pass2
);
268 memset(secret
, 0, sizeof secret
);
270 if (strcmp(pass
, pass2
) == 0)
277 void auth_client(int fd
, char *user
, char *challenge
)
285 if (!(pass
= getpassf(password_file
))
286 && !(pass
= getenv("RSYNC_PASSWORD"))) {
287 /* XXX: cyeoh says that getpass is deprecated, because
288 * it may return a truncated password on some systems,
289 * and it is not in the LSB.
291 * Andrew Klein says that getpassphrase() is present
292 * on Solaris and reads up to 256 characters.
294 * OpenBSD has a readpassphrase() that might be more suitable.
296 pass
= getpass("Password: ");
302 generate_hash(pass
, challenge
, pass2
);
303 io_printf(fd
, "%s %s\n", user
, pass2
);