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