[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 {