]> andersk Git - openssh.git/blob - auth-rhosts.c
- Merged OpenBSD CVS changes:
[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
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 }
This page took 0.055405 seconds and 5 git commands to generate.