Annotation of src/usr.bin/ssh/auth-rhosts.c, Revision 1.1
1.1 ! deraadt 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.2 1999/05/04 11:58:24 bg Exp $");
! 20:
! 21: #include "packet.h"
! 22: #include "ssh.h"
! 23: #include "xmalloc.h"
! 24: #include "uidswap.h"
! 25:
! 26: /* Returns true if the strings are equal, ignoring case (a-z only). */
! 27:
! 28: static int casefold_equal(const char *a, const char *b)
! 29: {
! 30: unsigned char cha, chb;
! 31: for (; *a; a++, b++)
! 32: {
! 33: cha = *a;
! 34: chb = *b;
! 35: if (!chb)
! 36: return 0;
! 37: if (cha >= 'a' && cha <= 'z')
! 38: cha -= 32;
! 39: if (chb >= 'a' && chb <= 'z')
! 40: chb -= 32;
! 41: if (cha != chb)
! 42: return 0;
! 43: }
! 44: return !*b;
! 45: }
! 46:
! 47: /* This function processes an rhosts-style file (.rhosts, .shosts, or
! 48: /etc/hosts.equiv). This returns true if authentication can be granted
! 49: based on the file, and returns zero otherwise. */
! 50:
! 51: int check_rhosts_file(const char *filename, const char *hostname,
! 52: const char *ipaddr, const char *client_user,
! 53: const char *server_user)
! 54: {
! 55: FILE *f;
! 56: char buf[1024]; /* Must not be larger than host, user, dummy below. */
! 57:
! 58: /* Open the .rhosts file. */
! 59: f = fopen(filename, "r");
! 60: if (!f)
! 61: return 0; /* Cannot read the .rhosts - deny access. */
! 62:
! 63: /* Go through the file, checking every entry. */
! 64: while (fgets(buf, sizeof(buf), f))
! 65: {
! 66: /* All three must be at least as big as buf to avoid overflows. */
! 67: char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp;
! 68: int negated;
! 69:
! 70: for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
! 71: ;
! 72: if (*cp == '#' || *cp == '\n' || !*cp)
! 73: continue;
! 74:
! 75: /* NO_PLUS is supported at least on OSF/1. We skip it (we don't ever
! 76: support the plus syntax). */
! 77: if (strncmp(cp, "NO_PLUS", 7) == 0)
! 78: continue;
! 79:
! 80: /* This should be safe because each buffer is as big as the whole
! 81: string, and thus cannot be overwritten. */
! 82: switch (sscanf(buf, "%s %s %s", hostbuf, userbuf, dummy))
! 83: {
! 84: case 0:
! 85: packet_send_debug("Found empty line in %.100s.", filename);
! 86: continue; /* Empty line? */
! 87: case 1:
! 88: /* Host name only. */
! 89: strncpy(userbuf, server_user, sizeof(userbuf));
! 90: userbuf[sizeof(userbuf) - 1] = 0;
! 91: break;
! 92: case 2:
! 93: /* Got both host and user name. */
! 94: break;
! 95: case 3:
! 96: packet_send_debug("Found garbage in %.100s.", filename);
! 97: continue; /* Extra garbage */
! 98: default:
! 99: continue; /* Weird... */
! 100: }
! 101:
! 102: host = hostbuf;
! 103: user = userbuf;
! 104: negated = 0;
! 105:
! 106: /* Process negated host names, or positive netgroups. */
! 107: if (host[0] == '-')
! 108: {
! 109: negated = 1;
! 110: host++;
! 111: }
! 112: else
! 113: if (host[0] == '+')
! 114: host++;
! 115:
! 116: if (user[0] == '-')
! 117: {
! 118: negated = 1;
! 119: user++;
! 120: }
! 121: else
! 122: if (user[0] == '+')
! 123: user++;
! 124:
! 125: /* Check for empty host/user names (particularly '+'). */
! 126: if (!host[0] || !user[0])
! 127: {
! 128: /* We come here if either was '+' or '-'. */
! 129: packet_send_debug("Ignoring wild host/user names in %.100s.",
! 130: filename);
! 131: continue;
! 132: }
! 133:
! 134: #ifdef HAVE_INNETGR
! 135:
! 136: /* Verify that host name matches. */
! 137: if (host[0] == '@')
! 138: {
! 139: if (!innetgr(host + 1, hostname, NULL, NULL) &&
! 140: !innetgr(host + 1, ipaddr, NULL, NULL))
! 141: continue;
! 142: }
! 143: else
! 144: if (!casefold_equal(host, hostname) && strcmp(host, ipaddr) != 0)
! 145: continue; /* Different hostname. */
! 146:
! 147: /* Verify that user name matches. */
! 148: if (user[0] == '@')
! 149: {
! 150: if (!innetgr(user + 1, NULL, client_user, NULL))
! 151: continue;
! 152: }
! 153: else
! 154: if (strcmp(user, client_user) != 0)
! 155: continue; /* Different username. */
! 156:
! 157: #else /* HAVE_INNETGR */
! 158:
! 159: if (!casefold_equal(host, hostname) && strcmp(host, ipaddr) != 0)
! 160: continue; /* Different hostname. */
! 161:
! 162: if (strcmp(user, client_user) != 0)
! 163: continue; /* Different username. */
! 164:
! 165: #endif /* HAVE_INNETGR */
! 166:
! 167: /* Found the user and host. */
! 168: fclose(f);
! 169:
! 170: /* If the entry was negated, deny access. */
! 171: if (negated)
! 172: {
! 173: packet_send_debug("Matched negative entry in %.100s.",
! 174: filename);
! 175: return 0;
! 176: }
! 177:
! 178: /* Accept authentication. */
! 179: return 1;
! 180: }
! 181:
! 182: /* Authentication using this file denied. */
! 183: fclose(f);
! 184: return 0;
! 185: }
! 186:
! 187: /* Tries to authenticate the user using the .shosts or .rhosts file.
! 188: Returns true if authentication succeeds. If ignore_rhosts is
! 189: true, only /etc/hosts.equiv will be considered (.rhosts and .shosts
! 190: are ignored). */
! 191:
! 192: int auth_rhosts(struct passwd *pw, const char *client_user,
! 193: int ignore_rhosts, int strict_modes)
! 194: {
! 195: char buf[1024];
! 196: const char *hostname, *ipaddr;
! 197: int port;
! 198: struct stat st;
! 199: static const char *rhosts_files[] = { ".shosts", ".rhosts", NULL };
! 200: unsigned int rhosts_file_index;
! 201:
! 202: /* Quick check: if the user has no .shosts or .rhosts files, return failure
! 203: immediately without doing costly lookups from name servers. */
! 204: /* Switch to the user's uid. */
! 205: temporarily_use_uid(pw->pw_uid);
! 206: for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
! 207: rhosts_file_index++)
! 208: {
! 209: /* Check users .rhosts or .shosts. */
! 210: sprintf(buf, "%.500s/%.100s",
! 211: pw->pw_dir, rhosts_files[rhosts_file_index]);
! 212: if (stat(buf, &st) >= 0)
! 213: break;
! 214: }
! 215: /* Switch back to privileged uid. */
! 216: restore_uid();
! 217:
! 218: if (!rhosts_files[rhosts_file_index] && stat("/etc/hosts.equiv", &st) < 0 &&
! 219: stat(SSH_HOSTS_EQUIV, &st) < 0)
! 220: return 0; /* The user has no .shosts or .rhosts file and there are no
! 221: system-wide files. */
! 222:
! 223: /* Get the name, address, and port of the remote host. */
! 224: hostname = get_canonical_hostname();
! 225: ipaddr = get_remote_ipaddr();
! 226: port = get_remote_port();
! 227:
! 228: /* Check that the connection comes from a privileged port.
! 229: Rhosts authentication only makes sense for priviledged programs.
! 230: Of course, if the intruder has root access on his local machine,
! 231: he can connect from any port. So do not use .rhosts
! 232: authentication from machines that you do not trust. */
! 233: if (port >= IPPORT_RESERVED ||
! 234: port < IPPORT_RESERVED / 2)
! 235: {
! 236: log("Connection from %.100s from nonpriviledged port %d",
! 237: hostname, port);
! 238: packet_send_debug("Your ssh client is not running as root.");
! 239: return 0;
! 240: }
! 241:
! 242: /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */
! 243: if (pw->pw_uid != 0)
! 244: {
! 245: if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user,
! 246: pw->pw_name))
! 247: {
! 248: packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.",
! 249: hostname, ipaddr);
! 250: return 1;
! 251: }
! 252: if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user,
! 253: pw->pw_name))
! 254: {
! 255: packet_send_debug("Accepted for %.100s [%.100s] by %.100s.",
! 256: hostname, ipaddr, SSH_HOSTS_EQUIV);
! 257: return 1;
! 258: }
! 259: }
! 260:
! 261: /* Check that the home directory is owned by root or the user, and is not
! 262: group or world writable. */
! 263: if (stat(pw->pw_dir, &st) < 0)
! 264: {
! 265: log("Rhosts authentication refused for %.100: no home directory %.200s",
! 266: pw->pw_name, pw->pw_dir);
! 267: packet_send_debug("Rhosts authentication refused for %.100: no home directory %.200s",
! 268: pw->pw_name, pw->pw_dir);
! 269: return 0;
! 270: }
! 271: if (strict_modes &&
! 272: ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
! 273: (st.st_mode & 022) != 0))
! 274: {
! 275: log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.",
! 276: pw->pw_name);
! 277: packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.",
! 278: pw->pw_name);
! 279: return 0;
! 280: }
! 281:
! 282: /* Check all .rhosts files (currently .shosts and .rhosts). */
! 283: /* Temporarily use the user's uid. */
! 284: temporarily_use_uid(pw->pw_uid);
! 285: for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
! 286: rhosts_file_index++)
! 287: {
! 288: /* Check users .rhosts or .shosts. */
! 289: sprintf(buf, "%.500s/%.100s",
! 290: pw->pw_dir, rhosts_files[rhosts_file_index]);
! 291: if (stat(buf, &st) < 0)
! 292: continue; /* No such file. */
! 293:
! 294: /* Make sure that the file is either owned by the user or by root,
! 295: and make sure it is not writable by anyone but the owner. This is
! 296: to help avoid novices accidentally allowing access to their account
! 297: by anyone. */
! 298: if (strict_modes &&
! 299: ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
! 300: (st.st_mode & 022) != 0))
! 301: {
! 302: log("Rhosts authentication refused for %.100s: bad modes for %.200s",
! 303: pw->pw_name, buf);
! 304: packet_send_debug("Bad file modes for %.200s", buf);
! 305: continue;
! 306: }
! 307:
! 308: /* Check if we have been configured to ignore .rhosts and .shosts
! 309: files. */
! 310: if (ignore_rhosts)
! 311: {
! 312: packet_send_debug("Server has been configured to ignore %.100s.",
! 313: rhosts_files[rhosts_file_index]);
! 314: continue;
! 315: }
! 316:
! 317: /* Check if authentication is permitted by the file. */
! 318: if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name))
! 319: {
! 320: packet_send_debug("Accepted by %.100s.",
! 321: rhosts_files[rhosts_file_index]);
! 322: /* Restore the privileged uid. */
! 323: restore_uid();
! 324: return 1;
! 325: }
! 326: }
! 327:
! 328: /* Rhosts authentication denied. */
! 329: /* Restore the privileged uid. */
! 330: restore_uid();
! 331: return 0;
! 332: }