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

Re: Use of PKINIT from PAM



Geoffrey Elgey wrote:

 > G'day,
 >
 > Are there any examples of PAM modules configured to use Heimdal's
 > PKINIT? Specifically using openssl UI callbacks for obtaining the PIN?


Yes, I sent a patch to this list on 5/3/5 with a diff file for pam_krb5-1.3-rc7
Here is a copy of that note and the diff file and pam.conf example.


-------- Original Message --------
Subject: Re: Use of PKINIT from PAM
Date: Tue, 03 May 2005 17:24:38 -0500
From: Douglas E. Engert <deengert@anl.gov>
To: Love Hörnquist Åstrand <lha@kth.se>
CC: heimdal-discuss@sics.se
References: <42715DC5.8090509@anl.gov> <am4qdo2q52.fsf@nutcracker.it.su.se>	<42768E07.9060506@anl.gov> 
<amk6mhxr2i.fsf@nutcracker.it.su.se>	<4277AA24.1080706@anl.gov> <amr7gousta.fsf@nutcracker.it.su.se>



Love Hörnquist Åstrand wrote:

> Is your GDM linked with pthreads ? Does is still deadlock if you run
> configure with --disable-pthread-support.

That fixed it, now things work as expected!!

Thanks a lot for adding these fixes.

I am also attaching the patch to the sourceforge pam_krb5-1.3-rc7
for anyone who is interested. This was configured with
CPPFLAGS="-DPKINIT" and without AFS as I was using pam_afs2.
The main changes are:

     creds_opt is no a pointer, as creds_opt must be allocated
     via krb5_get_init_creds_opt_alloc so the private pkinit part will
     be there.

     The pam args require try_pkinit otherwist it will not try the pkinit.

     If the first password is NULL, blank or "sc" then the pkinit will be
     tried. This is crude, but is good enough for testing.

I used a modified /ec/pam.d/gdm file that pointed at dee-system-auth
so you can look at the PAM paramaters. The GDM is gdm-2.6.0.5-6 on a
Red Hat Release Linux WS release 4.

The krb5.conf had:
[libdefaults]
...
     pkinit-openssl-engine = ENGINE=dynamic,
          PRE=SO_PATH:/opt/muscle/lib/opensc/engine_pkcs11.so,
          PRE=ID:pkcs11,PRE=LIST_ADD:1,PRE=LOAD,
          PRE=MODULE_PATH:/opt/muscle/lib/pkcs11/opensc-pkcs11.so,PRE=VERBOSE
[realms]
	MY.W2K.REALM = {
...
	win2k_pkinit = yes
...

...
[appdefaults]
      pkinit-anchors = OPENSSL-ANCHOR-DIR:/opt/muscle/trusted.certdir
      pk-user = ENGINE:CERT=slot_0,KEY=slot_0


So with along with the OpenSC-2050425, Heimdal-20050503, Muscle
pcsc-lite-1.2.9-beta7, ccid-0.9.3  and OpenSSL-0.9.7e  I believe all my changes
have been picked up or are in this pam diff file.



-- 

   Douglas E. Engert  <DEEngert@anl.gov>
   Argonne National Laboratory
   9700 South Cass Avenue
   Argonne, Illinois  60439
   (630) 252-5444


-- 

  Douglas E. Engert  <DEEngert@anl.gov>
  Argonne National Laboratory
  9700 South Cass Avenue
  Argonne, Illinois  60439
  (630) 252-5444
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      /lib/security/$ISA/pam_env.so
auth        sufficient    /lib/security/$ISA/pam_unix.so likeauth nullok
auth	    sufficient	  /krb5h/lib/pam_krb5.so stddebug:/tmp/gdm.debug debug use_first_pass force_cred try_pkinit  
#auth        sufficient    /lib/security/$ISA/pam_krb5.so debug use_first_pass force_cred
#auth        required	  /krb5/lib/pam_afs2.so debug 
auth        required      /lib/security/$ISA/pam_deny.so

account     required      /lib/security/$ISA/pam_unix.so
account	  [default=bad success=ok no_module_data=ignore user_unknown=ignore service_err=ignore system_err=ignore] /krb5h/lib/pam_krb5.so debug try_pkinit
#account     [default=bad success=ok user_unknown=ignore service_err=ignore system_err=ignore] /lib/security/$ISA/pam_krb5.so

password    required      /lib/security/$ISA/pam_cracklib.so retry=3 type=
password    sufficient    /lib/security/$ISA/pam_unix.so nullok use_authtok md5 shadow
password    sufficient    /krb5h/lib/pam_krb5.so debug use_authtok
#password    sufficient    /lib/security/$ISA/pam_krb5.so use_authtok
password    required      /lib/security/$ISA/pam_deny.so

session     required      /lib/security/$ISA/pam_limits.so
# with translator, no pag
session	    required	  /krb5/lib/pam_afs2.so debug  nopag
session     required      /lib/security/$ISA/pam_unix.so
session     optional      /krb5h/lib/pam_krb5.so debug
#session     optional      /lib/security/$ISA/pam_krb5.so

--- ,pam_krb5afs.c	Mon Mar 10 17:37:00 2003
+++ pam_krb5afs.c	Tue May  3 14:58:23 2005
@@ -49,6 +49,7 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <sys/resource.h>
 
 #ifdef HAVE_SYS_SYSLOG_H
 #include <sys/syslog.h>
@@ -234,7 +235,7 @@
 	int force_refresh;
 	int user_check;
 	int validate;
-	krb5_get_init_creds_opt creds_opt;
+	krb5_get_init_creds_opt *creds_opt;
 	int ticket_lifetime;
 	int renew_lifetime;
 	int warn_period;
@@ -247,6 +248,11 @@
 	char *required_tgs;
 	char *ccache_dir;
 	char *keytab;
+#ifdef PKINIT
+	char * pk_user_id;
+	char * pk_x509_anchors;
+	int try_pkinit;
+#endif
 };
 
 #ifdef KTH_KRB4
