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: auth-rhosts.c,v 1.12 1999/12/27 10:46:11 markus Exp $"); 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 } 267