1 From f31faf48842765bb3a9a5a9c400bf4613d639e94 Mon Sep 17 00:00:00 2001
   2 From: oracle <solaris@oracle.com>
   3 Date: Mon, 3 Aug 2015 14:36:19 -0700
   4 Subject: [PATCH 10/30] PAM enhancements for Solaris
   5 
   6 #
   7 # This patch contains a couple of PAM enhancements:
   8 #   1) Each SSHv2 userauth method has its own PAM service name so that PAM can
   9 #      be used to control what userauth methods are allowed.
  10 #   2) The PAMServiceName and PAMServicePrefix options.
  11 #
  12 # We have contributed back this feature to the OpenSSH upstream community.
  13 # For more information, see https://bugzilla.mindrot.org/show_bug.cgi?id=2246
  14 # In the future, if these enhancements are accepted by the upsteam in a
  15 # later release, we will remove this patch when we upgrade to that release.
  16 #
  17 ---
  18  auth-pam.c     | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  19  auth.h         |   3 ++
  20  auth2.c        |  61 ++++++++++++++++++++++++++++-
  21  monitor.c      |  63 ++++++++++++++++++++++++++++++
  22  monitor.h      |   3 ++
  23  monitor_wrap.c |  18 +++++++++
  24  servconf.c     |  56 +++++++++++++++++++++++++++
  25  servconf.h     |  10 +++++
  26  sshd.1m        |  27 +++++++++++++
  27  sshd.c         |   5 +++
  28  sshd_config.4  |  18 ++++++++-
  29  11 files changed, 379 insertions(+), 4 deletions(-)
  30 
  31 diff --git a/auth-pam.c b/auth-pam.c
  32 index b941991..7bdee5c 100644
  33 --- a/auth-pam.c
  34 +++ b/auth-pam.c
  35 @@ -617,6 +617,72 @@ sshpam_cleanup(void)
  36         sshpam_handle = NULL;
  37  }
  38  
  39 +#ifdef PAM_ENHANCEMENT
  40 +char *
  41 +derive_pam_service_name(Authctxt *authctxt)
  42 +{
  43 +       char *svcname = xmalloc(BUFSIZ);
  44 +
  45 +       /*
  46 +        * If PamServiceName is set we use that for everything, including
  47 +        * SSHv1
  48 +        */
  49 +       if (options.pam_service_name != NULL) {
  50 +               (void) strlcpy(svcname, options.pam_service_name, BUFSIZ);
  51 +               return (svcname);
  52 +       }
  53 +
  54 +       if (compat20) {
  55 +               char *method_name = authctxt->authmethod_name;
  56 +
  57 +               if (!method_name)
  58 +                       fatal("Userauth method unknown while starting PAM");
  59 +
  60 +               /*
  61 +                * For SSHv2 we use "sshd-<userauth name>
  62 +                * The "sshd" prefix can be changed via the PAMServicePrefix
  63 +                * sshd_config option.
  64 +                */
  65 +               if (strcmp(method_name, "none") == 0) {
  66 +                       snprintf(svcname, BUFSIZ, "%s-none",
  67 +                           options.pam_service_prefix);
  68 +               }
  69 +               if (strcmp(method_name, "password") == 0) {
  70 +                       snprintf(svcname, BUFSIZ, "%s-password",
  71 +                           options.pam_service_prefix);
  72 +               }
  73 +               if (strcmp(method_name, "keyboard-interactive") == 0) {
  74 +                       /* "keyboard-interactive" is too long, shorten it */
  75 +                       snprintf(svcname, BUFSIZ, "%s-kbdint",
  76 +                           options.pam_service_prefix);
  77 +               }
  78 +               if (strcmp(method_name, "publickey") == 0) {
  79 +                       /* "publickey" is too long, shorten it */
  80 +                       snprintf(svcname, BUFSIZ, "%s-pubkey",
  81 +                           options.pam_service_prefix);
  82 +               }
  83 +               if (strcmp(method_name, "hostbased") == 0) {
  84 +                       snprintf(svcname, BUFSIZ, "%s-hostbased",
  85 +                           options.pam_service_prefix);
  86 +               }
  87 +               if (strncmp(method_name, "gssapi-", 7) == 0) {
  88 +                       /*
  89 +                        * Although OpenSSH only supports "gssapi-with-mic"
  90 +                        * for now. We will still map any userauth method
  91 +                         * prefixed with "gssapi-" to the gssapi PAM service.
  92 +                        */ 
  93 +                       snprintf(svcname, BUFSIZ, "%s-gssapi",
  94 +                           options.pam_service_prefix);
  95 +               }
  96 +               return svcname;
  97 +       } else {
  98 +               /* SSHv1 doesn't get to be so cool */
  99 +               snprintf(svcname, BUFSIZ, "sshd-v1");
 100 +       }
 101 +       return svcname;
 102 +}
 103 +#endif /* PAM_ENHANCEMENT */
 104 +
 105  static int
 106  sshpam_init(Authctxt *authctxt)
 107  {
 108 @@ -624,18 +690,71 @@ sshpam_init(Authctxt *authctxt)
 109         const char *pam_rhost, *pam_user, *user = authctxt->user;
 110         const char **ptr_pam_user = &pam_user;
 111  
 112 +#ifdef PAM_ENHANCEMENT
 113 +       const char *pam_service;
 114 +        const char **ptr_pam_service = &pam_service;
 115 +       char *svc = NULL;
 116 +
 117 +       svc = derive_pam_service_name(authctxt);
 118 +        debug3("PAM service is %s", svc);
 119 +#endif
 120 +
 121         if (sshpam_handle != NULL) {
 122 +#ifdef PAM_ENHANCEMENT
 123 +               /* get the pam service name */
 124 +               sshpam_err = pam_get_item(sshpam_handle,
 125 +                   PAM_SERVICE, (sshpam_const void **)ptr_pam_service);
 126 +                if (sshpam_err != PAM_SUCCESS) 
 127 +                   fatal("Failed to get the PAM service name");
 128 +               debug3("Previous pam_service is %s", pam_service ?
 129 +                    pam_service : "NULL");
 130 +
 131 +               /* get the pam user name */
 132 +               sshpam_err = pam_get_item(sshpam_handle,
 133 +                   PAM_USER, (sshpam_const void **)ptr_pam_user);
 134 +
 135 +               /*
 136 +                * only need to re-start if either user or service is 
 137 +                 * different.
 138 +                 */
 139 +               if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0
 140 +                   && strncmp(svc, pam_service, strlen(svc)) == 0) {
 141 +                       free(svc);
 142 +                       return (0);
 143 +                }
 144 +
 145 +               /*
 146 +                * Clean up previous PAM state.  No need to clean up session 
 147 +                * and creds.
 148 +                */
 149 +                sshpam_authenticated = 0;
 150 +                sshpam_account_status = -1;
 151 +
 152 +               sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, NULL);
 153 +               if (sshpam_err != PAM_SUCCESS)
 154 +                       debug3("Cannot remove PAM conv"); /* a warning only */
 155 +#else /* Original */
 156                 /* We already have a PAM context; check if the user matches */
 157                 sshpam_err = pam_get_item(sshpam_handle,
 158                     PAM_USER, (sshpam_const void **)ptr_pam_user);
 159                 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)
 160                         return (0);
 161 +#endif /* PAM_ENHANCEMENT */
 162                 pam_end(sshpam_handle, sshpam_err);
 163                 sshpam_handle = NULL;
 164         }
 165         debug("PAM: initializing for \"%s\"", user);
 166 +
 167 +#ifdef PAM_ENHANCEMENT
 168 +        debug3("Starting PAM service %s for user %s method %s", svc, user,
 169 +            authctxt->authmethod_name);
 170 +       sshpam_err =
 171 +           pam_start(svc, user, &store_conv, &sshpam_handle);
 172 +       free(svc);
 173 +#else /* Original */
 174         sshpam_err =
 175             pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle);
 176 +#endif
 177         sshpam_authctxt = authctxt;
 178  
 179         if (sshpam_err != PAM_SUCCESS) {
 180 diff --git a/auth.h b/auth.h
 181 index 8b27575..a0e41a4 100644
 182 --- a/auth.h
 183 +++ b/auth.h
 184 @@ -81,6 +81,9 @@ struct Authctxt {
 185  
 186         struct sshkey   **prev_userkeys;
 187         u_int            nprev_userkeys;
 188 +#ifdef PAM_ENHANCEMENT
 189 +        char            *authmethod_name;
 190 +#endif 
 191  };
 192  /*
 193   * Every authentication method has to handle authentication requests for
 194 diff --git a/auth2.c b/auth2.c
 195 index 7177962..32ba663 100644
 196 --- a/auth2.c
 197 +++ b/auth2.c
 198 @@ -243,10 +243,21 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
 199                         PRIVSEP(audit_event(SSH_INVALID_USER));
 200  #endif
 201                 }
 202 +
 203 +
 204  #ifdef USE_PAM
 205 +#ifdef PAM_ENHANCEMENT
 206 +               /*
 207 +                * Start PAM here and once only, if each userauth does not
 208 +                * has its own PAM service.
 209 +                */
 210 +               if (options.use_pam && !options.pam_service_per_authmethod)
 211 +                       PRIVSEP(start_pam(authctxt));
 212 +#else
 213                 if (options.use_pam)
 214                         PRIVSEP(start_pam(authctxt));
 215  #endif
 216 +#endif
 217                 setproctitle("%s%s", authctxt->valid ? user : "unknown",
 218                     use_privsep ? " [net]" : "");
 219                 authctxt->service = xstrdup(service);
 220 @@ -277,6 +288,18 @@ input_userauth_request(int type, u_int32_t seq, void *ctxt)
 221         /* try to authenticate user */
 222         m = authmethod_lookup(authctxt, method);
 223         if (m != NULL && authctxt->failures < options.max_authtries) {
 224 +
 225 +#if defined(USE_PAM) && defined(PAM_ENHANCEMENT)
 226 +               /* start PAM service for each userauth */
 227 +                if (options.use_pam && options.pam_service_per_authmethod) {
 228 +                               if (authctxt->authmethod_name != NULL)
 229 +                               free(authctxt->authmethod_name);
 230 +                        authctxt->authmethod_name = xstrdup(method);
 231 +                        if (use_privsep)
 232 +                                mm_inform_authmethod(method);
 233 +                       PRIVSEP(start_pam(authctxt));
 234 +               }
 235 +#endif
 236                 debug2("input_userauth_request: try method %s", method);
 237                 authenticated = m->userauth(authctxt);
 238         }
 239 @@ -295,6 +318,10 @@ userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
 240         char *methods;
 241         int partial = 0;
 242  
 243 +#ifdef  PAM_ENHANCEMENT
 244 +        debug3("%s: entering", __func__);
 245 +#endif
 246 +
 247         if (!authctxt->valid && authenticated)
 248                 fatal("INTERNAL ERROR: authenticated invalid user %s",
 249                     authctxt->user);
 250 @@ -311,6 +338,25 @@ userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
 251         }
 252  
 253         if (authenticated && options.num_auth_methods != 0) {
 254 +
 255 +#if defined(USE_PAM) && defined(PAM_ENHANCEMENT)
 256 +                /*
 257 +                 * If each userauth has its own PAM service, then PAM need to 
 258 +                 * perform account check for this service.
 259 +                 */
 260 +                if (options.use_pam && options.pam_service_per_authmethod &&
 261 +                    !PRIVSEP(do_pam_account())) {
 262 +                        /* if PAM returned a message, send it to the user */
 263 +                        if (buffer_len(&loginmsg) > 0) {
 264 +                                buffer_append(&loginmsg, "\0", 1);
 265 +                                userauth_send_banner(buffer_ptr(&loginmsg));
 266 +                                packet_write_wait();
 267 +                        }
 268 +
 269 +                        fatal("Access denied for user %s by PAM account "
 270 +                            "configuration", authctxt->user);
 271 +                }
 272 +#endif
 273                 if (!auth2_update_methods_lists(authctxt, method, submethod)) {
 274                         authenticated = 0;
 275                         partial = 1;
 276 @@ -324,7 +370,20 @@ userauth_finish(Authctxt *authctxt, int authenticated, const char *method,
 277                 return;
 278  
 279  #ifdef USE_PAM
 280 +
 281 +#ifdef PAM_ENHANCEMENT
 282 +        /*
 283 +         * PAM needs to perform account checks after auth. However, if each
 284 +         * userauth has its own PAM service and options.num_auth_methods != 0,
 285 +         * then no need to perform account checking, because it was done 
 286 +         * already.
 287 +         */
 288 +        if (options.use_pam && authenticated && 
 289 +            !(options.num_auth_methods != 0 &&
 290 +            options.pam_service_per_authmethod)){
 291 +#else
 292         if (options.use_pam && authenticated) {
 293 +#endif
 294                 if (!PRIVSEP(do_pam_account())) {
 295                         /* if PAM returned a message, send it to the user */
 296                         if (buffer_len(&loginmsg) > 0) {
 297 @@ -615,5 +674,3 @@ auth2_update_methods_lists(Authctxt *authctxt, const char *method,
 298                 fatal("%s: method not in AuthenticationMethods", __func__);
 299         return 0;
 300  }
 301 -
 302 -
 303 diff --git a/monitor.c b/monitor.c
 304 index a914209..b3efbb0 100644
 305 --- a/monitor.c
 306 +++ b/monitor.c
 307 @@ -127,6 +127,9 @@ int mm_answer_sign(int, Buffer *);
 308  int mm_answer_pwnamallow(int, Buffer *);
 309  int mm_answer_auth2_read_banner(int, Buffer *);
 310  int mm_answer_authserv(int, Buffer *);
 311 +#ifdef PAM_ENHANCEMENT
 312 +int mm_answer_authmethod(int, Buffer *);
 313 +#endif
 314  int mm_answer_authpassword(int, Buffer *);
 315  int mm_answer_bsdauthquery(int, Buffer *);
 316  int mm_answer_bsdauthrespond(int, Buffer *);
 317 @@ -206,10 +209,17 @@ struct mon_table mon_dispatch_proto20[] = {
 318      {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign},
 319      {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow},
 320      {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv},
 321 +#ifdef PAM_ENHANCEMENT
 322 +    {MONITOR_REQ_AUTHMETHOD, MON_ISAUTH, mm_answer_authmethod},
 323 +#endif
 324      {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner},
 325      {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword},
 326  #ifdef USE_PAM
 327 +#ifdef PAM_ENHANCEMENT
 328 +    {MONITOR_REQ_PAM_START, MON_ISAUTH, mm_answer_pam_start},
 329 +#else
 330      {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start},
 331 +#endif
 332      {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account},
 333      {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx},
 334      {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query},
 335 @@ -371,6 +381,24 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
 336                         if (!compat20)
 337                                 fatal("AuthenticationMethods is not supported"
 338                                     "with SSH protocol 1");
 339 +
 340 +#if defined(USE_PAM) && defined(PAM_ENHANCEMENT)
 341 +                        /* 
 342 +                         * If each userauth has its own PAM service, then PAM
 343 +                         * need to perform account check for this service.
 344 +                         */
 345 +                        if (options.use_pam && authenticated &&
 346 +                            options.pam_service_per_authmethod) {
 347 +                                Buffer m;
 348 +
 349 +                                buffer_init(&m);
 350 +                                mm_request_receive_expect(pmonitor->m_sendfd,
 351 +                                    MONITOR_REQ_PAM_ACCOUNT, &m);
 352 +                                authenticated = 
 353 +                                    mm_answer_pam_account(pmonitor->m_sendfd, &m);
 354 +                                buffer_free(&m);
 355 +                         }
 356 +#endif
 357                         if (authenticated &&
 358                             !auth2_update_methods_lists(authctxt,
 359                             auth_method, auth_submethod)) {
 360 @@ -389,8 +417,21 @@ monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor)
 361                             !auth_root_allowed(auth_method))
 362                                 authenticated = 0;
 363  #ifdef USE_PAM
 364 +#ifdef PAM_ENHANCEMENT
 365 +                        /*
 366 +                         * PAM needs to perform account checks after auth.
 367 +                         * However, if each userauth has its own PAM service
 368 +                         * and options.num_auth_methods != 0, then no need to
 369 +                         * perform account checking, because it was done 
 370 +                         * already.
 371 +                         */
 372 +                        if (options.use_pam && authenticated &&
 373 +                            !(options.num_auth_methods != 0 &&
 374 +                            options.pam_service_per_authmethod)) {
 375 +#else
 376                         /* PAM needs to perform account checks after auth */
 377                         if (options.use_pam && authenticated) {
 378 +#endif
 379                                 Buffer m;
 380  
 381                                 buffer_init(&m);
 382 @@ -863,6 +904,10 @@ mm_answer_pwnamallow(int sock, Buffer *m)
 383                 /* Allow service/style information on the auth context */
 384                 monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1);
 385                 monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1);
 386 +#ifdef PAM_ENHANCEMENT
 387 +                /* Allow authmethod information on the auth context */
 388 +               monitor_permit(mon_dispatch, MONITOR_REQ_AUTHMETHOD, 1);
 389 +#endif
 390         }
 391  #ifdef USE_PAM
 392         if (options.use_pam)
 393 @@ -903,6 +948,24 @@ mm_answer_authserv(int sock, Buffer *m)
 394         return (0);
 395  }
 396  
 397 +#ifdef PAM_ENHANCEMENT
 398 +int
 399 +mm_answer_authmethod(int sock, Buffer *m)
 400 +{
 401 +       monitor_permit_authentications(1);
 402 +
 403 +       authctxt->authmethod_name = buffer_get_string(m, NULL);
 404 +       debug3("%s: authmethod_name=%s", __func__, authctxt->authmethod_name);
 405 +
 406 +       if (strlen(authctxt->authmethod_name) == 0) {
 407 +               free(authctxt->authmethod_name);
 408 +               authctxt->authmethod_name = NULL;
 409 +       }
 410 +
 411 +       return (0);
 412 +}
 413 +#endif
 414 +
 415  int
 416  mm_answer_authpassword(int sock, Buffer *m)
 417  {
 418 diff --git a/monitor.h b/monitor.h
 419 index 93b8b66..da63e7d 100644
 420 --- a/monitor.h
 421 +++ b/monitor.h
 422 @@ -65,6 +65,9 @@ enum monitor_reqtype {
 423         MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
 424         MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
 425  
 426 +#ifdef PAM_ENHANCEMENT
 427 +        MONITOR_REQ_AUTHMETHOD = 114,
 428 +#endif        
 429  };
 430  
 431  struct mm_master;
 432 diff --git a/monitor_wrap.c b/monitor_wrap.c
 433 index eac421b..95231a3 100644
 434 --- a/monitor_wrap.c
 435 +++ b/monitor_wrap.c
 436 @@ -345,6 +345,24 @@ mm_inform_authserv(char *service, char *style)
 437         buffer_free(&m);
 438  }
 439  
 440 +#ifdef PAM_ENHANCEMENT
 441 +/* Inform the privileged process about the authentication method */
 442 +void
 443 +mm_inform_authmethod(char *authmethod)
 444 +{
 445 +       Buffer m;
 446 +
 447 +       debug3("%s entering", __func__);
 448 +
 449 +       buffer_init(&m);
 450 +       buffer_put_cstring(&m, authmethod);
 451 +
 452 +       mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHMETHOD, &m);
 453 +
 454 +       buffer_free(&m);
 455 +}
 456 +#endif
 457 +
 458  /* Do the password authentication */
 459  int
 460  mm_auth_password(Authctxt *authctxt, char *password)
 461 diff --git a/servconf.c b/servconf.c
 462 index ad884ec..d0ca777 100644
 463 --- a/servconf.c
 464 +++ b/servconf.c
 465 @@ -169,6 +169,18 @@ initialize_server_options(ServerOptions *options)
 466         options->ip_qos_bulk = -1;
 467         options->version_addendum = NULL;
 468         options->fingerprint_hash = -1;
 469 +#ifdef PAM_ENHANCEMENT
 470 +       options->pam_service_name = NULL;
 471 +       options->pam_service_prefix = NULL;
 472 +
 473 +       /* 
 474 +        * Each user method will have its own PAM service by default.
 475 +        * However, if PAMServiceName is specified or the protocal version
 476 +        * is not compat20, then there will be only one PAM service for the
 477 +        * entire user authentication.
 478 +        */
 479 +       options->pam_service_per_authmethod = 1;
 480 +#endif
 481  }
 482  
 483  /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
 484 @@ -340,6 +352,12 @@ fill_default_server_options(ServerOptions *options)
 485                 options->ip_qos_bulk = IPTOS_THROUGHPUT;
 486         if (options->version_addendum == NULL)
 487                 options->version_addendum = xstrdup("");
 488 +
 489 +#ifdef PAM_ENHANCEMENT
 490 +       if (options->pam_service_prefix == NULL)
 491 +               options->pam_service_prefix = _SSH_PAM_SERVICE_PREFIX;
 492 +#endif
 493 +
 494         if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1)
 495                 options->fwd_opts.streamlocal_bind_mask = 0177;
 496         if (options->fwd_opts.streamlocal_bind_unlink == -1)
 497 @@ -421,6 +439,9 @@ typedef enum {
 498         sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
 499         sUsePrivilegeSeparation, sAllowAgentForwarding,
 500         sHostCertificate,
 501 +#ifdef PAM_ENHANCEMENT
 502 +       sPAMServicePrefix, sPAMServiceName,
 503 +#endif
 504         sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
 505         sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
 506         sKexAlgorithms, sIPQoS, sVersionAddendum,
 507 @@ -559,6 +580,10 @@ static struct {
 508         { "forcecommand", sForceCommand, SSHCFG_ALL },
 509         { "chrootdirectory", sChrootDirectory, SSHCFG_ALL },
 510         { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL },
 511 +#ifdef PAM_ENHANCEMENT
 512 +       { "pamserviceprefix", sPAMServicePrefix, SSHCFG_GLOBAL },
 513 +       { "pamservicename", sPAMServiceName, SSHCFG_GLOBAL },
 514 +#endif
 515         { "revokedkeys", sRevokedKeys, SSHCFG_ALL },
 516         { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL },
 517         { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
 518 @@ -1865,6 +1890,37 @@ process_server_config_line(ServerOptions *options, char *line,
 519                         options->fingerprint_hash = value;
 520                 break;
 521  
 522 +       case sPAMServicePrefix:
 523 +               arg = strdelim(&cp);
 524 +               if (!arg || *arg == '\0')
 525 +                       fatal("%s line %d: Missing argument.",
 526 +                           filename, linenum);
 527 +               if (options->pam_service_name != NULL)
 528 +                       fatal("%s line %d: PAMServiceName and PAMServicePrefix"
 529 +                           " are mutually exclusive.", filename, linenum);
 530 +               if (options->pam_service_prefix == NULL)
 531 +                       options->pam_service_prefix = xstrdup(arg);
 532 +               break;
 533 +
 534 +       case sPAMServiceName:
 535 +               arg = strdelim(&cp);
 536 +               if (!arg || *arg == '\0')
 537 +                       fatal("%s line %d: Missing argument.",
 538 +                           filename, linenum);
 539 +               if (options->pam_service_prefix != NULL)
 540 +                       fatal("%s line %d: PAMServiceName and PAMServicePrefix"
 541 +                           " are mutually exclusive.", filename, linenum);
 542 +               if (options->pam_service_name == NULL) {
 543 +                       options->pam_service_name = xstrdup(arg);
 544 +
 545 +                       /*
 546 +                        * When this option is specified, we will not have
 547 +                        * PAM service for each auth method.
 548 +                         */
 549 +                       options->pam_service_per_authmethod = 0;
 550 +               }
 551 +               break;
 552 +
 553         case sDeprecated:
 554                 logit("%s line %d: Deprecated option %s",
 555                     filename, linenum, arg);
 556 diff --git a/servconf.h b/servconf.h
 557 index f4137af..8c86b57 100644
 558 --- a/servconf.h
 559 +++ b/servconf.h
 560 @@ -54,6 +54,10 @@
 561  /* Magic name for internal sftp-server */
 562  #define INTERNAL_SFTP_NAME     "internal-sftp"
 563  
 564 +#ifdef PAM_ENHANCEMENT
 565 +#define _SSH_PAM_SERVICE_PREFIX "sshd"
 566 +#endif
 567 +
 568  typedef struct {
 569         u_int   num_ports;
 570         u_int   ports_from_cmdline;
 571 @@ -194,6 +198,12 @@ typedef struct {
 572         u_int   num_auth_methods;
 573         char   *auth_methods[MAX_AUTH_METHODS];
 574  
 575 +#ifdef PAM_ENHANCEMENT
 576 +       char   *pam_service_prefix;
 577 +       char   *pam_service_name;
 578 +       int     pam_service_per_authmethod;
 579 +#endif
 580 +        
 581         int     fingerprint_hash;
 582  }       ServerOptions;
 583  
 584 diff --git a/sshd.1m b/sshd.1m
 585 index 967a753..d67efd7 100644
 586 --- a/sshd.1m
 587 +++ b/sshd.1m
 588 @@ -944,6 +944,33 @@ concurrently for different ports, this contains the process ID of the one
 589  started last).
 590  The content of this file is not sensitive; it can be world-readable.
 591  .El
 592 +
 593 +.Sh SECURITY
 594 +sshd uses pam(3PAM) for password and keyboard-interactive methods as well as 
 595 +for account management, session management, and the password management for all
 596 +authentication methods.
 597 +.Pp
 598 +Each SSHv2 userauth type has its own PAM service name:
 599 +
 600 +.Bd -literal -offset 3n
 601 +
 602 +-----------------------------------------------
 603 +| SSHv2 Userauth       | PAM Service Name     |
 604 +-----------------------------------------------
 605 +| none                 | sshd-none            |
 606 +-----------------------------------------------
 607 +| password             | sshd-password        |
 608 +-----------------------------------------------
 609 +| keyboard-interactive | sshd-kbdint          |
 610 +-----------------------------------------------
 611 +| pubkey               | sshd-pubkey          |
 612 +-----------------------------------------------
 613 +| hostbased            | sshd-hostbased       |
 614 +-----------------------------------------------
 615 +| gssapi-with-mic      | sshd-gssapi          |
 616 +-----------------------------------------------
 617 +.Ed
 618 +
 619  .Sh SEE ALSO
 620  .Xr scp 1 ,
 621  .Xr sftp 1 ,
 622 diff --git a/sshd.c b/sshd.c
 623 index 3df50f8..5a00ae2 100644
 624 --- a/sshd.c
 625 +++ b/sshd.c
 626 @@ -2159,6 +2159,11 @@ main(int ac, char **av)
 627  
 628         sshd_exchange_identification(sock_in, sock_out);
 629  
 630 +#ifdef PAM_ENHANCEMENT
 631 +       if (!compat20)
 632 +               options.pam_service_per_authmethod = 0;
 633 +#endif
 634 +
 635         /* In inetd mode, generate ephemeral key only for proto 1 connections */
 636         if (!compat20 && inetd_flag && sensitive_data.server_key == NULL)
 637                 generate_ephemeral_server_key();
 638 diff --git a/sshd_config.4 b/sshd_config.4
 639 index ba4d79a..263175b 100644
 640 --- a/sshd_config.4
 641 +++ b/sshd_config.4
 642 @@ -1160,6 +1160,21 @@ The probability increases linearly and all connection attempts
 643  are refused if the number of unauthenticated connections reaches
 644  .Dq full
 645  (60).
 646 +.It Cm PAMServiceName
 647 +Specifies the PAM service name for the PAM session. The PAMServiceName and 
 648 +PAMServicePrefix options are mutually exclusive and if both set, sshd does not
 649 +start. If this option is set the service name is the same for all user 
 650 +authentication methods. The option has no default value. See PAMServicePrefix 
 651 +for more information.
 652 +.It Cm PAMServicePrefix
 653 +Specifies the PAM service name prefix for service names used for individual 
 654 +user authentication methods. The default is sshd. The PAMServiceName and 
 655 +PAMServicePrefix options are mutually exclusive and if both set, sshd does not 
 656 +start.
 657 +.Pp
 658 +For example, if this option is set to admincli, the service name for the 
 659 +keyboard-interactive authentication method is admincli-kbdint instead of the 
 660 +default sshd-kbdint.
 661  .It Cm PasswordAuthentication
 662  Specifies whether password authentication is allowed.
 663  The default is
 664 @@ -1573,8 +1588,7 @@ If
 665  is enabled, you will not be able to run
 666  .Xr sshd 1M
 667  as a non-root user.
 668 -The default is
 669 -.Dq no .
 670 +On Solaris, the option is always enabled.
 671  .It Cm UsePrivilegeSeparation
 672  Specifies whether
 673  .Xr sshd 1M
 674 -- 
 675 2.3.2 (Apple Git-55)
 676