Group :: System/Base
RPM: pam_pkcs11
Main Changelog Spec Patches Sources Download Gear Bugs and FR Repocop
Patch: pam_pkcs11-0.6.9-oid-mapper.patch
Download
Download
doc/pam_pkcs11.xml | 11 +-
src/common/cert_info.c | 275 +++++++++++++++++++++----------------------
src/common/cert_info.h | 2 +
src/mappers/generic_mapper.c | 100 +++++++++++++++-
4 files changed, 237 insertions(+), 151 deletions(-)
diff --git a/doc/pam_pkcs11.xml b/doc/pam_pkcs11.xml
index a073c90..27e5ef2 100644
--- a/doc/pam_pkcs11.xml
+++ b/doc/pam_pkcs11.xml
@@ -2158,8 +2158,17 @@ generic_mapper configuration file shows like:
# module = /usr/lib/pam_pkcs11/generic_mapper.so;
# ignore letter case on match/compare
ignorecase = false;
- # Use one of "cn" , "subject" , "kpn" , "email" , "upn" , "uid" or "serial"
+ # Use one of "cn" , "subject" , "kpn" , "email" , "upn" , "uid",
+ # "serial" or any OID value, e.g. 1.2.643.100.3 .
cert_item = cn;
+ # Use scramble flag to hash the extracted value with SHA1 hash
+ scramble = true;
+ # Limit the size of the hashed value to maxlen characters
+ maxlen = 15;
+ # May declare prefix and postfix that are automatically added
+ # to the extracted login values.
+ prefix = auto-;
+ postfix = -user;
# Declare mapfile if needed, else select "none"
mapfile = file:///etc/pam_pkcs11/generic_mapfile
# Decide if use getpwent() to map login
diff --git a/src/common/cert_info.c b/src/common/cert_info.c
index 12965e1..35600c6 100644
--- a/src/common/cert_info.c
+++ b/src/common/cert_info.c
@@ -329,47 +329,13 @@ void add_cert(X509 *cert, X509 ***certs, int *ncerts) {
(*ncerts)++;
}
+static char **cert_info_subj_obj(X509 *x509, ASN1_OBJECT *obj);
+
/*
* Extract Certificate's Common Name
*/
static char **cert_info_cn(X509 *x509) {
- static char *results[CERT_INFO_SIZE];
- int lastpos,position;
- X509_NAME *name = X509_get_subject_name(x509);
- if (!name) {
- DBG("Certificate has no subject");
- return NULL;
- }
- for (position=0;position<CERT_INFO_SIZE;position++) results[position]= NULL;
- position=0;
- lastpos = X509_NAME_get_index_by_NID(name,NID_commonName,-1);
- if (lastpos == -1) {
- DBG("Certificate has no UniqueID");
- return NULL;
- }
- while( ( lastpos != -1 ) && (position<CERT_INFO_MAX_ENTRIES) ) {
- X509_NAME_ENTRY *entry;
- ASN1_STRING *str;
- unsigned char *txt;
- if ( !(entry = X509_NAME_get_entry(name,lastpos)) ) {
- DBG1("X509_get_name_entry() failed: %s", ERR_error_string(ERR_get_error(),NULL));
- return results;
- }
- if ( !(str = X509_NAME_ENTRY_get_data(entry)) ) {
- DBG1("X509_NAME_ENTRY_get_data() failed: %s", ERR_error_string(ERR_get_error(),NULL));
- return results;
- }
- if ( ( ASN1_STRING_to_UTF8(&txt, str) ) < 0) {
- DBG1("ASN1_STRING_to_UTF8() failed: %s", ERR_error_string(ERR_get_error(),NULL));
- return results;
- }
- DBG2("%s = [%s]", OBJ_nid2sn(NID_commonName), txt);
- results[position++]=clone_str((const char *)txt);
- OPENSSL_free(txt);
- lastpos = X509_NAME_get_index_by_NID(name, NID_commonName, lastpos);
- }
- /* no more UID's availables in certificate */
- return results;
+ return cert_info_subj_obj(x509, OBJ_nid2obj(NID_commonName));
}
/*
@@ -407,59 +373,91 @@ static char **cert_info_issuer(X509 *x509) {
}
/*
-* Extract Certificate's Kerberos Principal Name
+* Extract Certificate's value by OID (object)
*/
-static char **cert_info_kpn(X509 *x509) {
- int i,j;
+static char **cert_info_ex_obj(X509 *x509, const ASN1_OBJECT *obj) {
+ int i, j = 0;
static char *entries[CERT_INFO_SIZE];
- STACK_OF(GENERAL_NAME) *gens;
- GENERAL_NAME *name;
- ASN1_OBJECT *krb5PrincipalName;
- DBG("Trying to find a Kerberos Principal Name in certificate");
- gens = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL);
- krb5PrincipalName = OBJ_txt2obj("1.3.6.1.5.2.2", 1);
- if (!gens) {
- DBG("No alternate name extensions");
- return NULL; /* no alternate names */
- }
- if (!krb5PrincipalName) {
- DBG("Cannot map KPN object");
- return NULL;
- }
- for (j=0;j<CERT_INFO_SIZE;j++) entries[j] = NULL;
- for (i=0,j=0; (i < sk_GENERAL_NAME_num(gens)) && (j<CERT_INFO_MAX_ENTRIES); i++) {
+
+ char oid[256];
+ OBJ_obj2txt(oid, sizeof(oid), obj, 0);
+
+ DBG1("Trying to find %s entry in the certificate...", oid);
+
+ STACK_OF(GENERAL_NAME) *gens;
+ GENERAL_NAME *name;
+ gens = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL);
+
+ if (gens) {
+ for (j=0; j < CERT_INFO_SIZE; j++) entries[j] = NULL;
+ for (i=0, j=0; (i < sk_GENERAL_NAME_num(gens)) && (j < CERT_INFO_MAX_ENTRIES); i++) {
name = sk_GENERAL_NAME_value(gens, i);
- if ( name && name->type==GEN_OTHERNAME ) { /* test for UPN */
- if (OBJ_cmp(name->d.otherName->type_id, krb5PrincipalName)) continue; /* object is not a UPN */
- else {
- /* NOTE:
- from PKINIT RFC, I deduce that stored format for kerberos
- Principal Name is ASN1_STRING, but not sure at 100%
- Any help will be granted
- */
- unsigned char *txt;
- ASN1_TYPE *val = name->d.otherName->value;
- ASN1_STRING *str= val->value.asn1_string;
- DBG("Found Kerberos Principal Name ");
- if ( ( ASN1_STRING_to_UTF8(&txt, str) ) < 0) {
- DBG1("ASN1_STRING_to_UTF8() failed: %s", ERR_error_string(ERR_get_error(),NULL));
- } else {
- DBG1("Adding KPN entry: %s",txt);
- entries[j++]= clone_str((const char *)txt);
- }
- }
+ if (name && name->type == GEN_OTHERNAME) {
+ if (OBJ_cmp(name->d.otherName->type_id, obj)) {
+ continue; /* OID doesn't match */
+ } else {
+ DBG1("Found %s entry", oid);
+ unsigned char *txt = NULL;
+ ASN1_TYPE *val = name->d.otherName->value;
+
+ switch (val->type) {
+ case V_ASN1_UTF8STRING:
+ txt = val->value.utf8string->data;
+ break;
+ default:
+ if ((ASN1_STRING_to_UTF8(&txt, val->value.asn1_string)) < 0) {
+ DBG1("ASN1_STRING_to_UTF8() failed: %s", ERR_error_string(ERR_get_error(), NULL));
+ }
+ break;
+ }
+
+ if (txt) {
+ DBG1("Adding entry: %s",txt);
+ entries[j++]= clone_str((const char *)txt);
+ }
+ }
}
}
+
sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
- ASN1_OBJECT_free(krb5PrincipalName);
- if(j==0) {
- DBG("Certificate does not contain a KPN entry");
+ } else {
+ DBG("No alternate name extensions");
+ }
+
+ if (j == 0) {
+ DBG1("Certificate extensions do not contain the %s entry", oid);
return NULL;
}
+
return entries;
}
/*
+* Extract Certificate's value by OID
+*/
+static char **cert_info_oid(X509 *x509, const char *oid) {
+ ASN1_OBJECT *obj = OBJ_txt2obj(oid, 1);
+ if (!obj) {
+ DBG("Cannot map OID");
+ return NULL;
+ }
+
+ char **ret = cert_info_subj_obj(x509, obj);
+ if (!ret) {
+ ret = cert_info_ex_obj(x509, obj);
+ ASN1_OBJECT_free(obj);
+ }
+ return ret;
+}
+
+/*
+* Extract Certificate's Kerberos Principal Name
+*/
+static char **cert_info_kpn(X509 *x509) {
+ return cert_info_oid(x509, "1.3.6.1.5.2.2");
+}
+
+/*
* Extract Certificate's email
*/
static char **cert_info_email(X509 *x509) {
@@ -492,90 +490,73 @@ static char **cert_info_email(X509 *x509) {
* Extract Certificate's Microsoft Universal Principal Name
*/
static char **cert_info_upn(X509 *x509) {
- int i,j;
- static char *entries[CERT_INFO_SIZE];
- STACK_OF(GENERAL_NAME) *gens;
- GENERAL_NAME *name;
- DBG("Trying to find an Universal Principal Name in certificate");
- gens = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL);
- if (!gens) {
- DBG("No alternate name extensions found");
- return NULL;
- }
- for (j=0;j<CERT_INFO_SIZE;j++) entries[j] = NULL;
- for (i=0,j=0; (i < sk_GENERAL_NAME_num(gens)) && (j<CERT_INFO_MAX_ENTRIES); i++) {
- name = sk_GENERAL_NAME_value(gens, i);
- if ( name && name->type==GEN_OTHERNAME ) {
- /* test for UPN */
- if (OBJ_cmp(name->d.otherName->type_id, OBJ_nid2obj(NID_ms_upn))) continue; /* object is not a UPN */
- DBG("Found MS Universal Principal Name ");
- /* try to extract string and return it */
- if (name->d.otherName->value->type == V_ASN1_UTF8STRING) {
- ASN1_UTF8STRING *str = name->d.otherName->value->value.utf8string;
- DBG1("Adding UPN NAME entry= %s",str->data);
- entries[j++] = clone_str((const char *)str->data);
- } else {
- DBG("Found UPN entry is not an utf8string");
- }
- }
- }
- sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
- if(j==0) {
- DBG("Certificate does not contain a MS UPN entry");
- return NULL;
- }
- return entries;
+ return cert_info_ex_obj(x509, OBJ_nid2obj(NID_ms_upn));
}
/*
-* Extract Certificate's Unique Identifier(s)
+* Extract given field Certificate's Unique Identifier(s)
* Array size is limited to CERT_INFO_MAX_ENTRIES UID's. expected to be enough...
*/
-static char **cert_info_uid(X509 *x509) {
+static char **cert_info_subj_obj(X509 *x509, ASN1_OBJECT *obj) {
static char *results[CERT_INFO_SIZE];
- int lastpos,position;
- int uid_type = UID_TYPE;
- X509_NAME *name = X509_get_subject_name(x509);
- if (!name) {
+ int lastpos = -1; int position = 0;
+
+ char oid[256];
+ OBJ_obj2txt(oid, sizeof(oid), obj, 0);
+
+ X509_NAME *name = X509_get_subject_name(x509);
+ if (!name) {
DBG("Certificate has no subject");
return NULL;
}
- for (position=0;position<CERT_INFO_SIZE;position++) results[position]= NULL;
- position=0;
- lastpos = X509_NAME_get_index_by_NID(name,uid_type,-1);
- if (lastpos == -1) {
- uid_type = NID_userId;
- lastpos = X509_NAME_get_index_by_NID(name,uid_type,-1);
- if (lastpos == -1) {
- DBG("Certificate has no UniqueID");
- return NULL;
- }
- }
- while( ( lastpos != -1 ) && (position<CERT_INFO_MAX_ENTRIES) ) {
+ for (position=0; position < CERT_INFO_SIZE; position++) {
+ results[position] = NULL;
+ }
+ position = 0;
+ lastpos = X509_NAME_get_index_by_OBJ(name, obj, -1);
+ if (lastpos == -1) {
+ DBG1("Certificate subject does not contain %s entry", oid);
+ return NULL;
+ }
+
+ while ((lastpos != -1) && (position < CERT_INFO_MAX_ENTRIES)) {
X509_NAME_ENTRY *entry;
ASN1_STRING *str;
unsigned char *txt;
- if ( !(entry = X509_NAME_get_entry(name,lastpos)) ) {
- DBG1("X509_get_name_entry() failed: %s", ERR_error_string(ERR_get_error(),NULL));
- return results;
- }
+ if ( !(entry = X509_NAME_get_entry(name, lastpos)) ) {
+ DBG1("X509_get_name_entry() failed: %s", ERR_error_string(ERR_get_error(),NULL));
+ return results;
+ }
if ( !(str = X509_NAME_ENTRY_get_data(entry)) ) {
- DBG1("X509_NAME_ENTRY_get_data() failed: %s", ERR_error_string(ERR_get_error(),NULL));
- return results;
- }
- if ( ( ASN1_STRING_to_UTF8(&txt, str) ) < 0) {
- DBG1("ASN1_STRING_to_UTF8() failed: %s", ERR_error_string(ERR_get_error(),NULL));
- return results;
- }
- DBG2("%s = [%s]", OBJ_nid2sn(UID_TYPE), txt);
- results[position++]=clone_str((const char *)txt);
+ DBG1("X509_NAME_ENTRY_get_data() failed: %s", ERR_error_string(ERR_get_error(),NULL));
+ return results;
+ }
+ if ( (ASN1_STRING_to_UTF8(&txt, str)) < 0) {
+ DBG1("ASN1_STRING_to_UTF8() failed: %s", ERR_error_string(ERR_get_error(),NULL));
+ return results;
+ }
+ DBG2("%s = [%s]", oid, txt);
+ results[position++] = clone_str((const char *)txt);
OPENSSL_free(txt);
- lastpos = X509_NAME_get_index_by_NID(name, UID_TYPE, lastpos);
+ lastpos = X509_NAME_get_index_by_OBJ(name, obj, lastpos);
}
- /* no more UID's availables in certificate */
+ /* no more objects availables in the certificate */
+
+ if (position == 0) {
+ DBG1("Certificate subject does not contain %s entry", oid);
+ return NULL;
+ }
return results;
}
+/*
+* Extract Certificate's Unique Identifier(s)
+* Array size is limited to CERT_INFO_MAX_ENTRIES UID's. expected to be enough...
+*/
+static char **cert_info_uid(X509 *x509) {
+ return cert_info_subj_obj(x509, OBJ_nid2obj(NID_userId));
+}
+
/* convert publickey into PEM format */
static char *key2pem(EVP_PKEY *key) {
int len;
@@ -941,6 +922,12 @@ char **cert_info(X509 *x509, int type, const char *algorithm ) {
default :
DBG1("Invalid info type requested: %d",type);
return NULL;
+ case CERT_OID:
+ if ( !algorithm ) {
+ DBG("Requires OID value in the \"algorithm\" argument");
+ return NULL;
+ }
+ return cert_info_oid(x509, algorithm);
}
/* should not get here */
return NULL;
diff --git a/src/common/cert_info.h b/src/common/cert_info.h
index 3b3353e..2d3c6d5 100644
--- a/src/common/cert_info.h
+++ b/src/common/cert_info.h
@@ -47,6 +47,8 @@
#define CERT_SERIAL 12
/** Certificate key algorithm */
#define CERT_KEY_ALG 13
+/** Certificate value by OID */
+#define CERT_OID 100
/** Max size of returned certificate content array */
#define CERT_INFO_SIZE 16
diff --git a/src/mappers/generic_mapper.c b/src/mappers/generic_mapper.c
index 1f5a214..53baacd 100644
--- a/src/mappers/generic_mapper.c
+++ b/src/mappers/generic_mapper.c
@@ -33,6 +33,8 @@
#include "../common/error.h"
#include "../common/strings.h"
#include "../common/cert_info.h"
+#include "../common/base64.h"
+#include <openssl/sha.h>
#include "mapper.h"
#include "generic_mapper.h"
@@ -44,14 +46,93 @@ static const char *mapfile = "none";
static int usepwent = 0;
static int ignorecase = 0;
static int id_type = CERT_CN;
+static const char *algorithm = ALGORITHM_NULL;
static int debug = 0;
+static const char *prefix;
+static const char *postfix;
+static int scramble = 0;
+
+#define MAX_ENTRY_LEN 256
+static int maxlen = MAX_ENTRY_LEN;
+
+static char *scramble_entry(const char* entry);
static char **generic_mapper_find_entries(X509 *x509, void *context) {
- if (!x509) {
- DBG("NULL certificate provided");
- return NULL;
+ if (!x509) {
+ DBG("NULL certificate provided");
+ return NULL;
+ }
+
+ char **entries = cert_info(x509, id_type, algorithm);
+ if (!entries) {
+ return NULL;
+ }
+
+ char *entry; int n;
+ char *entrybuf;
+ for (n=0, entry=entries[n]; entry; entry=entries[++n]) {
+ if (scramble) {
+ entries[n] = scramble_entry(entry);
+ // FIXME: free(entry) ?
+ entry = entries[n];
}
- return cert_info(x509, id_type, ALGORITHM_NULL);
+ if ((prefix || postfix) && NULL != entry) {
+ entrybuf = malloc(MAX_ENTRY_LEN);
+ if (!entrybuf) {
+ DBG("Unable to allocate entry buffer");
+ entries[n] = NULL;
+ } else {
+ snprintf(entrybuf, MAX_ENTRY_LEN, "%s%s%s",
+ prefix, entry, postfix);
+ entries[n] = entrybuf;
+ // FIXME: free(entry) ?
+ entry = entries[n];
+ }
+ }
+ }
+
+ return entries;
+}
+
+#define SHA1_LENGTH 20
+
+static char *scramble_entry(const char* entry) {
+ unsigned char hash[SHA1_LENGTH];
+ size_t entrysize = (4 * ((sizeof(hash) + 2) / 3)) + 1;
+ char *entrybuf;
+ SHA1(entry, strlen(entry), hash);
+
+ entrybuf = malloc(entrysize);
+ if (!entrybuf) {
+ DBG("Unable to allocate entry buffer");
+ return NULL;
+ }
+
+ size_t outlen = entrysize;
+ if ( 0 != base64_encode(hash, sizeof(hash), entrybuf, &outlen) ) {
+ DBG("Unexpected error: unable to BASE64-encode the entry");
+ return NULL;
+ }
+
+ int i;
+ for (i = 0; i < outlen; i++) {
+ if (entrybuf[i] >= 'A' && entrybuf[i] <= 'Z') {
+ entrybuf[i] = entrybuf[i] + 'a' - 'A';
+ } else {
+ switch (entrybuf[i]) {
+ case '+':
+ case '/':
+ case '=':
+ entrybuf[i] = 'o';
+ }
+ }
+ }
+
+ if (outlen > maxlen) {
+ entrybuf[maxlen] = '\0';
+ }
+
+ return entrybuf;
}
static char **get_mapped_entries(char **entries) {
@@ -175,7 +256,11 @@ mapper_module * generic_mapper_module_init(scconf_block *blk,const char *name) {
ignorecase = scconf_get_bool( blk,"ignorecase",0);
usepwent = scconf_get_bool( blk,"use_getpwent",0);
mapfile= scconf_get_str(blk,"mapfile",mapfile);
- item= scconf_get_str(blk,"cert_item","cn");
+ item = scconf_get_str(blk,"cert_item","cn");
+ prefix = scconf_get_str(blk,"prefix", "");
+ postfix = scconf_get_str(blk,"postfix", "");
+ scramble = scconf_get_bool(blk,"scramble", 0);
+ maxlen = scconf_get_int(blk,"maxlen", MAX_ENTRY_LEN);
} else {
/* should not occurs, but... */
DBG1("No block declaration for mapper '%s'",name);
@@ -188,7 +273,10 @@ mapper_module * generic_mapper_module_init(scconf_block *blk,const char *name) {
else if (!strcasecmp(item,"upn") ) id_type=CERT_UPN;
else if (!strcasecmp(item,"uid") ) id_type=CERT_UID;
else if (!strcasecmp(item,"serial") ) id_type=CERT_SERIAL;
- else {
+ else if (strlen(item) > 2 && item[0] >= '0' && item[0] < '3' && item[1] == '.') {
+ id_type = CERT_OID;
+ algorithm = item;
+ } else {
DBG1("Invalid certificate item to search '%s'; using 'cn'",item);
}
pt = init_mapper_st(blk,name);