1 /*
   2  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  * 1. Redistributions of source code must retain the above copyright
   8  *    notice, this list of conditions and the following disclaimer.
   9  * 2. Redistributions in binary form must reproduce the above copyright
  10  *    notice, this list of conditions and the following disclaimer in the
  11  *    documentation and/or other materials provided with the distribution.
  12  *
  13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23  */
  24 /*
  25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  26  * Use is subject to license terms.
  27  */
  28 
  29 #include "includes.h"
  30 RCSID("$OpenBSD: auth.c,v 1.45 2002/09/20 18:41:29 stevesk Exp $");
  31 
  32 #ifdef HAVE_LOGIN_H
  33 #include <login.h>
  34 #endif
  35 #if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW)
  36 #include <shadow.h>
  37 #endif /* defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) */
  38 
  39 #ifdef HAVE_LIBGEN_H
  40 #include <libgen.h>
  41 #endif
  42 
  43 #include "xmalloc.h"
  44 #include "match.h"
  45 #include "groupaccess.h"
  46 #include "log.h"
  47 #include "buffer.h"
  48 #include "servconf.h"
  49 #include "auth.h"
  50 #include "auth-options.h"
  51 #include "canohost.h"
  52 #include "bufaux.h"
  53 #include "uidswap.h"
  54 #include "tildexpand.h"
  55 #include "misc.h"
  56 #include "bufaux.h"
  57 #include "packet.h"
  58 #include "channels.h"
  59 #include "session.h"
  60 
  61 #ifdef HAVE_BSM
  62 #include "bsmaudit.h"
  63 #include <bsm/adt.h>
  64 #endif /* HAVE_BSM */
  65 
  66 /* import */
  67 extern ServerOptions options;
  68 
  69 /* Debugging messages */
  70 Buffer auth_debug;
  71 int auth_debug_init;
  72 
  73 /*
  74  * Check if the user is allowed to log in via ssh. If user is listed
  75  * in DenyUsers or one of user's groups is listed in DenyGroups, false
  76  * will be returned. If AllowUsers isn't empty and user isn't listed
  77  * there, or if AllowGroups isn't empty and one of user's groups isn't
  78  * listed there, false will be returned.
  79  * If the user's shell is not executable, false will be returned.
  80  * Otherwise true is returned.
  81  */
  82 int
  83 allowed_user(struct passwd * pw)
  84 {
  85         struct stat st;
  86         const char *hostname = NULL, *ipaddr = NULL;
  87         char *shell;
  88         int i;
  89 #ifdef WITH_AIXAUTHENTICATE
  90         char *loginmsg;
  91 #endif /* WITH_AIXAUTHENTICATE */
  92 #if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \
  93         !defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
  94         struct spwd *spw;
  95 
  96         /* Shouldn't be called if pw is NULL, but better safe than sorry... */
  97         if (!pw || !pw->pw_name)
  98                 return 0;
  99 
 100 #define DAY             (24L * 60 * 60) /* 1 day in seconds */
 101         spw = getspnam(pw->pw_name);
 102         if (spw != NULL) {
 103                 time_t today = time(NULL) / DAY;
 104                 debug3("allowed_user: today %d sp_expire %d sp_lstchg %d"
 105                     " sp_max %d", (int)today, (int)spw->sp_expire,
 106                     (int)spw->sp_lstchg, (int)spw->sp_max);
 107 
 108                 /*
 109                  * We assume account and password expiration occurs the
 110                  * day after the day specified.
 111                  */
 112                 if (spw->sp_expire != -1 && today > spw->sp_expire) {
 113                         log("Account %.100s has expired", pw->pw_name);
 114                         return 0;
 115                 }
 116 
 117                 if (spw->sp_lstchg == 0) {
 118                         log("User %.100s password has expired (root forced)",
 119                             pw->pw_name);
 120                         return 0;
 121                 }
 122 
 123                 if (spw->sp_max != -1 &&
 124                     today > spw->sp_lstchg + spw->sp_max) {
 125                         log("User %.100s password has expired (password aged)",
 126                             pw->pw_name);
 127                         return 0;
 128                 }
 129         }
 130 #else
 131         /* Shouldn't be called if pw is NULL, but better safe than sorry... */
 132         if (!pw || !pw->pw_name)
 133                 return 0;
 134 #endif
 135 
 136         /*
 137          * Get the shell from the password data.  An empty shell field is
 138          * legal, and means /bin/sh.
 139          */
 140         shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
 141 
 142         /* deny if shell does not exists or is not executable */
 143         if (stat(shell, &st) != 0) {
 144                 log("User %.100s not allowed because shell %.100s does not exist",
 145                     pw->pw_name, shell);
 146                 return 0;
 147         }
 148         if (S_ISREG(st.st_mode) == 0 ||
 149             (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) {
 150                 log("User %.100s not allowed because shell %.100s is not executable",
 151                     pw->pw_name, shell);
 152                 return 0;
 153         }
 154 
 155         if (options.num_deny_users > 0 || options.num_allow_users > 0) {
 156                 hostname = get_canonical_hostname(options.verify_reverse_mapping);
 157                 ipaddr = get_remote_ipaddr();
 158         }
 159 
 160         /* Return false if user is listed in DenyUsers */
 161         if (options.num_deny_users > 0) {
 162                 for (i = 0; i < options.num_deny_users; i++)
 163                         if (match_user(pw->pw_name, hostname, ipaddr,
 164                             options.deny_users[i])) {
 165                                 log("User %.100s not allowed because listed in DenyUsers",
 166                                     pw->pw_name);
 167                                 return 0;
 168                         }
 169         }
 170         /* Return false if AllowUsers isn't empty and user isn't listed there */
 171         if (options.num_allow_users > 0) {
 172                 for (i = 0; i < options.num_allow_users; i++)
 173                         if (match_user(pw->pw_name, hostname, ipaddr,
 174                             options.allow_users[i]))
 175                                 break;
 176                 /* i < options.num_allow_users iff we break for loop */
 177                 if (i >= options.num_allow_users) {
 178                         log("User %.100s not allowed because not listed in AllowUsers",
 179                             pw->pw_name);
 180                         return 0;
 181                 }
 182         }
 183         if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
 184                 /* Get the user's group access list (primary and supplementary) */
 185                 if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
 186                         log("User %.100s not allowed because not in any group",
 187                             pw->pw_name);
 188                         return 0;
 189                 }
 190 
 191                 /* Return false if one of user's groups is listed in DenyGroups */
 192                 if (options.num_deny_groups > 0)
 193                         if (ga_match(options.deny_groups,
 194                             options.num_deny_groups)) {
 195                                 ga_free();
 196                                 log("User %.100s not allowed because a group is listed in DenyGroups",
 197                                     pw->pw_name);
 198                                 return 0;
 199                         }
 200                 /*
 201                  * Return false if AllowGroups isn't empty and one of user's groups
 202                  * isn't listed there
 203                  */
 204                 if (options.num_allow_groups > 0)
 205                         if (!ga_match(options.allow_groups,
 206                             options.num_allow_groups)) {
 207                                 ga_free();
 208                                 log("User %.100s not allowed because none of user's groups are listed in AllowGroups",
 209                                     pw->pw_name);
 210                                 return 0;
 211                         }
 212                 ga_free();
 213         }
 214 
 215 #ifdef WITH_AIXAUTHENTICATE
 216         if (loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &loginmsg) != 0) {
 217                 if (loginmsg && *loginmsg) {
 218                         /* Remove embedded newlines (if any) */
 219                         char *p;
 220                         for (p = loginmsg; *p; p++) {
 221                                 if (*p == '\n')
 222                                         *p = ' ';
 223                         }
 224                         /* Remove trailing newline */
 225                         *--p = '\0';
 226                         log("Login restricted for %s: %.100s", pw->pw_name, loginmsg);
 227                 }
 228                 return 0;
 229         }
 230 #endif /* WITH_AIXAUTHENTICATE */
 231 
 232         /* We found no reason not to let this user try to log on... */
 233         return 1;
 234 }
 235 
 236 Authctxt *
 237 authctxt_new(void)
 238 {
 239         Authctxt *authctxt = xmalloc(sizeof(*authctxt));
 240         memset(authctxt, 0, sizeof(*authctxt));
 241         return authctxt;
 242 }
 243 
 244 void
 245 auth_log(Authctxt *authctxt, int authenticated, char *method, char *info)
 246 {
 247         void (*authlog) (const char *fmt,...) = verbose;
 248         char *authmsg, *user_str;
 249 
 250         if (authctxt == NULL)
 251                 fatal("%s: INTERNAL ERROR", __func__);
 252 
 253         /* Raise logging level */
 254         if (authenticated == 1 || !authctxt->valid)
 255                 authlog = log;
 256         else if (authctxt->failures >= AUTH_FAIL_LOG ||
 257             authctxt->attempt >= options.max_auth_tries_log ||
 258             authctxt->init_attempt >= options.max_init_auth_tries_log)
 259                 authlog = notice;
 260 
 261         if (authctxt->method) {
 262                 authmsg = "Failed";
 263                 if (authctxt->method->postponed)
 264                         authmsg = "Postponed"; /* shouldn't happen */
 265                 if (authctxt->method->abandoned)
 266                         authmsg = "Abandoned";
 267                 if (authctxt->method->authenticated) {
 268                         if (userauth_check_partial_failure(authctxt))
 269                                 authmsg = "Partially accepted";
 270                         else
 271                                 authmsg = "Accepted";
 272                 }
 273                 else
 274                         authmsg = "Failed";
 275         }
 276         else {
 277                 authmsg = authenticated ? "Accepted" : "Failed";
 278         }
 279 
 280         if (authctxt->user == NULL || *authctxt->user == '\0')
 281                 user_str = "<implicit>";
 282         else if (!authctxt->valid)
 283                 user_str =  "<invalid username>";
 284         else
 285                 user_str =  authctxt->user;
 286 
 287         authlog("%s %s for %s from %.200s port %d%s",
 288             authmsg,
 289             (method != NULL) ? method : "<unknown authentication method>",
 290             user_str,
 291             get_remote_ipaddr(),
 292             get_remote_port(),
 293             info);
 294 
 295 #ifdef WITH_AIXAUTHENTICATE
 296         if (authenticated == 0 && strcmp(method, "password") == 0)
 297             loginfailed(authctxt->user,
 298                 get_canonical_hostname(options.verify_reverse_mapping),
 299                 "ssh");
 300 #endif /* WITH_AIXAUTHENTICATE */
 301 
 302 }
 303 
 304 #ifdef HAVE_BSM
 305 void
 306 audit_failed_login_cleanup(void *ctxt)
 307 {
 308         Authctxt *authctxt = (Authctxt *)ctxt;
 309         adt_session_data_t *ah;
 310 
 311         /*
 312          * This table lists the different variable combinations evaluated and
 313          * what the resulting PAM return value is.  As the table shows
 314          * authctxt and authctxt->valid need to be checked before either of
 315          * the authctxt->pam* variables.
 316          *
 317          *           authctxt->                     authctxt->     
 318          * authctxt    valid      authctxt->pam       pam_retval   PAM rval
 319          * --------  ----------   -------------     ------------   --------
 320          *   NULL      ANY             ANY              ANY        PAM_ABORT
 321          *    OK      zero (0)         ANY              ANY     PAM_USER_UNKNOWN
 322          *    OK       one (1)         NULL         PAM_SUCCESS  PAM_PERM_DENIED
 323          *    OK       one (1)         NULL        !PAM_SUCCESS   authctxt->
 324          *                                                          pam_retval
 325          *    OK       one (1)         VALID            ANY       authctxt->
 326          *                                                        pam_retval (+)
 327          * (+) If not set then default to PAM_PERM_DENIED
 328          */
 329 
 330         if (authctxt == NULL) {
 331                 /* Internal error */
 332                 audit_sshd_login_failure(&ah, PAM_ABORT, NULL);
 333                 return;
 334         }
 335 
 336         if (authctxt->valid == 0) {
 337                 audit_sshd_login_failure(&ah, PAM_USER_UNKNOWN, NULL);
 338         } else if (authctxt->pam == NULL) {
 339                 if (authctxt->pam_retval == PAM_SUCCESS) {
 340                         audit_sshd_login_failure(&ah, PAM_PERM_DENIED,
 341                             authctxt->user);
 342                 } else {
 343                         audit_sshd_login_failure(&ah, authctxt->pam_retval,
 344                             authctxt->user);
 345                 }
 346         } else {
 347                 audit_sshd_login_failure(&ah, AUTHPAM_ERROR(authctxt,
 348                     PAM_PERM_DENIED), authctxt->user);
 349         }
 350 }
 351 #endif /* HAVE_BSM */
 352 
 353 /*
 354  * Check whether root logins are disallowed.
 355  */
 356 int
 357 auth_root_allowed(char *method)
 358 {
 359         switch (options.permit_root_login) {
 360         case PERMIT_YES:
 361                 return 1;
 362                 break;
 363         case PERMIT_NO_PASSWD:
 364                 if (strcmp(method, "password") != 0 &&
 365                     strcmp(method, "keyboard-interactive") != 0)
 366                         return 1;
 367                 break;
 368         case PERMIT_FORCED_ONLY:
 369                 if (forced_command) {
 370                         log("Root login accepted for forced command.");
 371                         return 1;
 372                 }
 373                 break;
 374         }
 375         log("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr());
 376         return 0;
 377 }
 378 
 379 
 380 /*
 381  * Given a template and a passwd structure, build a filename
 382  * by substituting % tokenised options. Currently, %% becomes '%',
 383  * %h becomes the home directory and %u the username.
 384  *
 385  * This returns a buffer allocated by xmalloc.
 386  */
 387 char *
 388 expand_filename(const char *filename, struct passwd *pw)
 389 {
 390         Buffer buffer;
 391         char *file;
 392         const char *cp;
 393 
 394         if (pw == 0)
 395                 return NULL; /* shouldn't happen */
 396         /*
 397          * Build the filename string in the buffer by making the appropriate
 398          * substitutions to the given file name.
 399          */
 400         buffer_init(&buffer);
 401         for (cp = filename; *cp; cp++) {
 402                 if (cp[0] == '%' && cp[1] == '%') {
 403                         buffer_append(&buffer, "%", 1);
 404                         cp++;
 405                         continue;
 406                 }
 407                 if (cp[0] == '%' && cp[1] == 'h') {
 408                         buffer_append(&buffer, pw->pw_dir, strlen(pw->pw_dir));
 409                         cp++;
 410                         continue;
 411                 }
 412                 if (cp[0] == '%' && cp[1] == 'u') {
 413                         buffer_append(&buffer, pw->pw_name,
 414                             strlen(pw->pw_name));
 415                         cp++;
 416                         continue;
 417                 }
 418                 buffer_append(&buffer, cp, 1);
 419         }
 420         buffer_append(&buffer, "\0", 1);
 421 
 422         /*
 423          * Ensure that filename starts anchored. If not, be backward
 424          * compatible and prepend the '%h/'
 425          */
 426         file = xmalloc(MAXPATHLEN);
 427         cp = buffer_ptr(&buffer);
 428         if (*cp != '/')
 429                 snprintf(file, MAXPATHLEN, "%s/%s", pw->pw_dir, cp);
 430         else
 431                 strlcpy(file, cp, MAXPATHLEN);
 432 
 433         buffer_free(&buffer);
 434         return file;
 435 }
 436 
 437 char *
 438 authorized_keys_file(struct passwd *pw)
 439 {
 440         return expand_filename(options.authorized_keys_file, pw);
 441 }
 442 
 443 char *
 444 authorized_keys_file2(struct passwd *pw)
 445 {
 446         return expand_filename(options.authorized_keys_file2, pw);
 447 }
 448 
 449 /* return ok if key exists in sysfile or userfile */
 450 HostStatus
 451 check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
 452     const char *sysfile, const char *userfile)
 453 {
 454         Key *found;
 455         char *user_hostfile;
 456         struct stat st;
 457         HostStatus host_status;
 458 
 459         /* Check if we know the host and its host key. */
 460         found = key_new(key->type);
 461         host_status = check_host_in_hostfile(sysfile, host, key, found, NULL);
 462 
 463         if (host_status != HOST_OK && userfile != NULL) {
 464                 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
 465                 if (options.strict_modes &&
 466                     (stat(user_hostfile, &st) == 0) &&
 467                     ((st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
 468                     (st.st_mode & 022) != 0)) {
 469                         log("Authentication refused for %.100s: "
 470                             "bad owner or modes for %.200s",
 471                             pw->pw_name, user_hostfile);
 472                 } else {
 473                         temporarily_use_uid(pw);
 474                         host_status = check_host_in_hostfile(user_hostfile,
 475                             host, key, found, NULL);
 476                         restore_uid();
 477                 }
 478                 xfree(user_hostfile);
 479         }
 480         key_free(found);
 481 
 482         debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ?
 483             "ok" : "not found", host);
 484         return host_status;
 485 }
 486 
 487 
 488 /*
 489  * Check a given file for security. This is defined as all components
 490  * of the path to the file must be owned by either the owner of
 491  * of the file or root and no directories must be group or world writable.
 492  *
 493  * XXX Should any specific check be done for sym links ?
 494  *
 495  * Takes an open file descriptor, the file name, a uid and and
 496  * error buffer plus max size as arguments.
 497  *
 498  * Returns 0 on success and -1 on failure
 499  */
 500 int
 501 secure_filename(FILE *f, const char *file, struct passwd *pw,
 502     char *err, size_t errlen)
 503 {
 504         uid_t uid;
 505         char buf[MAXPATHLEN], homedir[MAXPATHLEN];
 506         char *cp;
 507         int comparehome = 0;
 508         struct stat st;
 509 
 510         if (pw == NULL)
 511                 return 0;
 512 
 513         uid = pw->pw_uid;
 514 
 515         if (realpath(file, buf) == NULL) {
 516                 snprintf(err, errlen, "realpath %s failed: %s", file,
 517                     strerror(errno));
 518                 return -1;
 519         }
 520 
 521         /*
 522          * A user is not required to have all the files that are subject to
 523          * the strict mode checking in his/her home directory. If the
 524          * directory is not present at the moment, which might be the case if
 525          * the directory is not mounted until the user is authenticated, do
 526          * not perform the home directory check below.
 527          */
 528         if (realpath(pw->pw_dir, homedir) != NULL)
 529                 comparehome = 1;
 530 
 531         /* check the open file to avoid races */
 532         if (fstat(fileno(f), &st) < 0 ||
 533             (st.st_uid != 0 && st.st_uid != uid) ||
 534             (st.st_mode & 022) != 0) {
 535                 snprintf(err, errlen, "bad ownership or modes for file %s",
 536                     buf);
 537                 return -1;
 538         }
 539 
 540         /* for each component of the canonical path, walking upwards */
 541         for (;;) {
 542                 if ((cp = dirname(buf)) == NULL) {
 543                         snprintf(err, errlen, "dirname() failed");
 544                         return -1;
 545                 }
 546                 strlcpy(buf, cp, sizeof(buf));
 547 
 548                 debug3("secure_filename: checking '%s'", buf);
 549                 if (stat(buf, &st) < 0 ||
 550                     (st.st_uid != 0 && st.st_uid != uid) ||
 551                     (st.st_mode & 022) != 0) {
 552                         snprintf(err, errlen,
 553                             "bad ownership or modes for directory %s", buf);
 554                         return -1;
 555                 }
 556 
 557                 /* If we passed the homedir then we can stop. */
 558                 if (comparehome && strcmp(homedir, buf) == 0) {
 559                         debug3("secure_filename: terminating check at '%s'",
 560                             buf);
 561                         break;
 562                 }
 563                 /*
 564                  * dirname should always complete with a "/" path,
 565                  * but we can be paranoid and check for "." too
 566                  */
 567                 if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
 568                         break;
 569         }
 570         return 0;
 571 }
 572 
 573 struct passwd *
 574 getpwnamallow(const char *user)
 575 {
 576 #ifdef HAVE_LOGIN_CAP
 577         extern login_cap_t *lc;
 578 #ifdef BSD_AUTH
 579         auth_session_t *as;
 580 #endif
 581 #endif
 582         struct passwd *pw;
 583 
 584         if (user == NULL || *user == '\0')
 585                 return (NULL); /* implicit user, will be set later */
 586 
 587         parse_server_match_config(&options, user,
 588             get_canonical_hostname(options.verify_reverse_mapping), get_remote_ipaddr());
 589 
 590         pw = getpwnam(user);
 591         if (pw == NULL) {
 592                 log("Illegal user %.100s from %.100s",
 593                     user, get_remote_ipaddr());
 594                 return (NULL);
 595         }
 596         if (!allowed_user(pw))
 597                 return (NULL);
 598 #ifdef HAVE_LOGIN_CAP
 599         if ((lc = login_getclass(pw->pw_class)) == NULL) {
 600                 debug("unable to get login class: %s", user);
 601                 return (NULL);
 602         }
 603 #ifdef BSD_AUTH
 604         if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 ||
 605             auth_approval(as, lc, pw->pw_name, "ssh") <= 0) {
 606                 debug("Approval failure for %s", user);
 607                 pw = NULL;
 608         }
 609         if (as != NULL)
 610                 auth_close(as);
 611 #endif
 612 #endif
 613         if (pw != NULL)
 614                 return (pwcopy(pw));
 615         return (NULL);
 616 }
 617 
 618 
 619 /*
 620  * The fatal_cleanup method to kill the hook. Since hook has been put into
 621  * new process group all descendants will be killed as well.
 622  */
 623 static void
 624 kill_hook(void *arg)
 625 {
 626         pid_t pid;
 627 
 628         pid = *(pid_t*)arg;
 629         debug("killing hook and all it's children, process group: %ld", pid);
 630         xfree(arg);
 631         (void)killpg(pid, SIGTERM);
 632 }
 633 
 634 /*
 635  * Runs the PreUserauthHook.
 636  * Returns -1 on execution error or the exit code of the hook if execution is
 637  * successful.
 638  */
 639 int
 640 run_auth_hook(const char *path, const char *user, const char *method)
 641 {
 642         struct stat st;
 643         int i, status, ret = 1;
 644         u_int envsize, argsize;
 645         char buf[256];
 646         char **env, **args;
 647         pid_t pid, *ppid;
 648 
 649         if (path == NULL || user == NULL || method == NULL) {
 650                 return (-1);
 651         }
 652 
 653         /* Initialize the environment/arguments for the hook. */
 654         envsize = 4; /* 3 env vars + EndOfList marker */
 655         argsize = 4; /* 2 args + exe name + EndOfList marker */
 656         env = xmalloc(envsize * sizeof (char *));
 657         args = xmalloc(argsize * sizeof (char *));
 658         env[0] = NULL;
 659 
 660         /* we use the SSH env handling scheme */
 661         child_set_env_silent(&env, &envsize, "PATH", "/usr/bin:/bin");
 662         child_set_env_silent(&env, &envsize, "IFS", " \t\n");
 663 
 664         (void) snprintf(buf, sizeof (buf), "%.50s %d %.50s %d",
 665             get_remote_ipaddr(), get_remote_port(),
 666             get_local_ipaddr(packet_get_connection_in()), get_local_port());
 667         child_set_env_silent(&env, &envsize, "SSH_CONNECTION", buf);
 668 
 669         args[0] = xstrdup(path);
 670         args[1] = xstrdup(method);
 671         args[2] = xstrdup(user);
 672         args[3] = NULL;
 673 
 674         /*
 675          * sanity checks
 676          * note: the checks do not make sure that the file checked is actually
 677          * the same which is executed. However, in this case it shouldn't be a
 678          * major issue since the hook is rather static and the worst case would
 679          * be an uncorrect message in the log or a hook is run even though the
 680          * permissions are not right.
 681          */
 682 
 683         /* check if script does exist */
 684         if (stat(path, &st) < 0) {
 685                 log("Error executing PreUserauthHook \"%s\": %s", path,
 686                     strerror(errno));
 687                 goto cleanup;
 688         }
 689 
 690         /* Check correct permissions for script (uid of SSHD, mode 500) */
 691         if (st.st_uid != getuid() || ((st.st_mode & 0777) != 0500)) {
 692                 log("PreUserauthHook has invalid permissions (should be 500, is"
 693                     " %o) or ownership (should be %d, is %d)",
 694                 (uint) st.st_mode & 0777, getuid(), st.st_uid);
 695                 goto cleanup;
 696         }
 697 
 698         if ((pid = fork()) == 0) {
 699                 /* 
 700                  * We put the hook and all its (possible) descendants into
 701                  * a new process group so that in case of a hanging hook
 702                  * we can wipe out the whole "family".
 703                  */
 704                 if (setpgid(0, 0) != 0) {
 705                         log("setpgid: %s", strerror(errno));
 706                         _exit(255);
 707                 }
 708                 (void) execve(path, args, env);
 709                 /* child is gone so we shouldn't get here */
 710                 log("Error executing PreUserauthHook \"%s\": %s", path,
 711                     strerror(errno));
 712                 _exit(255);
 713         } else if (pid == -1) {
 714                 log("Error executing PreUserauthHook \"%s\": %s", path,
 715                     strerror(errno));
 716                 goto cleanup;
 717         }
 718 
 719         /* make preparations to kill hook if it is hanging */
 720         ppid = xmalloc(sizeof (pid_t));
 721         *ppid = pid;
 722         fatal_add_cleanup((void (*)(void *))kill_hook, (void *) ppid);
 723 
 724         if (waitpid(pid, &status, 0) == -1) {
 725                 log("Error executing PreUserauthHook \"%s\": %s", path,
 726                     strerror(errno));
 727                 goto cleanup;
 728         }
 729 
 730         ret = WEXITSTATUS(status);
 731 
 732         if (ret == 255) {
 733                 ret = -1; /* execve() failed, error msg already logged */
 734         } else if (ret != 0) {
 735                 log("PreUserauthHook \"%s\" failed with exit code %d",
 736                     path, ret);
 737         } else {
 738                 debug("PreUserauthHook \"%s\" finished successfully", path);
 739         }
 740 
 741 cleanup:
 742         for (i = 0; args[i] != NULL; i++) {
 743                 xfree(args[i]);
 744         }
 745         for (i = 0; env[i] != NULL; i++) {
 746                 xfree(env[i]);
 747         }
 748         xfree(args);
 749         xfree(env);
 750 
 751         fatal_remove_cleanup((void (*)(void *))kill_hook, (void *) ppid);
 752 
 753         return (ret);
 754 }
 755 
 756 void
 757 auth_debug_add(const char *fmt,...)
 758 {
 759         char buf[1024];
 760         va_list args;
 761 
 762         if (!auth_debug_init)
 763                 return;
 764 
 765         va_start(args, fmt);
 766         vsnprintf(buf, sizeof(buf), fmt, args);
 767         va_end(args);
 768         buffer_put_cstring(&auth_debug, buf);
 769 }
 770 
 771 void
 772 auth_debug_send(void)
 773 {
 774         char *msg;
 775 
 776         if (!auth_debug_init)
 777                 return;
 778         while (buffer_len(&auth_debug)) {
 779                 msg = buffer_get_string(&auth_debug, NULL);
 780                 packet_send_debug("%s", msg);
 781                 xfree(msg);
 782         }
 783 }
 784 
 785 void
 786 auth_debug_reset(void)
 787 {
 788         if (auth_debug_init)
 789                 buffer_clear(&auth_debug);
 790         else {
 791                 buffer_init(&auth_debug);
 792                 auth_debug_init = 1;
 793         }
 794 }