[BACK]Return to auth-rhosts.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/auth-rhosts.c, Revision 1.3

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"
1.3     ! deraadt    19: RCSID("$Id: auth-rhosts.c,v 1.2 1999/09/28 04:45:35 provos Exp $");
1.1       deraadt    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. */
1.3     ! deraadt    89:          strlcpy(userbuf, server_user, sizeof(userbuf));
1.1       deraadt    90:          break;
                     91:        case 2:
                     92:          /* Got both host and user name. */
                     93:          break;
                     94:        case 3:
                     95:          packet_send_debug("Found garbage in %.100s.", filename);
                     96:          continue; /* Extra garbage */
                     97:        default:
                     98:          continue; /* Weird... */
                     99:        }
                    100:
                    101:       host = hostbuf;
                    102:       user = userbuf;
                    103:       negated = 0;
                    104:
                    105:       /* Process negated host names, or positive netgroups. */
                    106:       if (host[0] == '-')
                    107:        {
                    108:          negated = 1;
                    109:          host++;
                    110:        }
                    111:       else
                    112:        if (host[0] == '+')
                    113:          host++;
                    114:
                    115:       if (user[0] == '-')
                    116:        {
                    117:          negated = 1;
                    118:          user++;
                    119:        }
                    120:       else
                    121:        if (user[0] == '+')
                    122:          user++;
                    123:
                    124:       /* Check for empty host/user names (particularly '+'). */
                    125:       if (!host[0] || !user[0])
                    126:        {
                    127:          /* We come here if either was '+' or '-'. */
                    128:          packet_send_debug("Ignoring wild host/user names in %.100s.",
                    129:                            filename);
                    130:          continue;
                    131:        }
                    132:
                    133: #ifdef HAVE_INNETGR
                    134:
                    135:       /* Verify that host name matches. */
                    136:       if (host[0] == '@')
                    137:        {
                    138:          if (!innetgr(host + 1, hostname, NULL, NULL) &&
                    139:              !innetgr(host + 1, ipaddr, NULL, NULL))
                    140:            continue;
                    141:        }
                    142:       else
                    143:        if (!casefold_equal(host, hostname) && strcmp(host, ipaddr) != 0)
                    144:          continue; /* Different hostname. */
                    145:
                    146:       /* Verify that user name matches. */
                    147:       if (user[0] == '@')
                    148:        {
                    149:          if (!innetgr(user + 1, NULL, client_user, NULL))
                    150:            continue;
                    151:        }
                    152:       else
                    153:        if (strcmp(user, client_user) != 0)
                    154:          continue; /* Different username. */
                    155:
                    156: #else /* HAVE_INNETGR */
                    157:
                    158:       if (!casefold_equal(host, hostname) && strcmp(host, ipaddr) != 0)
                    159:        continue; /* Different hostname. */
                    160:
                    161:       if (strcmp(user, client_user) != 0)
                    162:        continue; /* Different username. */
                    163:
                    164: #endif /* HAVE_INNETGR */
                    165:
                    166:       /* Found the user and host. */
                    167:       fclose(f);
                    168:
                    169:       /* If the entry was negated, deny access. */
                    170:       if (negated)
                    171:        {
                    172:          packet_send_debug("Matched negative entry in %.100s.",
                    173:                            filename);
                    174:          return 0;
                    175:        }
                    176:
                    177:       /* Accept authentication. */
                    178:       return 1;
                    179:     }
                    180:
                    181:   /* Authentication using this file denied. */
                    182:   fclose(f);
                    183:   return 0;
                    184: }
                    185:
                    186: /* Tries to authenticate the user using the .shosts or .rhosts file.
                    187:    Returns true if authentication succeeds.  If ignore_rhosts is
                    188:    true, only /etc/hosts.equiv will be considered (.rhosts and .shosts
                    189:    are ignored). */
                    190:
                    191: int auth_rhosts(struct passwd *pw, const char *client_user,
                    192:                int ignore_rhosts, int strict_modes)
                    193: {
                    194:   char buf[1024];
                    195:   const char *hostname, *ipaddr;
                    196:   int port;
                    197:   struct stat st;
                    198:   static const char *rhosts_files[] = { ".shosts", ".rhosts", NULL };
                    199:   unsigned int rhosts_file_index;
                    200:
                    201:   /* Quick check: if the user has no .shosts or .rhosts files, return failure
                    202:      immediately without doing costly lookups from name servers. */
                    203:   /* Switch to the user's uid. */
                    204:   temporarily_use_uid(pw->pw_uid);
                    205:   for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
                    206:        rhosts_file_index++)
                    207:     {
                    208:       /* Check users .rhosts or .shosts. */
1.3     ! deraadt   209:       snprintf(buf, sizeof buf, "%.500s/%.100s",
1.1       deraadt   210:              pw->pw_dir, rhosts_files[rhosts_file_index]);
                    211:       if (stat(buf, &st) >= 0)
                    212:        break;
                    213:     }
                    214:   /* Switch back to privileged uid. */
                    215:   restore_uid();
                    216:
                    217:   if (!rhosts_files[rhosts_file_index] && stat("/etc/hosts.equiv", &st) < 0 &&
                    218:       stat(SSH_HOSTS_EQUIV, &st) < 0)
                    219:     return 0; /* The user has no .shosts or .rhosts file and there are no
                    220:                 system-wide files. */
                    221:
                    222:   /* Get the name, address, and port of the remote host.  */
                    223:   hostname = get_canonical_hostname();
                    224:   ipaddr = get_remote_ipaddr();
                    225:   port = get_remote_port();
                    226:
                    227:   /* Check that the connection comes from a privileged port.
                    228:      Rhosts authentication only makes sense for priviledged programs.
                    229:      Of course, if the intruder has root access on his local machine,
                    230:      he can connect from any port.  So do not use .rhosts
                    231:      authentication from machines that you do not trust. */
                    232:   if (port >= IPPORT_RESERVED ||
                    233:       port < IPPORT_RESERVED / 2)
                    234:     {
                    235:       log("Connection from %.100s from nonpriviledged port %d",
                    236:          hostname, port);
                    237:       packet_send_debug("Your ssh client is not running as root.");
                    238:       return 0;
                    239:     }
                    240:
                    241:   /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */
                    242:   if (pw->pw_uid != 0)
                    243:     {
                    244:       if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user,
                    245:                            pw->pw_name))
                    246:        {
                    247:          packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.",
                    248:                            hostname, ipaddr);
                    249:          return 1;
                    250:        }
                    251:       if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user,
                    252:                            pw->pw_name))
                    253:        {
                    254:          packet_send_debug("Accepted for %.100s [%.100s] by %.100s.",
                    255:                            hostname, ipaddr, SSH_HOSTS_EQUIV);
                    256:          return 1;
                    257:        }
                    258:     }
                    259:
                    260:   /* Check that the home directory is owned by root or the user, and is not
                    261:      group or world writable. */
                    262:   if (stat(pw->pw_dir, &st) < 0)
                    263:     {
                    264:       log("Rhosts authentication refused for %.100: no home directory %.200s",
                    265:          pw->pw_name, pw->pw_dir);
                    266:       packet_send_debug("Rhosts authentication refused for %.100: no home directory %.200s",
                    267:                        pw->pw_name, pw->pw_dir);
                    268:       return 0;
                    269:     }
                    270:   if (strict_modes &&
                    271:       ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
                    272:        (st.st_mode & 022) != 0))
                    273:     {
                    274:       log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.",
                    275:          pw->pw_name);
                    276:       packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.",
                    277:                        pw->pw_name);
                    278:       return 0;
                    279:     }
                    280:
                    281:   /* Check all .rhosts files (currently .shosts and .rhosts). */
                    282:   /* Temporarily use the user's uid. */
                    283:   temporarily_use_uid(pw->pw_uid);
                    284:   for (rhosts_file_index = 0; rhosts_files[rhosts_file_index];
                    285:        rhosts_file_index++)
                    286:     {
                    287:       /* Check users .rhosts or .shosts. */
1.3     ! deraadt   288:       snprintf(buf, sizeof buf, "%.500s/%.100s",
1.1       deraadt   289:              pw->pw_dir, rhosts_files[rhosts_file_index]);
                    290:       if (stat(buf, &st) < 0)
                    291:        continue; /* No such file. */
                    292:
                    293:       /* Make sure that the file is either owned by the user or by root,
                    294:         and make sure it is not writable by anyone but the owner.  This is
                    295:         to help avoid novices accidentally allowing access to their account
                    296:         by anyone. */
                    297:       if (strict_modes &&
                    298:          ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
                    299:           (st.st_mode & 022) != 0))
                    300:        {
                    301:          log("Rhosts authentication refused for %.100s: bad modes for %.200s",
                    302:              pw->pw_name, buf);
                    303:          packet_send_debug("Bad file modes for %.200s", buf);
                    304:          continue;
                    305:        }
                    306:
                    307:       /* Check if we have been configured to ignore .rhosts and .shosts
                    308:         files. */
                    309:       if (ignore_rhosts)
                    310:        {
                    311:          packet_send_debug("Server has been configured to ignore %.100s.",
                    312:                            rhosts_files[rhosts_file_index]);
                    313:          continue;
                    314:        }
                    315:
                    316:       /* Check if authentication is permitted by the file. */
                    317:       if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name))
                    318:        {
                    319:          packet_send_debug("Accepted by %.100s.",
                    320:                            rhosts_files[rhosts_file_index]);
                    321:          /* Restore the privileged uid. */
                    322:          restore_uid();
                    323:          return 1;
                    324:        }
                    325:     }
                    326:
                    327:   /* Rhosts authentication denied. */
                    328:   /* Restore the privileged uid. */
                    329:   restore_uid();
                    330:   return 0;
                    331: }