authenticate.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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, ok = 1;
  139. const char *envpw = getenv("RSYNC_PASSWORD");
  140. if (!filename)
  141. return NULL;
  142. if ((fd = open(filename,O_RDONLY)) < 0) {
  143. rsyserr(FWARNING, errno, "could not open password file \"%s\"",
  144. filename);
  145. if (envpw)
  146. rprintf(FINFO, "falling back to RSYNC_PASSWORD environment variable.\n");
  147. return NULL;
  148. }
  149. if (do_stat(filename, &st) == -1) {
  150. rsyserr(FWARNING, errno, "stat(%s)", filename);
  151. ok = 0;
  152. } else if ((st.st_mode & 06) != 0) {
  153. rprintf(FWARNING, "password file must not be other-accessible\n");
  154. ok = 0;
  155. } else if (MY_UID() == 0 && st.st_uid != 0) {
  156. rprintf(FWARNING, "password file must be owned by root when running as root\n");
  157. ok = 0;
  158. }
  159. if (!ok) {
  160. close(fd);
  161. rprintf(FWARNING, "continuing without password file\n");
  162. if (envpw)
  163. rprintf(FINFO, "falling back to RSYNC_PASSWORD environment variable.\n");
  164. return NULL;
  165. }
  166. n = read(fd, buffer, sizeof buffer - 1);
  167. close(fd);
  168. if (n > 0) {
  169. buffer[n] = '\0';
  170. if ((p = strtok(buffer, "\n\r")) != NULL)
  171. return strdup(p);
  172. }
  173. return NULL;
  174. }
  175. /* Generate an MD4 hash created from the combination of the password
  176. * and the challenge string and return it base64-encoded. */
  177. static void generate_hash(const char *in, const char *challenge, char *out)
  178. {
  179. char buf[MAX_DIGEST_LEN];
  180. int len;
  181. sum_init(0);
  182. sum_update(in, strlen(in));
  183. sum_update(challenge, strlen(challenge));
  184. len = sum_end(buf);
  185. base64_encode(buf, len, out, 0);
  186. }
  187. /* Possibly negotiate authentication with the client. Use "leader" to
  188. * start off the auth if necessary.
  189. *
  190. * Return NULL if authentication failed. Return "" if anonymous access.
  191. * Otherwise return username.
  192. */
  193. char *auth_server(int f_in, int f_out, int module, const char *host,
  194. const char *addr, const char *leader)
  195. {
  196. char *users = lp_auth_users(module);
  197. char challenge[MAX_DIGEST_LEN*2];
  198. char line[BIGPATHBUFLEN];
  199. char secret[512];
  200. char pass2[MAX_DIGEST_LEN*2];
  201. char *tok, *pass;
  202. /* if no auth list then allow anyone in! */
  203. if (!users || !*users)
  204. return "";
  205. gen_challenge(addr, challenge);
  206. io_printf(f_out, "%s%s\n", leader, challenge);
  207. if (!read_line_old(f_in, line, sizeof line)
  208. || (pass = strchr(line, ' ')) == NULL) {
  209. rprintf(FLOG, "auth failed on module %s from %s (%s): "
  210. "invalid challenge response\n",
  211. lp_name(module), host, addr);
  212. return NULL;
  213. }
  214. *pass++ = '\0';
  215. if (!(users = strdup(users)))
  216. out_of_memory("auth_server");
  217. for (tok = strtok(users, " ,\t"); tok; tok = strtok(NULL, " ,\t")) {
  218. if (wildmatch(tok, line))
  219. break;
  220. }
  221. free(users);
  222. if (!tok) {
  223. rprintf(FLOG, "auth failed on module %s from %s (%s): "
  224. "unauthorized user\n",
  225. lp_name(module), host, addr);
  226. return NULL;
  227. }
  228. memset(secret, 0, sizeof secret);
  229. if (!get_secret(module, line, secret, sizeof secret - 1)) {
  230. memset(secret, 0, sizeof secret);
  231. rprintf(FLOG, "auth failed on module %s from %s (%s): "
  232. "missing secret for user \"%s\"\n",
  233. lp_name(module), host, addr, line);
  234. return NULL;
  235. }
  236. generate_hash(secret, challenge, pass2);
  237. memset(secret, 0, sizeof secret);
  238. if (strcmp(pass, pass2) != 0) {
  239. rprintf(FLOG, "auth failed on module %s from %s (%s): "
  240. "password mismatch\n",
  241. lp_name(module), host, addr);
  242. return NULL;
  243. }
  244. return strdup(line);
  245. }
  246. void auth_client(int fd, const char *user, const char *challenge)
  247. {
  248. const char *pass;
  249. char pass2[MAX_DIGEST_LEN*2];
  250. if (!user || !*user)
  251. user = "nobody";
  252. if (!(pass = getpassf(password_file))
  253. && !(pass = getenv("RSYNC_PASSWORD"))) {
  254. /* XXX: cyeoh says that getpass is deprecated, because
  255. * it may return a truncated password on some systems,
  256. * and it is not in the LSB.
  257. *
  258. * Andrew Klein says that getpassphrase() is present
  259. * on Solaris and reads up to 256 characters.
  260. *
  261. * OpenBSD has a readpassphrase() that might be more suitable.
  262. */
  263. pass = getpass("Password: ");
  264. }
  265. if (!pass)
  266. pass = "";
  267. generate_hash(pass, challenge, pass2);
  268. io_printf(fd, "%s %s\n", user, pass2);
  269. }