[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.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: }