@@ -984,7 +990,7 @@
 	}
 	config = ret;
 	memset(ret, 0, sizeof(struct config));
-	krb5_get_init_creds_opt_init(&ret->creds_opt);
+	krb5_get_init_creds_opt_alloc(context,&ret->creds_opt);
 	ret->try_first_pass = 1;
 	ret->try_second_pass = 1;
 
@@ -1005,7 +1011,7 @@
 			  ret->realm, &ret->realm);
 	krb5_set_default_realm(context, ret->realm);
 #if defined(HEIMDAL) && defined(HAVE_KRB5_GET_INIT_CREDS_OPT_SET_DEFAULT_FLAGS)
-	krb5_get_init_creds_opt_set_default_flags(context, APPDEFAULT_APP, ret->realm, &ret->creds_opt);
+	krb5_get_init_creds_opt_set_default_flags(context, APPDEFAULT_APP, ret->realm, ret->creds_opt);
 #endif
 	/* Whether to get an addressless ticket, or to get a ticket containing
 	 * addresses of other hosts in addition to those of local interfaces. */
@@ -1064,7 +1070,7 @@
 		}
 		free(hosts);
 	}
-	krb5_get_init_creds_opt_set_address_list(&ret->creds_opt, addresses);	
+	krb5_get_init_creds_opt_set_address_list(ret->creds_opt, addresses);	
 #else
 	if (i == TRUE) {
 		DEBUG("Creating an addressless ticket");
@@ -1123,7 +1129,7 @@
 		}
 		free(hosts);
 	}
-	krb5_get_init_creds_opt_set_address_list(&ret->creds_opt, addresses);
+	krb5_get_init_creds_opt_set_address_list(ret->creds_opt, addresses);
 #endif
 	/* Whether to get krb4 tickets using either krb524_convert_creds() or
 	 * a v4 TGT request.  We have to do this here so that we can override
@@ -1189,10 +1195,10 @@
 	appdefault_boolean(context, "forwardable", argc, argv, TRUE, &i);
 	if (i) {
 		DEBUG("making tickets forwardable");
-		krb5_get_init_creds_opt_set_forwardable(&ret->creds_opt, TRUE);
+		krb5_get_init_creds_opt_set_forwardable(ret->creds_opt, TRUE);
 	} else {
 		DEBUG("making tickets non-forwardable");
-		krb5_get_init_creds_opt_set_forwardable(&ret->creds_opt, FALSE);
+		krb5_get_init_creds_opt_set_forwardable(ret->creds_opt, FALSE);
 	}
 #ifndef HEIMDAL
 	/* Support for changing timeouts. This plays with some internal library
@@ -1235,17 +1241,17 @@
 	appdefault_boolean(context, "proxiable", argc, argv, TRUE, &i);
 	if (i) {
 		DEBUG("making tickets proxiable");
-		krb5_get_init_creds_opt_set_proxiable(&ret->creds_opt, TRUE);
+		krb5_get_init_creds_opt_set_proxiable(ret->creds_opt, TRUE);
 	} else {
 		DEBUG("making tickets non-proxiable");
-		krb5_get_init_creds_opt_set_proxiable(&ret->creds_opt, FALSE);
+		krb5_get_init_creds_opt_set_proxiable(ret->creds_opt, FALSE);
 	}
 
 	/* Renewable lifetime. */
 	appdefault_integer(context, "renew_lifetime", argc, argv,
 			   DEFAULT_LIFE, &ret->renew_lifetime);
 	DEBUG("setting renewable lifetime to %d", ret->renew_lifetime);
