auth-rhosts.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /* $OpenBSD: auth-rhosts.c,v 1.52 2020/04/17 03:30:05 djm Exp $ */
  2. /*
  3. * Author: Tatu Ylonen <ylo@cs.hut.fi>
  4. * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  5. * All rights reserved
  6. * Rhosts authentication. This file contains code to check whether to admit
  7. * the login based on rhosts authentication. This file also processes
  8. * /etc/hosts.equiv.
  9. *
  10. * As far as I am concerned, the code I have written for this software
  11. * can be used freely for any purpose. Any derived versions of this
  12. * software must be clearly marked as such, and if the derived work is
  13. * incompatible with the protocol description in the RFC file, it must be
  14. * called by a name other than "ssh" or "Secure Shell".
  15. */
  16. #include "includes.h"
  17. #include <sys/types.h>
  18. #include <sys/stat.h>
  19. #ifdef HAVE_NETGROUP_H
  20. # include <netgroup.h>
  21. #endif
  22. #include <pwd.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <stdarg.h>
  26. #include <fcntl.h>
  27. #include <unistd.h>
  28. #include "packet.h"
  29. #include "uidswap.h"
  30. #include "pathnames.h"
  31. #include "log.h"
  32. #include "misc.h"
  33. #include "sshbuf.h"
  34. #include "sshkey.h"
  35. #include "servconf.h"
  36. #include "canohost.h"
  37. #include "hostfile.h"
  38. #include "auth.h"
  39. /* import */
  40. extern ServerOptions options;
  41. extern int use_privsep;
  42. /*
  43. * This function processes an rhosts-style file (.rhosts, .shosts, or
  44. * /etc/hosts.equiv). This returns true if authentication can be granted
  45. * based on the file, and returns zero otherwise.
  46. */
  47. static int
  48. check_rhosts_file(const char *filename, const char *hostname,
  49. const char *ipaddr, const char *client_user,
  50. const char *server_user)
  51. {
  52. FILE *f;
  53. #define RBUFLN 1024
  54. char buf[RBUFLN];/* Must not be larger than host, user, dummy below. */
  55. int fd;
  56. struct stat st;
  57. /* Open the .rhosts file, deny if unreadable */
  58. if ((fd = open(filename, O_RDONLY|O_NONBLOCK)) == -1)
  59. return 0;
  60. if (fstat(fd, &st) == -1) {
  61. close(fd);
  62. return 0;
  63. }
  64. if (!S_ISREG(st.st_mode)) {
  65. logit("User %s hosts file %s is not a regular file",
  66. server_user, filename);
  67. close(fd);
  68. return 0;
  69. }
  70. unset_nonblock(fd);
  71. if ((f = fdopen(fd, "r")) == NULL) {
  72. close(fd);
  73. return 0;
  74. }
  75. while (fgets(buf, sizeof(buf), f)) {
  76. /* All three must have length >= buf to avoid overflows. */
  77. char hostbuf[RBUFLN], userbuf[RBUFLN], dummy[RBUFLN];
  78. char *host, *user, *cp;
  79. int negated;
  80. for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
  81. ;
  82. if (*cp == '#' || *cp == '\n' || !*cp)
  83. continue;
  84. /*
  85. * NO_PLUS is supported at least on OSF/1. We skip it (we
  86. * don't ever support the plus syntax).
  87. */
  88. if (strncmp(cp, "NO_PLUS", 7) == 0)
  89. continue;
  90. /*
  91. * This should be safe because each buffer is as big as the
  92. * whole string, and thus cannot be overwritten.
  93. */
  94. switch (sscanf(buf, "%1023s %1023s %1023s", hostbuf, userbuf,
  95. dummy)) {
  96. case 0:
  97. auth_debug_add("Found empty line in %.100s.", filename);
  98. continue;
  99. case 1:
  100. /* Host name only. */
  101. strlcpy(userbuf, server_user, sizeof(userbuf));
  102. break;
  103. case 2:
  104. /* Got both host and user name. */
  105. break;
  106. case 3:
  107. auth_debug_add("Found garbage in %.100s.", filename);
  108. continue;
  109. default:
  110. /* Weird... */
  111. continue;
  112. }
  113. host = hostbuf;
  114. user = userbuf;
  115. negated = 0;
  116. /* Process negated host names, or positive netgroups. */
  117. if (host[0] == '-') {
  118. negated = 1;
  119. host++;
  120. } else if (host[0] == '+')
  121. host++;
  122. if (user[0] == '-') {
  123. negated = 1;
  124. user++;
  125. } else if (user[0] == '+')
  126. user++;
  127. /* Check for empty host/user names (particularly '+'). */
  128. if (!host[0] || !user[0]) {
  129. /* We come here if either was '+' or '-'. */
  130. auth_debug_add("Ignoring wild host/user names "
  131. "in %.100s.", filename);
  132. continue;
  133. }
  134. /* Verify that host name matches. */
  135. if (host[0] == '@') {
  136. if (!innetgr(host + 1, hostname, NULL, NULL) &&
  137. !innetgr(host + 1, ipaddr, NULL, NULL))
  138. continue;
  139. } else if (strcasecmp(host, hostname) &&
  140. strcmp(host, ipaddr) != 0)
  141. continue; /* Different hostname. */
  142. /* Verify that user name matches. */
  143. if (user[0] == '@') {
  144. if (!innetgr(user + 1, NULL, client_user, NULL))
  145. continue;
  146. } else if (strcmp(user, client_user) != 0)
  147. continue; /* Different username. */
  148. /* Found the user and host. */
  149. fclose(f);
  150. /* If the entry was negated, deny access. */
  151. if (negated) {
  152. auth_debug_add("Matched negative entry in %.100s.",
  153. filename);
  154. return 0;
  155. }
  156. /* Accept authentication. */
  157. return 1;
  158. }
  159. /* Authentication using this file denied. */
  160. fclose(f);
  161. return 0;
  162. }
  163. /*
  164. * Tries to authenticate the user using the .shosts or .rhosts file. Returns
  165. * true if authentication succeeds. If ignore_rhosts is true, only
  166. * /etc/hosts.equiv will be considered (.rhosts and .shosts are ignored).
  167. */
  168. int
  169. auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname,
  170. const char *ipaddr)
  171. {
  172. char buf[1024];
  173. struct stat st;
  174. static const char *rhosts_files[] = {".shosts", ".rhosts", NULL};
  175. u_int rhosts_file_index;
  176. debug2("auth_rhosts2: clientuser %s hostname %s ipaddr %s",
  177. client_user, hostname, ipaddr);
  178. /* Switch to the user's uid. */
  179. temporarily_use_uid(pw);
  180. /*
  181. * Quick check: if the user has no .shosts or .rhosts files and
  182. * no system hosts.equiv/shosts.equiv files exist then return
  183. * failure immediately without doing costly lookups from name
  184. * servers.
  185. */
  186. for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
  187. rhosts_file_index++) {
  188. /* Check users .rhosts or .shosts. */
  189. snprintf(buf, sizeof buf, "%.500s/%.100s",
  190. pw->pw_dir, rhosts_files[rhosts_file_index]);
  191. if (stat(buf, &st) >= 0)
  192. break;
  193. }
  194. /* Switch back to privileged uid. */
  195. restore_uid();
  196. /*
  197. * Deny if The user has no .shosts or .rhosts file and there
  198. * are no system-wide files.
  199. */
  200. if (!rhosts_files[rhosts_file_index] &&
  201. stat(_PATH_RHOSTS_EQUIV, &st) == -1 &&
  202. stat(_PATH_SSH_HOSTS_EQUIV, &st) == -1) {
  203. debug3("%s: no hosts access files exist", __func__);
  204. return 0;
  205. }
  206. /*
  207. * If not logging in as superuser, try /etc/hosts.equiv and
  208. * shosts.equiv.
  209. */
  210. if (pw->pw_uid == 0)
  211. debug3("%s: root user, ignoring system hosts files", __func__);
  212. else {
  213. if (check_rhosts_file(_PATH_RHOSTS_EQUIV, hostname, ipaddr,
  214. client_user, pw->pw_name)) {
  215. auth_debug_add("Accepted for %.100s [%.100s] by "
  216. "/etc/hosts.equiv.", hostname, ipaddr);
  217. return 1;
  218. }
  219. if (check_rhosts_file(_PATH_SSH_HOSTS_EQUIV, hostname, ipaddr,
  220. client_user, pw->pw_name)) {
  221. auth_debug_add("Accepted for %.100s [%.100s] by "
  222. "%.100s.", hostname, ipaddr, _PATH_SSH_HOSTS_EQUIV);
  223. return 1;
  224. }
  225. }
  226. /*
  227. * Check that the home directory is owned by root or the user, and is
  228. * not group or world writable.
  229. */
  230. if (stat(pw->pw_dir, &st) == -1) {
  231. logit("Rhosts authentication refused for %.100s: "
  232. "no home directory %.200s", pw->pw_name, pw->pw_dir);
  233. auth_debug_add("Rhosts authentication refused for %.100s: "
  234. "no home directory %.200s", pw->pw_name, pw->pw_dir);
  235. return 0;
  236. }
  237. if (options.strict_modes &&
  238. ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
  239. (st.st_mode & 022) != 0)) {
  240. logit("Rhosts authentication refused for %.100s: "
  241. "bad ownership or modes for home directory.", pw->pw_name);
  242. auth_debug_add("Rhosts authentication refused for %.100s: "
  243. "bad ownership or modes for home directory.", pw->pw_name);
  244. return 0;
  245. }
  246. /* Temporarily use the user's uid. */
  247. temporarily_use_uid(pw);
  248. /* Check all .rhosts files (currently .shosts and .rhosts). */
  249. for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
  250. rhosts_file_index++) {
  251. /* Check users .rhosts or .shosts. */
  252. snprintf(buf, sizeof buf, "%.500s/%.100s",
  253. pw->pw_dir, rhosts_files[rhosts_file_index]);
  254. if (stat(buf, &st) == -1)
  255. continue;
  256. /*
  257. * Make sure that the file is either owned by the user or by
  258. * root, and make sure it is not writable by anyone but the
  259. * owner. This is to help avoid novices accidentally
  260. * allowing access to their account by anyone.
  261. */
  262. if (options.strict_modes &&
  263. ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
  264. (st.st_mode & 022) != 0)) {
  265. logit("Rhosts authentication refused for %.100s: bad modes for %.200s",
  266. pw->pw_name, buf);
  267. auth_debug_add("Bad file modes for %.200s", buf);
  268. continue;
  269. }
  270. /*
  271. * Check if we have been configured to ignore .rhosts
  272. * and .shosts files.
  273. */
  274. if (options.ignore_rhosts == IGNORE_RHOSTS_YES ||
  275. (options.ignore_rhosts == IGNORE_RHOSTS_SHOSTS &&
  276. strcmp(rhosts_files[rhosts_file_index], ".shosts") != 0)) {
  277. auth_debug_add("Server has been configured to "
  278. "ignore %.100s.", rhosts_files[rhosts_file_index]);
  279. continue;
  280. }
  281. /* Check if authentication is permitted by the file. */
  282. if (check_rhosts_file(buf, hostname, ipaddr,
  283. client_user, pw->pw_name)) {
  284. auth_debug_add("Accepted by %.100s.",
  285. rhosts_files[rhosts_file_index]);
  286. /* Restore the privileged uid. */
  287. restore_uid();
  288. auth_debug_add("Accepted host %s ip %s client_user "
  289. "%s server_user %s", hostname, ipaddr,
  290. client_user, pw->pw_name);
  291. return 1;
  292. }
  293. }
  294. /* Restore the privileged uid. */
  295. restore_uid();
  296. return 0;
  297. }