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