authenticate.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /*
  2. * Support rsync daemon authentication.
  3. *
  4. * Copyright (C) 1998-2000 Andrew Tridgell
  5. * Copyright (C) 2002-2009 Wayne Davison
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, visit the http://fsf.org website.
  19. */
  20. #include "rsync.h"
  21. extern char *password_file;
  22. /***************************************************************************
  23. encode a buffer using base64 - simple and slow algorithm. null terminates
  24. the result.
  25. ***************************************************************************/
  26. void base64_encode(const char *buf, int len, char *out, int pad)
  27. {
  28. char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  29. int bit_offset, byte_offset, idx, i;
  30. const uchar *d = (const uchar *)buf;
  31. int bytes = (len*8 + 5)/6;
  32. for (i = 0; i < bytes; i++) {
  33. byte_offset = (i*6)/8;
  34. bit_offset = (i*6)%8;
  35. if (bit_offset < 3) {
  36. idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
  37. } else {
  38. idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
  39. if (byte_offset+1 < len) {
  40. idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
  41. }
  42. }
  43. out[i] = b64[idx];
  44. }
  45. while (pad && (i % 4))
  46. out[i++] = '=';
  47. out[i] = '\0';
  48. }
  49. /* Generate a challenge buffer and return it base64-encoded. */
  50. static void gen_challenge(const char *addr, char *challenge)
  51. {
  52. char input[32];
  53. char digest[MAX_DIGEST_LEN];
  54. struct timeval tv;
  55. int len;
  56. memset(input, 0, sizeof input);
  57. strlcpy(input, addr, 17);
  58. sys_gettimeofday(&tv);
  59. SIVAL(input, 16, tv.tv_sec);
  60. SIVAL(input, 20, tv.tv_usec);
  61. SIVAL(input, 24, getpid());
  62. sum_init(0);
  63. sum_update(input, sizeof input);
  64. len = sum_end(digest);
  65. base64_encode(digest, len, challenge, 0);
  66. }
  67. /* Return the secret for a user from the secret file, null terminated.
  68. * Maximum length is len (not counting the null). */
  69. static int get_secret(int module, const char *user, char *secret, int len)
  70. {
  71. const char *fname = lp_secrets_file(module);
  72. STRUCT_STAT st;
  73. int fd, ok = 1;
  74. const char *p;
  75. char ch, *s;
  76. if (!fname || !*fname)
  77. return 0;
  78. if ((fd = open(fname, O_RDONLY)) < 0)
  79. return 0;
  80. if (do_stat(fname, &st) == -1) {
  81. rsyserr(FLOG, errno, "stat(%s)", fname);
  82. ok = 0;
  83. } else if (lp_strict_modes(module)) {
  84. if ((st.st_mode & 06) != 0) {
  85. rprintf(FLOG, "secrets file must not be other-accessible (see strict modes option)\n");
  86. ok = 0;
  87. } else if (MY_UID() == 0 && st.st_uid != 0) {
  88. rprintf(FLOG, "secrets file must be owned by root when running as root (see strict modes)\n");
  89. ok = 0;
  90. }
  91. }
  92. if (!ok) {
  93. rprintf(FLOG, "continuing without secrets file\n");
  94. close(fd);
  95. return 0;
  96. }
  97. if (*user == '#') {
  98. /* Reject attempt to match a comment. */
  99. close(fd);
  100. return 0;
  101. }
  102. /* Try to find a line that starts with the user name and a ':'. */
  103. p = user;
  104. while (1) {
  105. if (read(fd, &ch, 1) != 1) {
  106. close(fd);
  107. return 0;
  108. }
  109. if (ch == '\n')
  110. p = user;
  111. else if (p) {
  112. if (*p == ch)
  113. p++;
  114. else if (!*p && ch == ':')
  115. break;
  116. else
  117. p = NULL;
  118. }
  119. }
  120. /* Slurp the secret into the "secret" buffer. */
  121. s = secret;
  122. while (len > 0) {
  123. if (read(fd, s, 1) != 1 || *s == '\n')
  124. break;
  125. if (*s == '\r')
  126. continue;
  127. s++;
  128. len--;
  129. }
  130. *s = '\0';
  131. close(fd);
  132. return 1;
  133. }
  134. static const char *getpassf(const char *filename)
  135. {
  136. STRUCT_STAT st;
  137. char buffer[512], *p;
  138. int fd, n;
  139. if (!filename)
  140. return NULL;
  141. if ((fd = open(filename,O_RDONLY)) < 0) {
  142. rsyserr(FERROR, errno, "could not open password file %s", filename);
  143. exit_cleanup(RERR_SYNTAX);
  144. }
  145. if (do_stat(filename, &st) == -1) {
  146. rsyserr(FERROR, errno, "stat(%s)", filename);
  147. exit_cleanup(RERR_SYNTAX);
  148. }
  149. if ((st.st_mode & 06) != 0) {
  150. rprintf(FERROR, "ERROR: password file must not be other-accessible\n");
  151. exit_cleanup(RERR_SYNTAX);
  152. }
  153. if (MY_UID() == 0 && st.st_uid != 0) {
  154. rprintf(FERROR, "ERROR: password file must be owned by root when running as root\n");
  155. exit_cleanup(RERR_SYNTAX);
  156. }
  157. n = read(fd, buffer, sizeof buffer - 1);
  158. close(fd);
  159. if (n > 0) {
  160. buffer[n] = '\0';
  161. if ((p = strtok(buffer, "\n\r")) != NULL)
  162. return strdup(p);
  163. }
  164. rprintf(FERROR, "ERROR: failed to read a password from %s\n", filename);
  165. exit_cleanup(RERR_SYNTAX);
  166. }
  167. /* Generate an MD4 hash created from the combination of the password
  168. * and the challenge string and return it base64-encoded. */
  169. static void generate_hash(const char *in, const char *challenge, char *out)
  170. {
  171. char buf[MAX_DIGEST_LEN];
  172. int len;
  173. sum_init(0);
  174. sum_update(in, strlen(in));
  175. sum_update(challenge, strlen(challenge));
  176. len = sum_end(buf);
  177. base64_encode(buf, len, out, 0);
  178. }
  179. /* Possibly negotiate authentication with the client. Use "leader" to
  180. * start off the auth if necessary.
  181. *
  182. * Return NULL if authentication failed. Return "" if anonymous access.
  183. * Otherwise return username.
  184. */
  185. char *auth_server(int f_in, int f_out, int module, const char *host,
  186. const char *addr, const char *leader)
  187. {
  188. char *users = lp_auth_users(module);
  189. char challenge[MAX_DIGEST_LEN*2];
  190. char line[BIGPATHBUFLEN];
  191. char secret[512];
  192. char pass2[MAX_DIGEST_LEN*2];
  193. char *tok, *pass;
  194. /* if no auth list then allow anyone in! */
  195. if (!users || !*users)
  196. return "";
  197. gen_challenge(addr, challenge);
  198. io_printf(f_out, "%s%s\n", leader, challenge);
  199. if (!read_line_old(f_in, line, sizeof line)
  200. || (pass = strchr(line, ' ')) == NULL) {
  201. rprintf(FLOG, "auth failed on module %s from %s (%s): "
  202. "invalid challenge response\n",
  203. lp_name(module), host, addr);
  204. return NULL;
  205. }
  206. *pass++ = '\0';
  207. if (!(users = strdup(users)))
  208. out_of_memory("auth_server");
  209. for (tok = strtok(users, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
  210. if (wildmatch(tok, line))
  211. break;
  212. }
  213. free(users);
  214. if (!tok) {
  215. rprintf(FLOG, "auth failed on module %s from %s (%s): "
  216. "unauthorized user\n",
  217. lp_name(module), host, addr);
  218. return NULL;
  219. }
  220. memset(secret, 0, sizeof secret);
  221. if (!get_secret(module, line, secret, sizeof secret - 1)) {
  222. memset(secret, 0, sizeof secret);
  223. rprintf(FLOG, "auth failed on module %s from %s (%s): "
  224. "missing secret for user \"%s\"\n",
  225. lp_name(module), host, addr, line);
  226. return NULL;
  227. }
  228. generate_hash(secret, challenge, pass2);
  229. memset(secret, 0, sizeof secret);
  230. if (strcmp(pass, pass2) != 0) {
  231. rprintf(FLOG, "auth failed on module %s from %s (%s): "
  232. "password mismatch\n",
  233. lp_name(module), host, addr);
  234. return NULL;
  235. }
  236. return strdup(line);
  237. }
  238. void auth_client(int fd, const char *user, const char *challenge)
  239. {
  240. const char *pass;
  241. char pass2[MAX_DIGEST_LEN*2];
  242. if (!user || !*user)
  243. user = "nobody";
  244. if (!(pass = getpassf(password_file))
  245. && !(pass = getenv("RSYNC_PASSWORD"))) {
  246. /* XXX: cyeoh says that getpass is deprecated, because
  247. * it may return a truncated password on some systems,
  248. * and it is not in the LSB.
  249. *
  250. * Andrew Klein says that getpassphrase() is present
  251. * on Solaris and reads up to 256 characters.
  252. *
  253. * OpenBSD has a readpassphrase() that might be more suitable.
  254. */
  255. pass = getpass("Password: ");
  256. }
  257. if (!pass)
  258. pass = "";
  259. generate_hash(pass, challenge, pass2);
  260. io_printf(fd, "%s %s\n", user, pass2);
  261. }