1 /*
   2  * Copyright (c) 2000 Damien Miller.  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 (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
  26  */
  27 
  28 #include "includes.h"
  29 
  30 #ifdef USE_PAM
  31 #include "xmalloc.h"
  32 #include "log.h"
  33 #include "auth.h"
  34 #include "auth-options.h"
  35 #include "auth-pam.h"
  36 #include "buffer.h"
  37 #include "servconf.h"
  38 #include "canohost.h"
  39 #include "compat.h"
  40 #include "misc.h"
  41 #include "sshlogin.h"
  42 #include "ssh-gss.h"
  43 
  44 #include <security/pam_appl.h>
  45 
  46 extern char *__progname;
  47 
  48 extern u_int utmp_len;
  49 extern ServerOptions options;
  50 
  51 extern Authmethod method_kbdint;
  52 
  53 RCSID("$Id: auth-pam.c,v 1.54 2002/07/28 20:24:08 stevesk Exp $");
  54 
  55 #define NEW_AUTHTOK_MSG \
  56         "Warning: Your password has expired, please change it now."
  57 
  58 /* PAM conversation for non-interactive userauth methods */
  59 static int do_pam_conversation(int num_msg, const struct pam_message **msg,
  60         struct pam_response **resp, void *appdata_ptr);
  61 
  62 static void do_pam_cleanup_proc(void *context);
  63 
  64 static char *get_method_name(Authctxt *authctxt);
  65 
  66 /* PAM conversation for non-interactive userauth methods */
  67 static struct pam_conv conv = {
  68         (int (*)())do_pam_conversation,
  69         NULL
  70 };
  71 char *__pam_msg = NULL;
  72 
  73 static
  74 char *
  75 get_method_name(Authctxt *authctxt)
  76 {
  77         if (!authctxt)
  78                 return "(unknown)";
  79 
  80         if (!compat20)
  81                 return (authctxt->v1_auth_name) ? authctxt->v1_auth_name :
  82                                                   "(sshv1-unknown)";
  83 
  84         if (!authctxt->method || !authctxt->method->name)
  85                         return "(sshv2-unknown)";
  86 
  87         return authctxt->method->name;
  88 }
  89 
  90 char *
  91 derive_pam_service_name(Authmethod *method)
  92 {
  93         char *svcname = xmalloc(BUFSIZ);
  94 
  95         /*
  96          * If PamServiceName is set we use that for everything, including
  97          * SSHv1
  98          */
  99         if (options.pam_service_name != NULL) {
 100                 (void) strlcpy(svcname, options.pam_service_name, BUFSIZ);
 101                 return (svcname);
 102         }
 103 
 104         if (compat20 && method) {
 105                 char *method_name = method->name;
 106 
 107                 if (!method_name)
 108                         fatal("Userauth method unknown while starting PAM");
 109 
 110                 /*
 111                  * For SSHv2 we use "sshd-<userauth name>
 112                  * The "sshd" prefix can be changed via the PAMServicePrefix
 113                  * sshd_config option.
 114                  */
 115                 if (strcmp(method_name, "none") == 0) {
 116                         snprintf(svcname, BUFSIZ, "%s-none",
 117                             options.pam_service_prefix);
 118                 }
 119                 if (strcmp(method_name, "password") == 0) {
 120                         snprintf(svcname, BUFSIZ, "%s-password",
 121                             options.pam_service_prefix);
 122                 }
 123                 if (strcmp(method_name, "keyboard-interactive") == 0) {
 124                         /* "keyboard-interactive" is too long, shorten it */
 125                         snprintf(svcname, BUFSIZ, "%s-kbdint",
 126                             options.pam_service_prefix);
 127                 }
 128                 if (strcmp(method_name, "publickey") == 0) {
 129                         /* "publickey" is too long, shorten it */
 130                         snprintf(svcname, BUFSIZ, "%s-pubkey",
 131                             options.pam_service_prefix);
 132                 }
 133                 if (strcmp(method_name, "hostbased") == 0) {
 134                         /* "hostbased" can't really be shortened... */
 135                         snprintf(svcname, BUFSIZ, "%s-hostbased",
 136                             options.pam_service_prefix);
 137                 }
 138                 if (strncmp(method_name, "gss", 3) == 0) {
 139                         /* "gss" is too short, elongate it */
 140                         snprintf(svcname, BUFSIZ, "%s-gssapi",
 141                             options.pam_service_prefix);
 142                 }
 143                 return svcname;
 144         } else {
 145                 /* SSHv1 doesn't get to be so cool */
 146                 snprintf(svcname, BUFSIZ, "%s-v1",
 147                     options.pam_service_prefix);
 148         }
 149         return svcname;
 150 }
 151 
 152 void
 153 new_start_pam(Authctxt *authctxt, struct pam_conv *conv)
 154 {
 155         int             retval;
 156         pam_handle_t    *pamh;
 157         const char      *rhost;
 158         char            *svc;
 159         char            *user = NULL;
 160         pam_stuff       *pam;
 161 
 162         if (authctxt == NULL)
 163                 fatal("Internal error during userauth");
 164 
 165         if (compat20 && authctxt->method == NULL)
 166                 fatal("Userauth method unknown while starting PAM");
 167 
 168         /* PAM service selected here */
 169         svc = derive_pam_service_name(authctxt->method);
 170         debug2("Starting PAM service %s for method %s", svc,
 171                 get_method_name(authctxt));
 172 
 173         if (authctxt->user != NULL)
 174                 user = authctxt->user;
 175 
 176         /* Cleanup previous PAM state */
 177         if (authctxt->pam != NULL) {
 178                 fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam);
 179                 do_pam_cleanup_proc(authctxt->pam);
 180         }
 181 
 182         pam = xmalloc(sizeof(pam_stuff));
 183         (void) memset(pam, 0, sizeof(pam_stuff));
 184 
 185         /*
 186          * pam->last_pam_retval has to be and is considered
 187          * along with pam->state.
 188          *
 189          * pam->state = 0; -> no PAM auth, account, etc, work
 190          * done yet.  (Set by memset() above.)
 191          *
 192          * pam->last_pam_retval = PAM_SUCCESS; -> meaningless at
 193          * this point.
 194          *
 195          * See finish_userauth_do_pam() below.
 196          */
 197         pam->authctxt = authctxt;
 198         pam->last_pam_retval = PAM_SUCCESS;
 199 
 200         authctxt->pam = pam;
 201 
 202         /* Free any previously stored text/error PAM prompts */
 203         if (__pam_msg) {
 204                 xfree(__pam_msg);
 205                 __pam_msg = NULL;
 206         }
 207 
 208         if ((retval = pam_start(svc, user, conv, &pamh)) != PAM_SUCCESS) {
 209                 fatal("PAM initialization failed during %s userauth",
 210                         get_method_name(authctxt));
 211         }
 212 
 213         free(svc);
 214 
 215         fatal_add_cleanup((void (*)(void *)) &do_pam_cleanup_proc,
 216                           (void *) authctxt->pam);
 217 
 218         rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping);
 219         if ((retval = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) {
 220                 (void) pam_end(pamh, retval);
 221                 fatal("Could not set PAM_RHOST item during %s userauth",
 222                         get_method_name(authctxt));
 223         }
 224 
 225         if ((retval = pam_set_item(pamh, PAM_TTY, "sshd")) != PAM_SUCCESS) {
 226                 (void) pam_end(pamh, retval);
 227                 fatal("Could not set PAM_TTY item during %s userauth",
 228                         get_method_name(authctxt));
 229         }
 230 
 231         if (authctxt->cuser != NULL) 
 232                 if ((retval = pam_set_item(pamh, PAM_AUSER, authctxt->cuser)) != PAM_SUCCESS) {
 233                         (void) pam_end(pamh, retval);
 234                         fatal("Could not set PAM_AUSER item during %s userauth",
 235                                 get_method_name(authctxt));
 236                 }
 237 
 238         authctxt->pam->h = pamh;
 239 }
 240 
 241 /*
 242  * To be called from userauth methods, directly (as in keyboard-interactive) or
 243  * indirectly (from auth_pam_password() or from do_pam_non_initial_userauth().
 244  *
 245  * The caller is responsible for calling new_start_pam() first.
 246  *
 247  * PAM state is not cleaned up here on error.  This is left to subsequent calls
 248  * to new_start_pam() or to the cleanup function upon authentication error.
 249  */
 250 int
 251 finish_userauth_do_pam(Authctxt *authctxt)
 252 {
 253         int retval;
 254         char *user, *method;
 255 
 256         /* Various checks; fail gracefully */
 257         if (authctxt == NULL || authctxt->pam == NULL)
 258                 return PAM_SYSTEM_ERR;  /* shouldn't happen */
 259 
 260         if (compat20) {
 261                 if (authctxt->method == NULL || authctxt->method->name == NULL)
 262                         return PAM_SYSTEM_ERR;  /* shouldn't happen */
 263                 method = authctxt->method->name;
 264         } else if ((method = authctxt->v1_auth_name) == NULL)
 265                 return PAM_SYSTEM_ERR;  /* shouldn't happen */
 266 
 267         if (AUTHPAM_DONE(authctxt))
 268                 return PAM_SYSTEM_ERR;  /* shouldn't happen */
 269 
 270         if (!(authctxt->pam->state & PAM_S_DONE_ACCT_MGMT)) {
 271                 retval = pam_acct_mgmt(authctxt->pam->h, 0);
 272                 authctxt->pam->last_pam_retval = retval;
 273                 if (retval == PAM_NEW_AUTHTOK_REQD) {
 274                         userauth_force_kbdint();
 275                         return retval;
 276                 }
 277                 if (retval != PAM_SUCCESS)
 278                         return retval;
 279                 authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
 280         }
 281 
 282         /*
 283          * Handle PAM_USER change, if any.
 284          *
 285          * We do this before pam_open_session() because we need the PAM_USER's
 286          * UID for:
 287          *
 288          * a) PermitRootLogin checking
 289          * b) to get at the lastlog entry before pam_open_session() updates it.
 290          */
 291         retval = pam_get_item(authctxt->pam->h, PAM_USER, (void **) &user);
 292         if (retval != PAM_SUCCESS) {
 293                 fatal("PAM failure: pam_get_item(PAM_USER) "
 294                       "returned %d: %.200s", retval,
 295                       PAM_STRERROR(authctxt->pam->h, retval));
 296         }
 297 
 298         if (user == NULL || *user == '\0') {
 299                 debug("PAM set NULL PAM_USER");
 300                 return PAM_PERM_DENIED;
 301         }
 302 
 303         if (strcmp(user, authctxt->user) != 0) {
 304                 log("PAM changed the SSH username");
 305                 pwfree(&authctxt->pw);
 306                 authctxt->pw = getpwnamallow(user);
 307                 authctxt->valid = (authctxt->pw != NULL);
 308                 xfree(authctxt->user);
 309                 authctxt->user = xstrdup(user);
 310         }
 311 
 312         if (!authctxt->valid) {
 313                 debug2("PAM set PAM_USER to unknown user");
 314                 /*
 315                  * Return success, userauth_finish() will catch
 316                  * this and send back a failure message.
 317                  */
 318                 return PAM_SUCCESS;
 319         }
 320 
 321         /* Check PermitRootLogin semantics */
 322         if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(method))
 323                 return PAM_PERM_DENIED;
 324 
 325         if (!(authctxt->pam->state & PAM_S_DONE_SETCRED)) {
 326                 retval = pam_setcred(authctxt->pam->h,
 327                                      PAM_ESTABLISH_CRED);
 328                 authctxt->pam->last_pam_retval = retval;
 329                 if (retval != PAM_SUCCESS)
 330                         return retval;
 331                 authctxt->pam->state |= PAM_S_DONE_SETCRED;
 332 
 333 #ifdef GSSAPI
 334                 /*
 335                  * Store GSS-API delegated creds after pam_setcred(), which may
 336                  * have set the current credential store.
 337                  */
 338                 ssh_gssapi_storecreds(NULL, authctxt);
 339 #endif /* GSSAPI */
 340         }
 341 
 342         if (!(authctxt->pam->state & PAM_S_DONE_OPEN_SESSION)) {
 343                 retval = pam_open_session(authctxt->pam->h, 0);
 344                 authctxt->pam->last_pam_retval = retval;
 345                 if (retval != PAM_SUCCESS)
 346                         return retval;
 347                 authctxt->pam->state |= PAM_S_DONE_OPEN_SESSION;
 348         }
 349 
 350         /*
 351          * All PAM work done successfully.
 352          *
 353          * PAM handle stays around so we can call pam_close_session() on
 354          * it later.
 355          */
 356         return PAM_SUCCESS;
 357 }
 358 
 359 /*
 360  * PAM conversation function for non-interactive userauth methods that
 361  * really cannot do any prompting.  Password userauth and CHANGEREQ can
 362  * always set the PAM_AUTHTOK and PAM_OLDAUTHTOK items to avoid
 363  * conversation (and if they do and nonetheless some module tries to
 364  * converse, then password userauth / CHANGEREQ MUST fail).
 365  *
 366  * Except, PAM_TEXT_INFO and PAM_ERROR_MSG prompts can be squirelled
 367  * away and shown to the user later.
 368  *
 369  * Keyboard-interactive userauth has its own much more interesting
 370  * conversation function.
 371  *
 372  */
 373 static int
 374 do_pam_conversation(int num_msg, const struct pam_message **msg,
 375         struct pam_response **resp, void *appdata_ptr)
 376 {
 377         struct pam_response *reply;
 378         int count;
 379 
 380         /* PAM will free this later */
 381         reply = xmalloc(num_msg * sizeof(*reply));
 382 
 383         (void) memset(reply, 0, num_msg * sizeof(*reply));
 384 
 385         for (count = 0; count < num_msg; count++) {
 386                 /*
 387                  * We can't use stdio yet, queue messages for 
 388                  * printing later
 389                  */
 390                 switch(PAM_MSG_MEMBER(msg, count, msg_style)) {
 391                 case PAM_PROMPT_ECHO_ON:
 392                         xfree(reply);
 393                         return PAM_CONV_ERR;
 394                 case PAM_PROMPT_ECHO_OFF:
 395                         xfree(reply);
 396                         return PAM_CONV_ERR;
 397                         break;
 398                 case PAM_ERROR_MSG:
 399                 case PAM_TEXT_INFO:
 400                         if (PAM_MSG_MEMBER(msg, count, msg) != NULL) {
 401                                 message_cat(&__pam_msg, 
 402                                     PAM_MSG_MEMBER(msg, count, msg));
 403                         }
 404                         reply[count].resp = xstrdup("");
 405                         reply[count].resp_retcode = PAM_SUCCESS;
 406                         break;
 407                 default:
 408                         xfree(reply);
 409                         return PAM_CONV_ERR;
 410                 }
 411         }
 412 
 413         *resp = reply;
 414 
 415         return PAM_SUCCESS;
 416 }
 417 
 418 /* Called at exit to cleanly shutdown PAM */
 419 static void
 420 do_pam_cleanup_proc(void *context)
 421 {
 422         int pam_retval;
 423         pam_stuff *pam = (pam_stuff *) context;
 424 
 425         if (pam == NULL)
 426                 return;
 427 
 428         if (pam->authctxt != NULL && pam->authctxt->pam == pam) {
 429                 pam->authctxt->pam_retval = pam->last_pam_retval;
 430                 pam->authctxt->pam = NULL;
 431                 pam->authctxt = NULL;
 432         }
 433 
 434         if (pam->h == NULL)
 435                 return;
 436 
 437         /*
 438          * We're in fatal_cleanup() or not in userauth or without a
 439          * channel -- can't converse now, too bad.
 440          */
 441         pam_retval = pam_set_item(pam->h, PAM_CONV, NULL);
 442         if (pam_retval != PAM_SUCCESS) {
 443                 log("Cannot remove PAM conv, close session or delete creds[%d]: %.200s",
 444                         pam_retval, PAM_STRERROR(pam->h, pam_retval));
 445                 goto cleanup;
 446         }
 447 
 448         if (pam->state & PAM_S_DONE_OPEN_SESSION) {
 449                 pam_retval = pam_close_session(pam->h, 0);
 450                 if (pam_retval != PAM_SUCCESS)
 451                         log("Cannot close PAM session[%d]: %.200s",
 452                             pam_retval, PAM_STRERROR(pam->h, pam_retval));
 453         }
 454 
 455         if (pam->state & PAM_S_DONE_SETCRED) {
 456                 pam_retval = pam_setcred(pam->h, PAM_DELETE_CRED);
 457                 if (pam_retval != PAM_SUCCESS)
 458                         debug("Cannot delete credentials[%d]: %.200s", 
 459                             pam_retval, PAM_STRERROR(pam->h, pam_retval));
 460         }
 461 
 462 cleanup:
 463 
 464         /* Use the previous PAM result, if not PAM_SUCCESS for pam_end() */
 465         if (pam->last_pam_retval != PAM_SUCCESS)
 466                 pam_retval = pam_end(pam->h, pam->last_pam_retval);
 467         else if (pam_retval != PAM_SUCCESS)
 468                 pam_retval = pam_end(pam->h, pam_retval);
 469         else
 470                 pam_retval = pam_end(pam->h, PAM_ABORT);
 471 
 472         if (pam_retval != PAM_SUCCESS)
 473                 log("Cannot release PAM authentication[%d]: %.200s",
 474                     pam_retval, PAM_STRERROR(pam->h, pam_retval));
 475 
 476         xfree(pam);
 477 }
 478 
 479 /* Attempt password authentation using PAM */
 480 int
 481 auth_pam_password(Authctxt *authctxt, const char *password)
 482 {
 483         int retval;
 484 
 485         /* Ensure we have a fresh PAM handle / state */
 486         new_start_pam(authctxt, &conv);
 487 
 488         retval = pam_set_item(authctxt->pam->h, PAM_AUTHTOK, password);
 489         if (retval != PAM_SUCCESS) {
 490                 authctxt->pam->last_pam_retval = retval;
 491                 return 1;
 492         }
 493 
 494         retval = pam_authenticate(authctxt->pam->h,
 495                         options.permit_empty_passwd ?  0 :
 496                         PAM_DISALLOW_NULL_AUTHTOK);
 497 
 498         if (retval != PAM_SUCCESS) {
 499                 authctxt->pam->last_pam_retval = retval;
 500                 return 0;
 501         }
 502 
 503         if ((retval = finish_userauth_do_pam(authctxt)) != PAM_SUCCESS)
 504                 return 0;
 505 
 506         if (authctxt->method)
 507                 authctxt->method->authenticated = 1;      /* SSHv2 */
 508 
 509         return 1;
 510 }
 511 
 512 int
 513 do_pam_non_initial_userauth(Authctxt *authctxt)
 514 {
 515         new_start_pam(authctxt, &conv);
 516         return (finish_userauth_do_pam(authctxt) == PAM_SUCCESS);
 517 }
 518 
 519 /* Cleanly shutdown PAM */
 520 void finish_pam(Authctxt *authctxt)
 521 {
 522         fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam);
 523         do_pam_cleanup_proc(authctxt->pam);
 524 }
 525 
 526 static
 527 char **
 528 find_env(char **env, char *var)
 529 {
 530         char **p;
 531         int len;
 532 
 533         if (strchr(var, '=') == NULL)
 534                 len = strlen(var);
 535         else
 536                 len = (strchr(var, '=') - var) + 1;
 537 
 538         for ( p = env ; p != NULL && *p != NULL ; p++ ) {
 539                 if (strncmp(*p, var, len) == 0)
 540                         return (p);
 541         }
 542 
 543         return (NULL);
 544 }
 545 
 546 /* Return list of PAM environment strings */
 547 char **
 548 fetch_pam_environment(Authctxt *authctxt)
 549 {
 550 #ifdef HAVE_PAM_GETENVLIST
 551         char    **penv;
 552 
 553         if (authctxt == NULL || authctxt->pam == NULL ||
 554             authctxt->pam->h == NULL)
 555                 return (NULL);
 556 
 557         penv = pam_getenvlist(authctxt->pam->h);
 558 
 559         return (penv);
 560 #else /* HAVE_PAM_GETENVLIST */
 561         return(NULL);
 562 #endif /* HAVE_PAM_GETENVLIST */
 563 }
 564 
 565 void free_pam_environment(char **env)
 566 {
 567         int i;
 568 
 569         if (env != NULL) {
 570                 for (i = 0; env[i] != NULL; i++)
 571                         xfree(env[i]);
 572         }
 573 
 574         xfree(env);
 575 }
 576 
 577 /* Print any messages that have been generated during authentication */
 578 /* or account checking to stderr */
 579 void print_pam_messages(void)
 580 {
 581         if (__pam_msg != NULL)
 582                 (void) fputs(__pam_msg, stderr);
 583 }
 584 
 585 /* Append a message to buffer */
 586 void message_cat(char **p, const char *a)
 587 {
 588         char *cp;
 589         size_t new_len;
 590 
 591         new_len = strlen(a);
 592 
 593         if (*p) {
 594                 size_t len = strlen(*p);
 595 
 596                 *p = xrealloc(*p, new_len + len + 2);
 597                 cp = *p + len;
 598         } else
 599                 *p = cp = xmalloc(new_len + 2);
 600 
 601         (void) memcpy(cp, a, new_len);
 602         cp[new_len] = '\n';
 603         cp[new_len + 1] = '\0';
 604 }
 605 
 606 #endif /* USE_PAM */