]> andersk Git - openssh.git/blob - auth-rhosts.c
c1f1034ef0a7f9cd4c8cc7278b303d2bcb84732f
[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 /* This function processes an rhosts-style file (.rhosts, .shosts, or
28    /etc/hosts.equiv).  This returns true if authentication can be granted
29    based on the file, and returns zero otherwise. */
30
31 int check_rhosts_file(const char *filename, const char *hostname,
32                       const char *ipaddr, const char *client_user,
33                       const char *server_user)
34 {
35   FILE *f;
36   char buf[1024]; /* Must not be larger than host, user, dummy below. */
37   
38   /* Open the .rhosts file. */
39   f = fopen(filename, "r");
40   if (!f)
41     return 0; /* Cannot read the .rhosts - deny access. */
42
43   /* Go through the file, checking every entry. */
44   while (fgets(buf, sizeof(buf), f))
45     {
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;
48       int negated;
49       
50       for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
51         ;
52       if (*cp == '#' || *cp == '\n' || !*cp)
53         continue;
54
55       /* NO_PLUS is supported at least on OSF/1.  We skip it (we don't ever
56          support the plus syntax). */
57       if (strncmp(cp, "NO_PLUS", 7) == 0)
58         continue;
59
60       /* This should be safe because each buffer is as big as the whole
61          string, and thus cannot be overwritten. */
62       switch (sscanf(buf, "%s %s %s", hostbuf, userbuf, dummy))
63         {
64         case 0:
65           packet_send_debug("Found empty line in %.100s.", filename);
66           continue; /* Empty line? */
67         case 1:
68           /* Host name only. */
69           strlcpy(userbuf, server_user, sizeof(userbuf));
70           break;
71         case 2:
72           /* Got both host and user name. */
73           break;
74         case 3:
75           packet_send_debug("Found garbage in %.100s.", filename);
76           continue; /* Extra garbage */
77         default:
78           continue; /* Weird... */
79         }
80
81       host = hostbuf;
82       user = userbuf;
83       negated = 0;
84
85       /* Process negated host names, or positive netgroups. */
86       if (host[0] == '-')
87         {
88           negated = 1;
89           host++;
90         }
91       else
92         if (host[0] == '+')
93           host++;
94
95       if (user[0] == '-')
96         {
97           negated = 1;
98           user++;
99         }
100       else
101         if (user[0] == '+')
102           user++;
103
104       /* Check for empty host/user names (particularly '+'). */
105       if (!host[0] || !user[0])
106         { 
107           /* We come here if either was '+' or '-'. */
108           packet_send_debug("Ignoring wild host/user names in %.100s.",
109                             filename);
110           continue;
111         }
112           
113       /* Verify that host name matches. */
114       if (host[0] == '@')
115         {
116           if (!innetgr(host + 1, hostname, NULL, NULL) &&
117               !innetgr(host + 1, ipaddr, NULL, NULL))
118             continue;
119         }
120       else
121         if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0)
122           continue; /* Different hostname. */
123
124       /* Verify that user name matches. */
125       if (user[0] == '@')
126         {
127           if (!innetgr(user + 1, NULL, client_user, NULL))
128             continue;
129         }
130       else
131         if (strcmp(user, client_user) != 0)
132           continue; /* Different username. */
133
134       /* Found the user and host. */
135       fclose(f);
136
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 }
153
154 /* Tries to authenticate the user using the .shosts or .rhosts file.  
155    Returns true if authentication succeeds.  If ignore_rhosts is
156    true, only /etc/hosts.equiv will be considered (.rhosts and .shosts
157    are ignored). */
158
159 int auth_rhosts(struct passwd *pw, const char *client_user)
160 {
161   extern ServerOptions options;
162   char buf[1024];
163   const char *hostname, *ipaddr;
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
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         }
203       if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user,
204                             pw->pw_name))
205         {
206           packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", 
207                             hostname, ipaddr, SSH_HOSTS_EQUIV);
208           return 1;
209         }
210     }
211
212   /* Check that the home directory is owned by root or the user, and is not 
213      group or world writable. */
214   if (stat(pw->pw_dir, &st) < 0)
215     {
216       log("Rhosts authentication refused for %.100: no home directory %.200s",
217           pw->pw_name, pw->pw_dir);
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         }
258
259       /* Check if we have been configured to ignore .rhosts and .shosts 
260          files. */
261       if (options.ignore_rhosts)
262         {
263           packet_send_debug("Server has been configured to ignore %.100s.",
264                             rhosts_files[rhosts_file_index]);
265           continue;
266         }
267
268       /* Check if authentication is permitted by the file. */
269       if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name))
270         {
271           packet_send_debug("Accepted by %.100s.",
272                             rhosts_files[rhosts_file_index]);
273           /* Restore the privileged uid. */
274           restore_uid();
275           return 1;
276         }
277     }
278
279   /* Rhosts authentication denied. */
280   /* Restore the privileged uid. */
281   restore_uid();
282   return 0;
283 }
This page took 0.048853 seconds and 3 git commands to generate.