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