--- ./,options.c Thu Oct 5 18:28:12 2006 +++ ./options.c Thu Oct 12 16:24:09 2006 @@ -27,6 +27,10 @@ args->ccache_dir = NULL; args->realm = NULL; args->realm_data = NULL; +#ifdef HAVE_KRB5_HEIMDAL + args->pk_user = NULL; + args->pk_anchors = NULL; +#endif return args; } @@ -43,6 +47,12 @@ free(args->ccache_dir); if (args->realm != NULL) free(args->realm); +#ifdef HAVE_KRB5_HEIMDAL + if (args->pk_user !=NULL) + free(args->pk_user); + if (args->pk_anchors !=NULL) + free(args->pk_anchors); +#endif pamk5_compat_free_realm(args); free(args); } @@ -176,6 +186,13 @@ default_time(args, c, "renew_lifetime", 0, &args->renew_lifetime); default_boolean(args, c, "retain_after_close", 0, &args->retain); default_boolean(args, c, "search_k5login", 0, &args->search_k5login); +#ifdef HAVE_KRB5_HEIMDAL + default_boolean(args, c, "try_pkinit", 0, &args->try_pkinit); + default_boolean(args, c, "use_pkinit", 0, &args->use_pkinit); + default_boolean(args, c, "broken_conv", 0, &args->broken_conv); + default_string(args, c, "pkinit-user", NULL, &args->pk_user); + default_string(args, c, "pkinit-anchors", NULL, &args->pk_anchors); +#endif krb5_free_context(c); } @@ -226,6 +243,25 @@ args->use_authtok = 1; else if (strcmp(argv[i], "use_first_pass") == 0) args->use_first_pass = 1; +#ifdef HAVE_KRB5_HEIMDAL + else if (strcmp(argv[i], "try_pkinit") == 0) + args->try_pkinit = 1; + else if (strcmp(argv[i], "use_pkinit") == 0) + args->use_pkinit = 1; + else if (strcmp(argv[i], "broken_conv") == 0) + args->broken_conv = 1; + else if (strncmp(argv[i], "pkinit-user=", 12) == 0) { + if (args->pk_user) + free(args->pk_user); + args->pk_user = strdup(&argv[i][strlen("pkinit-user=")]); + } + else if (strncmp(argv[i], "pkinit-anchors=", 15) == 0) { + if (args->pk_anchors) + free(args->pk_anchors); + args->pk_anchors = strdup(&argv[i][strlen("pkinit-anchors=")]); + } + +#endif else pamk5_error(NULL, "unknown option %s", argv[i]); } --- ./,pam_krb5.h Thu Oct 5 18:28:12 2006 +++ ./pam_krb5.h Thu Oct 12 16:22:45 2006 @@ -34,6 +34,13 @@ int try_first_pass; /* Try the previously entered password. */ int use_authtok; /* Require a previous password be used. */ int use_first_pass; /* Always use the previous password. */ +#if HAVE_KRB5_HEIMDAL + int try_pkinit; /* try to use PKINIT if there is a smartcard */ + int use_pkinit; /* Only try PKINIT not password */ + int broken_conv; /* try and get PIN via pam_conv */ + char *pk_user; /* parameter to pass to PKINIT PKCS11:/path/pkcs11.so */ + char *pk_anchors; /* trusted certs usually per realm */ +#endif /* * The default realm, used mostly in option parsing but also for @@ -95,6 +102,12 @@ * are verified by checking them against the local system key. */ int pamk5_password_auth(struct context *, struct pam_args *, + char *in_tkt_service, struct credlist **); + +/* + * Use spartcard with pkinit + */ +int pamk5_pkinit_auth(struct context *, struct pam_args *, char *in_tkt_service, struct credlist **); /* Generic prompting function to get information from the user. */ --- ./,support.c Thu Oct 5 18:28:12 2006 +++ ./support.c Thu Oct 12 16:25:56 2006 @@ -22,6 +22,9 @@ #include #include +#ifdef HAVE_KRB5_HEIMDAL +#include +#endif #include "pam_krb5.h" /* @@ -182,10 +185,13 @@ int retval, retry, success; char *pass = NULL; int authtok = in_tkt_service == NULL ? PAM_AUTHTOK : PAM_OLDAUTHTOK; +#ifdef HAVE_KRB5_HEIMDAL + int try_password = 1; +#endif /* Bail if we should be ignoring this user. */ if (pamk5_should_ignore(ctx, args, ctx->name)) { - retval = PAM_SERVICE_ERR; + retval = PAM_USER_UNKNOWN; goto done; } @@ -224,6 +230,97 @@ retval = PAM_SERVICE_ERR; goto done; } + +#ifdef HAVE_KRB5_HEIMDAL + /* + * Try and do PKINIT using smartcard. Some smartcard readers have a pin pad + * to have the pin entered directly into the card without every being exposed + * to a keyboard sniffer, or other host based code. The Heimdal PKINIT can handle + * this and use the prompter instead. + * We can try for PKINIT and then password, or just PKINIT with the use_pkinit + * + * PKINIT is just one of many possible pre-auth types that could be used. + * We try and treat this differently as the the user has some input by inserting + * a smartcard in the reader or not. + */ + + if (args->use_pkinit || args->try_pkinit) { + krb5_get_init_creds_opt *opts_pkinit = NULL; /* special expanded creds_opts */ + + try_password = 0; + + /* + * gnome-screensaver jumps into pam if mouse is moved, and expects to get prompted + * for password. So we need to wait here to give user chance to insert card + */ + if (args->broken_conv) { + retval = pamk5_prompt(ctx->pamh, + args->use_pkinit ? "Insert smart card then Enter " + : "Password: or Insert smart card then Enter", + PAM_PROMPT_ECHO_OFF, &pass); + if (pass && *pass != '\0') { + /* Set this for the next PAM module's try_first_pass. */ + retval = pam_set_item(ctx->pamh, authtok, pass); + free(pass); + if (retval != PAM_SUCCESS) { + pamk5_debug_pam(ctx, args, "error storing password", retval); + retval = PAM_SERVICE_ERR; + goto done; + } + pam_get_item(ctx->pamh, authtok, (void *) &pass); + } + } + + krb5_get_init_creds_opt_alloc(ctx->context, &opts_pkinit); + if (opts_pkinit == NULL) { + retval = PAM_SERVICE_ERR; + goto done; + } +#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS + krb5_get_init_creds_opt_set_default_flags(ctx->context, "pam", + args->realm_data, opts_pkinit); +#endif + if (args->forwardable) + krb5_get_init_creds_opt_set_forwardable(opts_pkinit,1); + if (args->renew_lifetime != 0) + krb5_get_init_creds_opt_set_renew_life(opts_pkinit,args->renew_lifetime); + + retval = krb5_get_init_creds_opt_set_pkinit(ctx->context, + opts_pkinit, + ctx->princ, + args->pk_user, + args->pk_anchors, + NULL, + NULL, + 0, + pamk5_prompter_krb5, + ctx->pamh, + NULL); + + if (retval == 0) + retval = krb5_get_init_creds_password(ctx->context, + &creds, ctx->princ, + NULL, + pamk5_prompter_krb5, + ctx->pamh, + 0, + in_tkt_service, + opts_pkinit); + + if (retval == 0) + retval = pamk5_credlist_append(ctx, credlist, creds); + + if (retval == HX509_PKCS11_NO_TOKEN || retval == HX509_PKCS11_NO_SLOT) + try_password = 1; /* no card, or reader, try password */ + + if (args->use_pkinit == 1) /* never try the password */ + try_password = 0; + + if (opts_pkinit) + krb5_get_init_creds_opt_free(opts_pkinit); + } + if (try_password) /* skip the loop and process the retval or try with a password */ +#endif do { if (pass == NULL) { retry = 0;