summaryrefslogtreecommitdiff
path: root/auth-rhosts.c
diff options
context:
space:
mode:
Diffstat (limited to 'auth-rhosts.c')
-rw-r--r--auth-rhosts.c476
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 *
3auth-rhosts.c 3 * auth-rhosts.c
4 4 *
5Author: Tatu Ylonen <ylo@cs.hut.fi> 5 * Author: Tatu Ylonen <ylo@cs.hut.fi>
6 6 *
7Copyright (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 *
10Created: Fri Mar 17 05:12:18 1995 ylo 10 * Created: Fri Mar 17 05:12:18 1995 ylo
11 11 *
12Rhosts authentication. This file contains code to check whether to admit 12 * Rhosts authentication. This file contains code to check whether to admit
13the 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"
19RCSID("$Id: auth-rhosts.c,v 1.4 1999/11/18 21:25:48 damien Exp $"); 19RCSID("$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
31int check_rhosts_file(const char *filename, const char *hostname, 31int
32 const char *ipaddr, const char *client_user, 32check_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
159int auth_rhosts(struct passwd *pw, const char *client_user) 143int
144auth_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}