[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: pam_krb5 with PKINIT from Heimdal and MIT



Attached are mods to Russ's pam_krb5-2.4 to use the Heimdal PKINIT
as found in the snapshots this week. These include error codes
to indicate if PKCS11 found a reader and smart card.

There is one non-pkinit mod and that is to return PAM_USER_UNKNOWN
rather the PAM_SERVICE_ERR if the user is to be ignored.

Also attached are /etc/pam.d/gdm and /etc/pam.d/gnome-screensaver
to use pam_krb5 with the try_pkinit before trying a password.

Note the try_pkinit and use_pkinit, to try and use pkinit
if a card is inserted, or to force the use of a smart card on this
workstation. This is independent of what the KDC may say.

Note that the way gnome-screensaver works, the broken_conv parameter
was introduced. Without this the gnome-screensaver-dialog will call
pam if the mouse is moved and will get into the pkinit code before
the user has a chance to insert the smart card.

In the krb5.conf file the following are the pkinit related lines:

[realms]
    TEST.REALM = {
	kdc = kdc.host.name...
         win2k_pkinit = yes
         pkinit_require_eku = no
#       win2k_pkinit_require_binding = no
#       pkinit_require_krbtgt_otherName = no
#       pkinit_require_hostname_match = no
         pkinit-anchors = DIR:/opt/smartcard/trusted.certdir
    }

[appdefaults]
     pkinit-pool = DIR:/opt/smartcard/pool.certdir
     pkinit-user = PKCS11:/usr/lib/opensc-pkcs11.so

This is run on Ubuntu Edgy, with OpenSC is 0.11.1 which has support
for the HSPD-12 PIV smart cards that I am using.  The Heimdal
and pam_krb5 are compiled from snapshots. The KDC is W2k3, and
the cert on the cards are Windows Enterprise CA issued certs.

There is still an issue with the way the Heimdal lib cleans up
and calls the PKCS11 C_Finalize to release the card.

P.S. the pam_afs2.so can refressh the AFS token when called from
gnome-screensaver.


Nalin Dahyabhai wrote:

> On Fri, Oct 06, 2006 at 11:44:15AM -0500, Douglas E. Engert wrote:
> 
>>So while waiting for the pam_krb5-2.4 source to show up on
>>http://www.eyrie.org, I am attaching the Heimdal pkinit mods for
>>pam-krb5-2.3 as a point of discussion. I would hope the MIT
>>and CITI people would comment on their pkinit API.
> 
> 
> The pkinit support is hooked in through a preauthentication abstraction
> interface.  The only thing libkrb5 is aware of on its side is that
> there's a module which can supply data for, and process data provided
> by, a KDC for preauthentication type 16 (or 17, or 15).
> 
> The same interface could be used to implement other preauthentication
> types (that's the intention anyway), but at the moment I think attention
> is focused on making sure that it's sufficient to interface with pkinit
> modules.
> 

The real use of pkinit is with smartcards, and so you need a way
to test if the user has inserted the card. The Heimdal snapshots have
a way to do this when it cals PKCS11.

> 
>> o The thinking is if the user puts in a smart card, try and use it.
>>   If no card is present use passwords as before. If they put in a card
>>   and it fails, don't fall back to password, make them take the card
>>   out first.
> 
> 
> The libkrb5 side of things goes through the list of preauth types
> suggested by the KDC, and the first preauth type for which it's able to
> obtain data is deemed good enough to fire off a request to the KDC.
> 
> If a pkinit module is called first, and it supplies data, then the
> client uses that, skipping the password prompt.  If the KDC rejects the
> pkinit request, the client will give up unless the KDC's error response
> includes e-data which might be used to try adjust the request.

Consider how the user reacts to this as you may need a PIN
for pkinit, or a password if not. So you may want to consider these
as seperate attempts and not try and do it all at once in the library.
Most users will use only a smartcard or a password but not both.

> 
> If the KDC's error included e-data, the modules get a crack at it, and
> if one of them provides different data, the library will re-send the
> request with the new padata.
> 
> The end-result is what you describe above.

Another point to keep in mind, is that smart card readers are being
introduced, that have a builtin keypad. It is used to enter the PIN
in such a way that the PIN is never sent to the workstation thus
avoiding keyboard sniffers. This means that pam can not treat the
"password" prompt as a prompt for the pin, and the pin can not be
cached. Heimdal and PKCS11, OpenSC and PCSC can handle such a reader
although  I don't have one of these readers to test.)


> 
> HTH,
> 
> Nalin
> _______________________________________________
> krbdev mailing list             krbdev@mit.edu
> https://mailman.mit.edu/mailman/listinfo/krbdev
> 
> 

-- 

  Douglas E. Engert  <DEEngert@anl.gov>
  Argonne National Laboratory
  9700 South Cass Avenue
  Argonne, Illinois  60439
  (630) 252-5444
--- ./,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 <sys/stat.h>
 #include <unistd.h>
 
+#ifdef HAVE_KRB5_HEIMDAL
+#include <hx509_err.h>
+#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;
#%PAM-1.0
auth	requisite	pam_nologin.so
auth	required	pam_env.so
auth sufficient /krb5h/lib/security/pam_krb5.so debug try_pkinit minimum_uid=100
@include common-auth

account  required  /krb5h/lib/security/pam_krb5.so debug minimum_uid=100
@include common-account

session	required	pam_limits.so
session optional /krb5h/lib/security/pam_krb5.so debug minimum_uid=100
session optional pam_afs2.so debug
@include common-session

@include common-password
auth  required     /krb5h/lib/security/pam_krb5.so debug minimum_uid=100 try_pkinit broken_conv
auth  optional       pam_unix.so debug try_first_pass nullok_secure 
auth  optional 	     pam_afs2.so debug nopag