-	krb5_get_init_creds_opt_set_renew_life(&ret->creds_opt,
+	krb5_get_init_creds_opt_set_renew_life(ret->creds_opt,
 					       ret->renew_lifetime);
 
 	/* Get the name of a service ticket the user must be able to obtain and
@@ -1265,7 +1271,7 @@
 	appdefault_integer(context, "ticket_lifetime", argc, argv,
 		           DEFAULT_LIFE, &ret->ticket_lifetime);
 	DEBUG("setting ticket lifetime to %d", ret->ticket_lifetime);
-	krb5_get_init_creds_opt_set_tkt_life(&ret->creds_opt,
+	krb5_get_init_creds_opt_set_tkt_life(ret->creds_opt,
 					     ret->ticket_lifetime);
 #endif
 #ifndef HEIMDAL
@@ -1298,6 +1304,14 @@
 	ret->warn_period = i;
 	DEBUG("warn_period %d", ret->warn_period);
 
+#ifdef PKINIT
+	appdefault_string(context, "pk-user", argc, argv,
+                 "", &ret->pk_user_id);
+	appdefault_string(context, "pkinit-anchors", argc, argv,
+				 "", &ret->pk_x509_anchors);
+#endif
+
+
 	/* Parse the rest of the arguments which don't fit the above
 	 * scheme very well. */
 	for (i = 0; i < argc; i++) {
@@ -1346,7 +1360,28 @@
 		    (strcmp(argv[i], "retain_tokens") == 0)) {
 		    ret->retain_token = 1;
 		}
-		
+
+		if ((strncmp(argv[i], "stddebug:",9) == 0)) {
+			/* usefull to get stdout and stderr from lower 
+			 * level libs during develoment
+			 */
+			int f;
+			if ((f = open(argv[i]+9,O_APPEND|O_WRONLY|O_CREAT,
+				S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) > 0) {
+				DEBUG("Directing stdout, stderr to %s",argv[i]+9);
+				close(1); /* stdout*/
+				dup(f);   /* set stdout to the debug file */
+				close(2); /* close stderr */
+				dup(f);   /* set stderr to the debug file */
+				close (f);/* close the temp file desc */
+			}
+		}
+			
+#ifdef PKINIT
+		if ((strcmp(argv[i], "try_pkinit") == 0)) {
+		    ret->try_pkinit = 1;
+		}
+#endif
 
 	}
 
@@ -1407,6 +1442,20 @@
 			free(cfg->keytab);
 			cfg->keytab = NULL;
 		}
+		if (cfg->creds_opt) {
+			krb5_get_init_creds_opt_free(cfg->creds_opt);
+			cfg->creds_opt = NULL;
+		}
+#ifdef PKINIT
+		if (cfg->pk_user_id) {
+			free(cfg->pk_user_id);
+			cfg->pk_user_id = NULL;
+		}
+		if (cfg->pk_x509_anchors) {
+			free(cfg->pk_x509_anchors);
+			cfg->pk_x509_anchors = NULL;
+		}
+#endif
 		free(cfg);
 	}
 }
