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