diff options
Diffstat (limited to 'auth-rhosts.c')
-rw-r--r-- | auth-rhosts.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/auth-rhosts.c b/auth-rhosts.c new file mode 100644 index 000000000..ebf2fcbc2 --- /dev/null +++ b/auth-rhosts.c | |||
@@ -0,0 +1,298 @@ | |||
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" | ||
19 | RCSID("$Id: auth-rhosts.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | ||
20 | |||
21 | #include "packet.h" | ||
22 | #include "ssh.h" | ||
23 | #include "xmalloc.h" | ||
24 | #include "uidswap.h" | ||
25 | |||
26 | /* This function processes an rhosts-style file (.rhosts, .shosts, or | ||
27 | /etc/hosts.equiv). This returns true if authentication can be granted | ||
28 | based on the file, and returns zero otherwise. */ | ||
29 | |||
30 | int check_rhosts_file(const char *filename, const char *hostname, | ||
31 | const char *ipaddr, const char *client_user, | ||
32 | const char *server_user) | ||
33 | { | ||
34 | FILE *f; | ||
35 | char buf[1024]; /* Must not be larger than host, user, dummy below. */ | ||
36 | |||
37 | /* Open the .rhosts file. */ | ||
38 | f = fopen(filename, "r"); | ||
39 | if (!f) | ||
40 | return 0; /* Cannot read the .rhosts - deny access. */ | ||
41 | |||
42 | /* Go through the file, checking every entry. */ | ||
43 | while (fgets(buf, sizeof(buf), f)) | ||
44 | { | ||
45 | /* All three must be at least as big as buf to avoid overflows. */ | ||
46 | char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp; | ||
47 | int negated; | ||
48 | |||
49 | for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) | ||
50 | ; | ||
51 | if (*cp == '#' || *cp == '\n' || !*cp) | ||
52 | continue; | ||
53 | |||
54 | /* NO_PLUS is supported at least on OSF/1. We skip it (we don't ever | ||
55 | support the plus syntax). */ | ||
56 | if (strncmp(cp, "NO_PLUS", 7) == 0) | ||
57 | continue; | ||
58 | |||
59 | /* This should be safe because each buffer is as big as the whole | ||
60 | string, and thus cannot be overwritten. */ | ||
61 | switch (sscanf(buf, "%s %s %s", hostbuf, userbuf, dummy)) | ||
62 | { | ||
63 | case 0: | ||
64 | packet_send_debug("Found empty line in %.100s.", filename); | ||
65 | continue; /* Empty line? */ | ||
66 | case 1: | ||
67 | /* Host name only. */ | ||
68 | strlcpy(userbuf, server_user, sizeof(userbuf)); | ||
69 | break; | ||
70 | case 2: | ||
71 | /* Got both host and user name. */ | ||
72 | break; | ||
73 | case 3: | ||
74 | packet_send_debug("Found garbage in %.100s.", filename); | ||
75 | continue; /* Extra garbage */ | ||
76 | default: | ||
77 | continue; /* Weird... */ | ||
78 | } | ||
79 | |||
80 | host = hostbuf; | ||
81 | user = userbuf; | ||
82 | negated = 0; | ||
83 | |||
84 | /* Process negated host names, or positive netgroups. */ | ||
85 | if (host[0] == '-') | ||
86 | { | ||
87 | negated = 1; | ||
88 | host++; | ||
89 | } | ||
90 | else | ||
91 | if (host[0] == '+') | ||
92 | host++; | ||
93 | |||
94 | if (user[0] == '-') | ||
95 | { | ||
96 | negated = 1; | ||
97 | user++; | ||
98 | } | ||
99 | else | ||
100 | if (user[0] == '+') | ||
101 | user++; | ||
102 | |||
103 | /* Check for empty host/user names (particularly '+'). */ | ||
104 | if (!host[0] || !user[0]) | ||
105 | { | ||
106 | /* We come here if either was '+' or '-'. */ | ||
107 | packet_send_debug("Ignoring wild host/user names in %.100s.", | ||
108 | filename); | ||
109 | continue; | ||
110 | } | ||
111 | |||
112 | /* Verify that host name matches. */ | ||
113 | if (host[0] == '@') | ||
114 | { | ||
115 | if (!innetgr(host + 1, hostname, NULL, NULL) && | ||
116 | !innetgr(host + 1, ipaddr, NULL, NULL)) | ||
117 | continue; | ||
118 | } | ||
119 | else | ||
120 | if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0) | ||
121 | continue; /* Different hostname. */ | ||
122 | |||
123 | /* Verify that user name matches. */ | ||
124 | if (user[0] == '@') | ||
125 | { | ||
126 | if (!innetgr(user + 1, NULL, client_user, NULL)) | ||
127 | continue; | ||
128 | } | ||
129 | else | ||
130 | if (strcmp(user, client_user) != 0) | ||
131 | continue; /* Different username. */ | ||
132 | |||
133 | /* Found the user and host. */ | ||
134 | fclose(f); | ||
135 | |||
136 | /* If the entry was negated, deny access. */ | ||
137 | if (negated) | ||
138 | { | ||
139 | packet_send_debug("Matched negative entry in %.100s.", | ||
140 | filename); | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | /* Accept authentication. */ | ||
145 | return 1; | ||
146 | } | ||
147 | |||
148 | /* Authentication using this file denied. */ | ||
149 | fclose(f); | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | /* Tries to authenticate the user using the .shosts or .rhosts file. | ||
154 | Returns true if authentication succeeds. If ignore_rhosts is | ||
155 | true, only /etc/hosts.equiv will be considered (.rhosts and .shosts | ||
156 | are ignored). */ | ||
157 | |||
158 | int auth_rhosts(struct passwd *pw, const char *client_user, | ||
159 | int ignore_rhosts, int strict_modes) | ||
160 | { | ||
161 | char buf[1024]; | ||
162 | const char *hostname, *ipaddr; | ||
163 | int port; | ||
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. */ | ||
176 | snprintf(buf, sizeof buf, "%.500s/%.100s", | ||
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 | port = get_remote_port(); | ||
193 | |||
194 | /* Check that the connection comes from a privileged port. | ||
195 | Rhosts authentication only makes sense for priviledged programs. | ||
196 | Of course, if the intruder has root access on his local machine, | ||
197 | he can connect from any port. So do not use .rhosts | ||
198 | authentication from machines that you do not trust. */ | ||
199 | if (port >= IPPORT_RESERVED || | ||
200 | port < IPPORT_RESERVED / 2) | ||
201 | { | ||
202 | log("Connection from %.100s from nonpriviledged port %d", | ||
203 | hostname, port); | ||
204 | packet_send_debug("Your ssh client is not running as root."); | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */ | ||
209 | if (pw->pw_uid != 0) | ||
210 | { | ||
211 | if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user, | ||
212 | pw->pw_name)) | ||
213 | { | ||
214 | packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.", | ||
215 | hostname, ipaddr); | ||
216 | return 1; | ||
217 | } | ||
218 | if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user, | ||
219 | pw->pw_name)) | ||
220 | { | ||
221 | packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", | ||
222 | hostname, ipaddr, SSH_HOSTS_EQUIV); | ||
223 | return 1; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | /* Check that the home directory is owned by root or the user, and is not | ||
228 | group or world writable. */ | ||
229 | if (stat(pw->pw_dir, &st) < 0) | ||
230 | { | ||
231 | log("Rhosts authentication refused for %.100: no home directory %.200s", | ||
232 | pw->pw_name, pw->pw_dir); | ||
233 | packet_send_debug("Rhosts authentication refused for %.100: no home directory %.200s", | ||
234 | pw->pw_name, pw->pw_dir); | ||
235 | return 0; | ||
236 | } | ||
237 | if (strict_modes && | ||
238 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
239 | (st.st_mode & 022) != 0)) | ||
240 | { | ||
241 | log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", | ||
242 | pw->pw_name); | ||
243 | packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", | ||
244 | pw->pw_name); | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | /* Check all .rhosts files (currently .shosts and .rhosts). */ | ||
249 | /* Temporarily use the user's uid. */ | ||
250 | temporarily_use_uid(pw->pw_uid); | ||
251 | for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; | ||
252 | rhosts_file_index++) | ||
253 | { | ||
254 | /* Check users .rhosts or .shosts. */ | ||
255 | snprintf(buf, sizeof buf, "%.500s/%.100s", | ||
256 | pw->pw_dir, rhosts_files[rhosts_file_index]); | ||
257 | if (stat(buf, &st) < 0) | ||
258 | continue; /* No such file. */ | ||
259 | |||
260 | /* Make sure that the file is either owned by the user or by root, | ||
261 | and make sure it is not writable by anyone but the owner. This is | ||
262 | to help avoid novices accidentally allowing access to their account | ||
263 | by anyone. */ | ||
264 | if (strict_modes && | ||
265 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
266 | (st.st_mode & 022) != 0)) | ||
267 | { | ||
268 | log("Rhosts authentication refused for %.100s: bad modes for %.200s", | ||
269 | pw->pw_name, buf); | ||
270 | packet_send_debug("Bad file modes for %.200s", buf); | ||
271 | continue; | ||
272 | } | ||
273 | |||
274 | /* Check if we have been configured to ignore .rhosts and .shosts | ||
275 | files. */ | ||
276 | if (ignore_rhosts) | ||
277 | { | ||
278 | packet_send_debug("Server has been configured to ignore %.100s.", | ||
279 | rhosts_files[rhosts_file_index]); | ||
280 | continue; | ||
281 | } | ||
282 | |||
283 | /* Check if authentication is permitted by the file. */ | ||
284 | if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) | ||
285 | { | ||
286 | packet_send_debug("Accepted by %.100s.", | ||
287 | rhosts_files[rhosts_file_index]); | ||
288 | /* Restore the privileged uid. */ | ||
289 | restore_uid(); | ||
290 | return 1; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | /* Rhosts authentication denied. */ | ||
295 | /* Restore the privileged uid. */ | ||
296 | restore_uid(); | ||
297 | return 0; | ||
298 | } | ||