@@ -1877,6 +1926,10 @@
 			DEBUG("pam_get_user returned `%s'", rouser);
 			user = strdup(rouser);
 		} else {
+#ifdef PKINIT
+	/* TODO could get the principal from the cert subject alt name */
+	/* but we also need to know if a smartcard is present */
+#endif
 			CRIT("couldn't determine user (first guess was `%s'), "
 			     "prompting for user name");
 			prc = pam_prompt_for(pamh,
@@ -1981,7 +2034,40 @@
 		}
 
 		/* Try the password, if we have one. */
+#ifdef PKINIT
+		if (config->try_pkinit && config->pk_user_id && config->pk_x509_anchors) {
+			if ( !password || password[0] == '\0' || !strcmp(password,"sc")) {
+			krc = krb5_get_init_creds_opt_set_pkinit(context, config->creds_opt,
+					principal,
+					config->pk_user_id,
+					config->pk_x509_anchors,
+					0, /* flags */
+					pam_prompter,
+					pamh,
+					NULL);
+			DEBUG("krb5_get_init_creds_opt_set_pkinit: returned %s",
+                     krc ? error_message(krc) : "Success");
+			if (krc != KRB5_SUCCESS) {
+				/* need to see if it is because no card, or what */
+			}
+		}
+	}
+#endif
 		if (config->try_first_pass && password && !authenticated) {
+#ifdef PKINIT
+			if ( password[0] == '\0' || !strcmp(password,"sc")) {
+				krc = krb5_get_init_creds_password(context,
+								&stash->v5_creds,
+								principal,
+								NULL,
+								pam_prompter,
+								pamh,
+								0,
+								NULL,
+								config->creds_opt);
+			} else
+#endif
+
 			krc = krb5_get_init_creds_password(context,
 							   &stash->v5_creds,
 							   principal,
@@ -1990,7 +2076,7 @@
 							   NULL,
 							   0,
 							   NULL,
-							   &config->creds_opt);
+							   config->creds_opt);
 			DEBUG("get_int_tkt returned %s",
 			      krc ? error_message(krc) : "Success");
 			if (krc == KRB5_SUCCESS) {
@@ -2053,7 +2139,7 @@
 							   pamh,
 							   0,
 							   NULL,
-							   &config->creds_opt);
+							   config->creds_opt);
 			DEBUG("get_int_tkt returned %s",
 			      krc ? error_message(krc) : "Success");
 			if (krc == KRB5_SUCCESS) {
@@ -3421,6 +3507,9 @@
 			 * password check or decryption succeeds, just whether
 			 * or not the attempt to fetch a TGT gives us a "key
 			 * expired" error or not. */
+#ifdef PKINIT
+		if (config->try_pkinit == 0) { /* skip this if pkinit */
+#endif
 #ifdef HEIMDAL
 		    krc = krb5_get_in_tkt(context,
 					      0,
@@ -3469,6 +3558,9 @@
 				default:
 					krc = KRB5_SUCCESS;
 			}
+#ifdef PKINIT
+			}
+#endif
 		} else {
 			prc = convert_kerror(krc);
 		}
@@ -3517,11 +3609,11 @@
 	DEBUG("pam_sm_chauthtok() called");
 	/* Reset the flags, since we're doing password changing. */
 	if (RC_OK) {
-		krb5_get_init_creds_opt_set_forwardable(&config->creds_opt,
+		krb5_get_init_creds_opt_set_forwardable(config->creds_opt,
 							FALSE);
-		krb5_get_init_creds_opt_set_proxiable(&config->creds_opt,
+		krb5_get_init_creds_opt_set_proxiable(config->creds_opt,
 						      FALSE);
-		krb5_get_init_creds_opt_set_renew_life(&config->creds_opt, 0);
+		krb5_get_init_creds_opt_set_renew_life(config->creds_opt, 0);
 	}
 
 	/* Initialize prompt strings. */
@@ -3584,7 +3676,7 @@
 						   NULL,
 						   0,
 						   PASSWORD_CHANGING_SERVICE,
-						   &config->creds_opt);
+						   config->creds_opt);
 		if (krc == KRB5_SUCCESS) {
 			DEBUG("user exists, but users's password is equal to "
 			      "user's name -- this should be changed");
@@ -3632,7 +3724,7 @@
 							   NULL,
 							   0,
 							   PASSWORD_CHANGING_SERVICE,
-							   &config->creds_opt);
+							   config->creds_opt);
 			if (krc == KRB5_SUCCESS) {
 				DEBUG("%s cleared for password change", user);
 			} else {
@@ -3712,7 +3804,7 @@
 							   NULL,
 							   0,
 							   PASSWORD_CHANGING_SERVICE,
-							   &config->creds_opt);
+							   config->creds_opt);
 			if (krc == KRB5_SUCCESS) {
 				DEBUG("%s prepared for password change", user);
 			} else {