]> andersk Git - openssh.git/blob - auth-rhosts.c
- OpenBSD CVS updates:
[openssh.git] / auth-rhosts.c
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$");
20
21 #include "packet.h"
22 #include "ssh.h"
23 #include "xmalloc.h"
24 #include "uidswap.h"
25 #include "servconf.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         unsigned int rhosts_file_index;
158
159         /* Switch to the user's uid. */
160         temporarily_use_uid(pw->pw_uid);
161         /*
162          * Quick check: if the user has no .shosts or .rhosts files, return
163          * failure immediately without doing costly lookups from name
164          * servers.
165          */
166         for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
167              rhosts_file_index++) {
168                 /* Check users .rhosts or .shosts. */
169                 snprintf(buf, sizeof buf, "%.500s/%.100s",
170                          pw->pw_dir, rhosts_files[rhosts_file_index]);
171                 if (stat(buf, &st) >= 0)
172                         break;
173         }
174         /* Switch back to privileged uid. */
175         restore_uid();
176
177         /* Deny if The user has no .shosts or .rhosts file and there are no system-wide files. */
178         if (!rhosts_files[rhosts_file_index] &&
179             stat("/etc/hosts.equiv", &st) < 0 &&
180             stat(SSH_HOSTS_EQUIV, &st) < 0)
181                 return 0;
182
183         hostname = get_canonical_hostname();
184         ipaddr = get_remote_ipaddr();
185
186         /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */
187         if (pw->pw_uid != 0) {
188                 if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user,
189                                       pw->pw_name)) {
190                         packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.",
191                                           hostname, ipaddr);
192                         return 1;
193                 }
194                 if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user,
195                                       pw->pw_name)) {
196                         packet_send_debug("Accepted for %.100s [%.100s] by %.100s.",
197                                       hostname, ipaddr, SSH_HOSTS_EQUIV);
198                         return 1;
199                 }
200         }
201         /*
202          * Check that the home directory is owned by root or the user, and is
203          * not group or world writable.
204          */
205         if (stat(pw->pw_dir, &st) < 0) {
206                 log("Rhosts authentication refused for %.100s: no home directory %.200s",
207                     pw->pw_name, pw->pw_dir);
208                 packet_send_debug("Rhosts authentication refused for %.100s: no home directory %.200s",
209                                   pw->pw_name, pw->pw_dir);
210                 return 0;
211         }
212         if (options.strict_modes &&
213             ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
214              (st.st_mode & 022) != 0)) {
215                 log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.",
216                     pw->pw_name);
217                 packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.",
218                                   pw->pw_name);
219                 return 0;
220         }
221         /* Temporarily use the user's uid. */
222         temporarily_use_uid(pw->pw_uid);
223
224         /* Check all .rhosts files (currently .shosts and .rhosts). */
225         for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
226              rhosts_file_index++) {
227                 /* Check users .rhosts or .shosts. */
228                 snprintf(buf, sizeof buf, "%.500s/%.100s",
229                          pw->pw_dir, rhosts_files[rhosts_file_index]);
230                 if (stat(buf, &st) < 0)
231                         continue;
232
233                 /*
234                  * Make sure that the file is either owned by the user or by
235                  * root, and make sure it is not writable by anyone but the
236                  * owner.  This is to help avoid novices accidentally
237                  * allowing access to their account by anyone.
238                  */
239                 if (options.strict_modes &&
240                     ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
241                      (st.st_mode & 022) != 0)) {
242                         log("Rhosts authentication refused for %.100s: bad modes for %.200s",
243                             pw->pw_name, buf);
244                         packet_send_debug("Bad file modes for %.200s", buf);
245                         continue;
246                 }
247                 /* Check if we have been configured to ignore .rhosts and .shosts files. */
248                 if (options.ignore_rhosts) {
249                         packet_send_debug("Server has been configured to ignore %.100s.",
250                                           rhosts_files[rhosts_file_index]);
251                         continue;
252                 }
253                 /* Check if authentication is permitted by the file. */
254                 if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) {
255                         packet_send_debug("Accepted by %.100s.",
256                                           rhosts_files[rhosts_file_index]);
257                         /* Restore the privileged uid. */
258                         restore_uid();
259                         return 1;
260                 }
261         }
262
263         /* Restore the privileged uid. */
264         restore_uid();
265         return 0;
266 }
This page took 0.358848 seconds and 5 git commands to generate.