configure.ac | 10 + etc/Makefile.am | 14 ++ etc/control.d/Makefile.am | 3 + etc/control.d/pam-pkcs11-mapping.in | 10 + etc/control.d/pam-pkcs11-module.in | 202 +++++++++++++++++ etc/control.d/pam-pkcs11-profile.in | 325 +++++++++++++++++++++++++++ etc/control.d/pkcs11-events.in | 209 +++++++++++++++++ etc/mapping.profiles/cert.in | 13 ++ etc/mapping.profiles/cn.in | 12 + etc/mapping.profiles/mail.in | 16 ++ etc/mapping.profiles/subject.in | 12 + etc/modules.avail/aladdin.in | 10 + etc/pam.d_login.example.in | 2 +- etc/pam_pkcs11.conf.example.in | 59 +++-- etc/profiles/opensc | 9 + etc/system-auth-pkcs11_strict | 11 + etc/system-auth-use_first_pass-pkcs11_strict | 6 + 17 files changed, 899 insertions(+), 24 deletions(-) diff --git a/configure.ac b/configure.ac index 2821092..69ae387 100644 --- a/configure.ac +++ b/configure.ac @@ -258,6 +258,16 @@ doc/doxygen.conf etc/Makefile etc/pam.d_login.example etc/pam_pkcs11.conf.example +etc/control.d/Makefile +etc/control.d/pam-pkcs11-module +etc/control.d/pam-pkcs11-profile +etc/control.d/pam-pkcs11-mapping +etc/control.d/pkcs11-events +etc/mapping.profiles/cn +etc/mapping.profiles/mail +etc/mapping.profiles/subject +etc/mapping.profiles/cert +etc/modules.avail/aladdin src/Makefile src/scconf/Makefile src/common/Makefile diff --git a/etc/Makefile.am b/etc/Makefile.am index fc5e35b..fd3f7bc 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -1,6 +1,7 @@ # Process this file with automake to create Makefile.in MAINTAINERCLEANFILES = Makefile.in +SUBDIRS = control.d TO_INSTALL = \ subject_mapping.example \ @@ -16,3 +17,16 @@ EXTRA_DIST = $(TO_INSTALL) \ doc_DATA = $(TO_INSTALL) \ pam_pkcs11.conf.example \ pam.d_login.example + +pamdir = $(sysconfdir)/pam.d +pam_DATA = system-auth-pkcs11_strict system-auth-use_first_pass-pkcs11_strict + +modulesdir = $(confdir)/modules.avail +modules_DATA = modules.avail/aladdin + +profilesdir = $(confdir)/profiles +profiles_DATA = profiles/opensc + +mprofilesdir = $(confdir)/mapping.profiles +mprofiles_DATA = mapping.profiles/cn mapping.profiles/mail \ + mapping.profiles/subject mapping.profiles/cert diff --git a/etc/control.d/Makefile.am b/etc/control.d/Makefile.am new file mode 100644 index 0000000..c70e93c --- /dev/null +++ b/etc/control.d/Makefile.am @@ -0,0 +1,3 @@ +controldir = $(sysconfdir)/control.d/facilities +control_SCRIPTS = pam-pkcs11-module pam-pkcs11-profile pam-pkcs11-mapping \ + pkcs11-events diff --git a/etc/control.d/pam-pkcs11-mapping.in b/etc/control.d/pam-pkcs11-mapping.in new file mode 100755 index 0000000..b3cee4f --- /dev/null +++ b/etc/control.d/pam-pkcs11-mapping.in @@ -0,0 +1,10 @@ +#!/bin/sh + +. /etc/control.d/functions + +CONFIG="${CONFIG:-@confdir@/pam_pkcs11.conf}" +MAPPING_PROFILES_DIR="${MAPPING_PROFILES_DIR:-@confdir@/mapping.profiles}" + +export PROFILES_DIR="$MAPPING_PROFILES_DIR" +export SUMMARY="${SUMMARY:-PKCS11 username mapping profile selector}" +"${0%/*}/pam-pkcs11-profile" "$@" diff --git a/etc/control.d/pam-pkcs11-module.in b/etc/control.d/pam-pkcs11-module.in new file mode 100755 index 0000000..8efdded --- /dev/null +++ b/etc/control.d/pam-pkcs11-module.in @@ -0,0 +1,202 @@ +#!/bin/sh + +. /etc/control.d/functions + +CONFIG="${CONFIG:-@confdir@/pam_pkcs11.conf}" +CONFIG_MGR="${CONFIG_MGR:-@confdir@/pkcs11_eventmgr.conf}" +MODULES_DIR="${MODULES_DIR:-@confdir@/modules.avail}" + +new_summary "PKCS11 module selector" + +list_modules() { + local conf="${1:-$CONFIG}" + sed -n -e '/^[[:space:]]*pkcs11_module[[:space:]]\+[^{]\+[[:space:]]*{/,/}/ { + /^[[:space:]]*pkcs11_module[[:space:]]\+/ { + s/^[[:space:]]*pkcs11_module[[:space:]]\+//; + s/[[:space:]]*{.*$/:/; + p; + } + /^[[:space:]]*module[[:space:]]*=/ { + s/^[[:space:]]*module[[:space:]]*=[[:space:]]*"\?\([^;]\+\)"\?.*$/-- path:\1/; + p; + } + /^[[:space:]]*description[[:space:]]*=/ { + s/^[[:space:]]*description[[:space:]]*=[[:space:]]*"\?\([^";]\+\)"\?.*$/-- desc:\1/; + p; + } + }' -- "$conf" +} + +for_each_module() { + local modules="$1"; shift + + [ -s "$modules" ] || return 0 + + local func="$1"; shift + local module_name= + local module_path= + local module_desc= + + while read ln; do + case "$ln" in + --\ path:*) + module_path="${ln##*path:}" + ;; + --\ desc:*) + module_desc="${ln##*desc:}" + ;; + *:) + if [ -n "$module_name" ]; then + if [ -z "$module_desc" ]; then + module_desc="$(echo "$module_name" | tr '[:lower:]' '[:upper:]') module" + fi + "$func" "$module_name" "$module_path" "$module_desc" "$@" + fi + module_name="${ln%:}"; module_path=; module_desc= + ;; + esac + done <"$modules" + + if [ -n "$module_name" ]; then + if [ -z "$module_desc" ]; then + module_desc="$(echo "$module_name" | tr '[:lower:]' '[:upper:]') module" + fi + "$func" "$module_name" "$module_path" "$module_desc" "$@" + fi +} + +register_module() { + new_help "$1" "$3" +} + +check_name() { + if [ "$1" = "$4" ]; then + echo "$1" + fi +} + +register_new_module() { + local already="$(for_each_module "$4" check_name "$1")" + if [ -z "$already" ]; then + new_help "$1" "$3" + fi +} + +read_selected() { + sed -n -e '/^[[:space:]]*pam_pkcs11[[:space:]]*{/,$ { + /^[[:space:]]*pam_pkcs11[[:space:]]*{/ d; + /{/,/}/ d; + /}/ q; + /^[[:space:]]*use_pkcs11_module[[:space:]]*=/ { + s/^[[:space:]]*use_pkcs11_module[[:space:]]*=[[:space:]]*//; + s/;.*$//; + p; q; + } + }' -- "$CONFIG" +} + +get_path() { + if [ "$1" = "$4" ]; then + echo "$2" + fi +} + +add_to_config() { + local module="$1"; shift + local newmod="$workdir/newmod" + + sed -e 's/^.*$/ &/' "${MODULES_DIR%/}/$module" >"$newmod" + echo "}" >>"$newmod" + + sed -i -e "/^[[:space:]]*pam_pkcs11[[:space:]]*{/,$ { + /^[[:space:]]*pam_pkcs11[[:space:]]*{/ { p; d } + /{/,/}/ { p; d } + /}/ { + r $newmod + ; d + } + /}/,\$ { p; d } + }" -- "$CONFIG" +} + +select_module() { + local module="$1"; shift + local module_path= + + module_path="$(for_each_module "$workdir/configured" get_path "$module")" + if [ -z "$module_path" ]; then + module_path="$(for_each_module "$workdir/available" get_path "$module")" + if [ -n "$module_path" ]; then + # Add module to the main config + add_to_config "$module" + fi + fi + if [ -z "$module_path" ]; then + echo "ERROR: Module path isn't set!" >&2 + return 1 + fi + + sed -i -e "/^[[:space:]]*pam_pkcs11[[:space:]]*{/,\$ { + /^[[:space:]]*pam_pkcs11[[:space:]]*{/ { p; d; } + /{/,/}/ { p; d } + /}/,\$ { p; d } + /^[[:space:]]*use_pkcs11_module[[:space:]]*=/ { + s/^[[:space:]]*use_pkcs11_module[[:space:]]*=.*\$/ use_pkcs11_module = $module;/; + } + }" -- "$CONFIG" + + # Skip the event manager configuration if its conffile is missing + [ -e "$CONFIG_MGR" ] || return 0 + + module_path="$(echo "$module_path" | sed -e 's,/,\\/,g')" + sed -i -e "/^[[:space:]]*pkcs11_eventmgr[[:space:]]*{/,\$ { + /^[[:space:]]*pkcs11_eventmgr[[:space:]]*{/ { p; d; } + /{/,/}/ { p; d } + /}/,\$ { p; d } + /^[[:space:]]*pkcs11_module[[:space:]]*=/ { + s/^[[:space:]]*pkcs11_module[[:space:]]*=.*\$/\\tpkcs11_module = $module_path;/; + } + }" -- "$CONFIG_MGR" +} + + +## Main + +REQUEST="$*" + +workdir="$(mktemp -d --tmpdir "${0##*/}.XXXX")" +trap "rm -rf \"$workdir\"" EXIT + +list_modules >"$workdir/configured" +if [ -d "$MODULES_DIR" ]; then + ls -1 "$MODULES_DIR" | while read conf; do + case "$conf" in + *~|.*|*.rpmnew|*.rpmold) + ;; + *) + list_modules "${MODULES_DIR%/}/$conf" >>"$workdir/available" + ;; + esac + done +fi + +for_each_module "$workdir/configured" register_module +for_each_module "$workdir/available" register_new_module "$workdir/configured" + +case "$REQUEST" in + help|'help '*) + control_help "${REQUEST#help}" + ;; + list) + control_list + ;; + summary) + control_summary + ;; + status) + read_selected + ;; + *) + select_module "$REQUEST" + ;; +esac diff --git a/etc/control.d/pam-pkcs11-profile.in b/etc/control.d/pam-pkcs11-profile.in new file mode 100755 index 0000000..7215f06 --- /dev/null +++ b/etc/control.d/pam-pkcs11-profile.in @@ -0,0 +1,325 @@ +#!/bin/sh + +. /etc/control.d/functions + +CONFIG="${CONFIG:-@confdir@/pam_pkcs11.conf}" +PROFILES_DIR="${PROFILES_DIR:-@confdir@/profiles}" +SUMMARY="${SUMMARY:-PKCS11 profile selector}" + +new_summary "$SUMMARY" + +read_desc() { + local profile="$1"; shift + sed -n -e '/^#/ { + s/^#[[:space:]]*//; + p; + q + }' -- "$profile" +} + +_read_values() { + local profile="$1"; shift + sed -n -e '/^[[:space:]]*pam_pkcs11[[:space:]]*{/,/}/ { + /^[[:space:]]*pam_pkcs11[[:space:]]*{/ d; + /^[[:space:]]*[^[:space:]{]*[^{]*[^[:space:]{]\+[[:space:]]*{/,/}/ { + /^[[:space:]]*[^[:space:]{]*[^{]*[^[:space:]{]\+[[:space:]]*{/ { + s/^[[:space:]]*\([^[:space:]{]*[^{]*[^[:space:]{]\+\)[[:space:]]*{.*$/ subname = \1;/; + s/"/\\"/g; + s/= \(.*\);$/= "\1";/; + p; d + } + /^[[:space:]]*\([^=[:space:]]\+\)[[:space:]]*=[[:space:]]*[^;]\+;/ { + s/^[[:space:]]*\([^=[:space:]]\+\)[[:space:]]*=[[:space:]]*\(.*[^[:space:]]\)[[:space:]]*;$/ subvalues[subname][\1] = \2;/; + s/"/\\"/g; + s/subvalues\[subname\]\[\(.*\)\] = \(.*\);$/subvalues[subname]["\1"] = "\2";/; + p; d + } + /}/ d + } + /{/,/}/ d; + /^[[:space:]]*[^=[:space:]]\+[[:space:]]*=[[:space:]]*[^;]\+;/ { + s/^[[:space:]]*\([^=[:space:]]\+\)[[:space:]]*=[[:space:]]*\([^;]\+\);.*$/ values[\1] = \2;/; + s/"/\\"/g; + s/values\[\(.*\)\] = \(.*\);$/values["\1"] = "\2";/; + p; d + } + }' -- "$profile" +} + +make_parser_begin() { + local profile="$1"; shift + local name="${profile##*/}"; shift + case "$name" in + *.*) + name="$name" + ;; + *) + name="$name.begin.awk" + ;; + esac + + cat <"$workdir/$name" +BEGIN { + pam_pkcs11 = 0; + subconfig = 0; +EOF + + _read_values "$profile" >>"$workdir/$name" + + cat <>"$workdir/$name" +} +EOF +} + +make_parser() { + local name="${1:-.parser.awk}" + cat <"$workdir/$name" +/^[[:space:]]*#/ { + if (OUT) { print; } + next; +} + +/^[[:space:]]*[^[:space:]{]*[^{]*[^[:space:]{]+[[:space:]]*{/ { + if (\$1 == "pam_pkcs11" && \$2 == "{") { + pam_pkcs11 = 1; + } else if (pam_pkcs11) { + subconfig = 1; + subname = \$1; + for (i = 2; i < NF && \$i != "{"; i++) { + subname = subname " " \$i; + } + } + if (OUT) { print; } + next; +} + +/^[[:space:]]*[^=[:space:]]+[[:space:]]*=[[:space:]]*[^;]+;/ { + value = \$0; + if (CMP) { + sub(/^[^=]+=[[:space:]]*/, "", value); + sub(/[[:space:]]*;([[:space:]#]+.*)?\$/, "", value); + } + if (subconfig) { + if (subname in subvalues && \$1 in subvalues[subname]) { + if (OUT) { + print " " \$1 " = " subvalues[subname][\$1] ";"; + } + if (!CMP || value == subvalues[subname][\$1]) { + delete subvalues[subname][\$1]; + } + if (!CMP) { + existing[subname] = 1; + } + } else { + if (OUT) { print; } + } + } else { + if (\$1 in values) { + if (OUT) { + print " " \$1 " = " values[\$1] ";"; + } + if (!CMP || value == values[\$1]) { + delete values[\$1]; + } + } else { + if (OUT) { print; } + } + } + next; +} + +/}/ { + if (subconfig) { + subconfig = 0; + if (subname in subvalues) { + for (key in subvalues[subname]) { + if (OUT) { + print " " key " = " subvalues[subname][key] ";"; + } + if (!CMP) { + delete subvalues[subname][key]; + } + } + if (!CMP) { + existing[subname] = 1; + } + } + } else if (pam_pkcs11) { + pam_pkcs11 = 0; + for (key in values) { + if (OUT) { + print " " key " = " values[key] ";"; + } + if (!CMP) { + delete values[key]; + } + } + for (subname in subvalues) { + if (!(subname in existing)) { + if (OUT) { + print ""; + print " " subname " {"; + } + for (key in subvalues[subname]) { + if (OUT) { + print " " key " = " subvalues[subname][key] ";"; + } + if (!CMP) { + delete subvalues[subname][key]; + } + } + } + if (!CMP) { + delete subvalues[subname]; + } + } + } + if (OUT) { print; } + next; +} + +{ if (OUT) { print; } } + +END { + value_count = 0; + for (key in values) { value_count++ }; + subvalue_count = 0; + for (subname in subvalues) { + for (key in subvalues[subname]) { + subvalue_count++; + } + } + diff = 0; + if (value_count > 0 || subvalue_count > 0) { + diff = 1; + print "pam_pkcs11 {"; + for (key in values) { + print " " key " = " values[key] ";"; + } + for (subname in subvalues) { + subvalue_count = 0; + for (key in subvalues[subname]) { + subvalue_count++; + } + if (subvalue_count > 0) { + print ""; + print " " subname " {"; + for (key in subvalues[subname]) { + print " " key " = " subvalues[subname][key] ";"; + } + print " }"; + } + } + print "}"; + } + if (diff) { + exit 1; + } +} +EOF +} + +read_current() { + local name= + + make_parser + for_each_profile make_parser_begin + + for f in $workdir/*.begin.awk; do + name="${f##*/}"; name="${name%.begin.awk}" + if awk -f "$f" -f "$workdir/.parser.awk" -v 'CMP=1' "$CONFIG" 1>/dev/null 2>&1; then + echo "$name" + return 0 + fi + done + + echo 'custom' +} + +update() { + local profile="$1"; shift + local name="${profile##*/}" + make_parser + make_parser_begin "$profile" + awk -f "$workdir/$name.begin.awk" -f "$workdir/.parser.awk" \ + -v 'OUT=1' -- "$CONFIG" \ + >"$workdir/.config.updated" && \ + mv "$workdir/.config.updated" "$CONFIG" +} + +use_profile() { + local profile="${PROFILES_DIR%/}/$1"; shift + if [ -e "$profile" ]; then + update "$profile" && \ + use_module "$profile" + else + echo "ERROR: Profile not found: $1" >&2 + return 1 + fi +} + +read_module() { + local profile="$1"; shift + sed -n -e '/^[[:space:]]*use_pkcs11_module[[:space:]]*=/ { + s/^[[:space:]]*use_pkcs11_module[[:space:]]*=[[:space:]]*//; + s/;.*$//; + p; + q + }' -- "$profile" +} + +use_module() { + local module="$(read_module "$1")" + if [ -n "$module" ]; then + if ! control pam-pkcs11-module "$module"; then + echo "Error selecting module: ${module##*/}" >&2 + return 1 + fi + fi +} + +for_each_profile() { + local func="$1"; shift + if [ -d $PROFILES_DIR ]; then + for p in ${PROFILES_DIR%/}/*; do + case "${p##*/}" in + *~|.*|*.rpmnew|*.rpmold) + ;; + *) + "$func" "$p" "$@" + ;; + esac + done + fi +} + +register_profile() { + new_help "${1##*/}" "$(read_desc "$1")" +} + +## Main + +REQUEST="$*" + +workdir="$(mktemp -d --tmpdir "${0##*/}.XXXX")" +trap "rm -rf \"$workdir\"" EXIT + +for_each_profile register_profile + +case "$REQUEST" in + help|'help '*) + control_help "${REQUEST#help}" + ;; + list) + control_list + ;; + summary) + control_summary + ;; + status) + read_current + ;; + *) + use_profile "$REQUEST" + ;; +esac diff --git a/etc/control.d/pkcs11-events.in b/etc/control.d/pkcs11-events.in new file mode 100755 index 0000000..eb3a5a6 --- /dev/null +++ b/etc/control.d/pkcs11-events.in @@ -0,0 +1,209 @@ +#!/bin/sh + +. /etc/control.d/functions +. alterator-service-functions + +CONFIG="${CONFIG:-@confdir@/pkcs11_eventmgr.conf}" + +CA_INSERT="/usr/bin/card-inserted" +CA_REMOVE="/usr/bin/card-removed" +SERVICE="pkcs11-eventmgr" + +new_summary "PKCS11 event actions" +new_help 'other' "User specified or package default actions" +new_help 'disabled' "Event monitring is disabled" +new_help 'card_actions' "Actions handled by the $CA_INSERT and $CA_REMOVE alternatives" + +read_event_action() { + local ev="$1"; shift + sed -n -e "/^[[:space:]]*event[[:space:]]\\+$ev[[:space:]]*{/,/^[[:space:]]*}[#[:space:]]*\$/ { + /^[[:space:]]*action[[:space:]]*=/,/;[[:space:]]*\$/ { + H; + /;[[:space:]]*\$/! d; + g; + s/^[[:space:]]*action[[:space:]]*=[[:space:]]*//; + s/\\n/ /g; + s/\",[[:space:]]\\+\"/\", \"/g; + s/\",[[:space:]]\\+;/\",;/; + s/;[[:space:]]*\$//p; + s/^.*\$//; h; + } + }" -- "$CONFIG" +} + +comment_event_action() { + local ev="$1"; shift + sed -i -e "/^[[:space:]]*event[[:space:]]\\+$ev[[:space:]]*{/,/^[[:space:]]*}[#[:space:]]*\$/ { + /^[[:space:]]*\\(#[[:space:]]*\\)*action[[:space:]]*=/,/;[[:space:]]*\$/ { + /^[[:space:]]*}[#[:space:]]*\$/ { p; d }; + s/^[#[:space:]]*/\\t\\t/; + H; + /;[[:space:]]*\$/! d; + g; + s/^[[:space:]]*\\(#[[:space:]]*\\)*action[[:space:]]*=[[:space:]]*/\\t\\t#action = /; + s/\\n/ /g; + s/\",[[:space:]]\\+\"/\", \"/g; + s/\",[[:space:]]\\+;/\",;/; + x; s/^.*\$//; x; + } + }" -- "$CONFIG" +} + +add_event_action() { + local ev="$1"; shift + local act="$1"; shift + act="$(echo "$act" | sed -e 's,/,\\/,g')" + comment_event_action "$ev" + sed -i -e "/^[[:space:]]*event[[:space:]]\\+$ev[[:space:]]*{/,/^[[:space:]]*}[#[:space:]]*\$/ { + /^[[:space:]]*}[#[:space:]]*\$/ { + s/^.*}\\([^}]*\\)\$/\\t\\taction = \"$act\";\\n\\t}\\1/; + } + }" -- "$CONFIG" +} + +delete_event_action() { + local ev="$1"; shift + comment_event_action "$ev" + sed -i -e "/^[[:space:]]*event[[:space:]]\\+$ev[[:space:]]*{/,/^[[:space:]]*}[#[:space:]]*\$/ { + /^[[:space:]]*\\#action[[:space:]]*=/ { + x; /^.\\+\$/ p; d; + } + }" -- "$CONFIG" +} + +restore_event_action() { + local ev="$1"; shift + comment_event_action "$ev" + sed -i -e "/^[[:space:]]*event[[:space:]]\\+$ev[[:space:]]*{/,/^[[:space:]]*}[#[:space:]]*\$/ { + /^[[:space:]]*\\#action[[:space:]]*=/ { + x; /^.\\+\$/ p; d; + } + /^[[:space:]]*}[#[:space:]]*\$/ { + x; s/#action/action/p; x; + } + }" -- "$CONFIG" +} + +## Main + +REQUEST="$*" + +action_command() { + local act="$1"; shift + act="${act#\"}" + act="${act%%\",*}" + act="${act%\"}" + act="${act%% *}" + echo "$act" +} + +read_oninsert() { + oninsert="$(action_command "$(read_event_action 'card_insert')")" + if [ -z "$oninsert" ]; then + return 1 + elif [ ! -x "$oninsert" ]; then + echo "Warning: $oninsert is configured for 'insert' but isn't available!" >&2 + return 2 + fi +} + +read_onremove() { + onremove="$(action_command "$(read_event_action 'card_remove')")" + if [ -z "$onremove" ]; then + return 1 + elif [ ! -x "$onremove" ]; then + echo "Warning: $onremove is configured for 'remove' but isn't available!" >&2 + return 2 + fi +} + +is_enabled() { + if sd_service_exists "$SERVICE" && \ + ! sd_service_control "$SERVICE" 'is-enabled' + then + return 1 + fi + + if sysv_service_exists "$SERVICE" && \ + ! sysv_service_control "$SERVICE" 'is-enabled' + then + return 1 + fi + + return 0 +} + +ret=0; startstop= + +case "$REQUEST" in + help|'help '*) + control_help "${REQUEST#help}" + ;; + list) + control_list + ;; + summary) + control_summary + ;; + status) + if is_enabled; then + read_oninsert ||: + read_onremove ||: + if [ "$oninsert" = "$CA_INSERT" -a "$onremove" = "$CA_REMOVE" ] + then + echo "card_actions" + else + echo "other" + fi + else + echo "disabled" + fi + ;; + card_actions) + startstop="$SERVICE" + if [ -x "$CA_INSERT" ]; then + if [ "$oninsert" != "$CA_INSERT" ]; then + add_event_action 'card_insert' "$CA_INSERT" + fi + else + echo "Error: $CA_INSERT is not available!" >&2 + ret=1 + fi + if [ -x "$CA_REMOVE" ]; then + if [ "$onremove" != "$CA_REMOVE" ]; then + add_event_action 'card_remove' "$CA_REMOVE" + fi + else + echo "Error: $CA_REMOVE is not available!" >&2 + ret=1 + fi + ;; + other) + startstop="$SERVICE" + if [ "$oninsert" != "$CA_INSERT" ]; then + delete_event_action 'card_insert' + restore_event_action 'card_insert' + read_oninsert || ret=$? + fi + if [ "$onremove" != "$CA_INSERT" ]; then + delete_event_action 'card_remove' + restore_event_action 'card_remove' + read_onremove || ret=$? + fi + ;; + disabled) + service_control "$SERVICE" 'disable' + ;; +esac + +if [ -n "$startstop" ]; then + sact= + if [ $ret -eq 0 ]; then + sact='enable' + else + sact='disable' + fi + for name in $startstop; do + service_control "$name" "$sact" + done +fi diff --git a/etc/mapping.profiles/cert.in b/etc/mapping.profiles/cert.in new file mode 100644 index 0000000..45d745a --- /dev/null +++ b/etc/mapping.profiles/cert.in @@ -0,0 +1,13 @@ +# Map by the certificate itself +# +pam_pkcs11 { + use_mappers = openssh, opensc; + mapper openssh { + debug = false; + module = @libdir@/pam_pkcs11/openssh_mapper.so; + } + mapper opensc { + debug = false; + module = @libdir@/pam_pkcs11/opensc_mapper.so; + } +} diff --git a/etc/mapping.profiles/cn.in b/etc/mapping.profiles/cn.in new file mode 100644 index 0000000..200a9f3 --- /dev/null +++ b/etc/mapping.profiles/cn.in @@ -0,0 +1,12 @@ +# Map the CN (common name) to user name +# +pam_pkcs11 { + use_mappers = cn; + mapper cn { + debug = false; + module = internal; + ignorecase = true; + # mapfile = file://@confdir@/cn_map; + mapfile = "none"; + } +} diff --git a/etc/mapping.profiles/mail.in b/etc/mapping.profiles/mail.in new file mode 100644 index 0000000..640c77c --- /dev/null +++ b/etc/mapping.profiles/mail.in @@ -0,0 +1,16 @@ +# Map the email address to user name +# +pam_pkcs11 { + use_mappers = mail; + mapper mail { + debug = false; + module = internal; + # mapfile = file://@confdir@/mail_mapping; + mapfile = "none"; + # Some certs store email in uppercase. take care on this + ignorecase = true; + # Also check that host matches mx domain + # when using mapfile this feature is ignored + ignoredomain = false; + } +} diff --git a/etc/mapping.profiles/subject.in b/etc/mapping.profiles/subject.in new file mode 100644 index 0000000..a673cd6 --- /dev/null +++ b/etc/mapping.profiles/subject.in @@ -0,0 +1,12 @@ +# Map the cert. subject to user name +# +pam_pkcs11 { + use_mappers = subject; + mapper subject { + debug = false; + module = internal; + ignorecase = false; + # mapfile = file://@confdir@/subject_mapping; + mapfile = "none"; + } +} diff --git a/etc/modules.avail/aladdin.in b/etc/modules.avail/aladdin.in new file mode 100644 index 0000000..b5e6a1e --- /dev/null +++ b/etc/modules.avail/aladdin.in @@ -0,0 +1,10 @@ +# Aladdin eTokenPRO 32 +pkcs11_module etoken { + module = /usr/local/lib/libetpkcs11.so + description = "Aladdin eTokenPRO-32"; + slot_num = 0; + support_threads = true; + ca_dir = @confdir@/cacerts; + crl_dir = @confdir@/crls; + cert_policy = ca,signature; +} diff --git a/etc/pam.d_login.example.in b/etc/pam.d_login.example.in index 35493e8..8edb288 100644 --- a/etc/pam.d_login.example.in +++ b/etc/pam.d_login.example.in @@ -1,7 +1,7 @@ #%PAM-1.0 auth sufficient pam_pkcs11.so nullok try_first_pass \ pkcs11_module=@libdir@/pkcs11/opensc-pkcs11.so \ - ca_dir=/etc/pam_pkcs11 crl_dir=/etc/pam_pkcs11 cert_policy=none + ca_dir=@confdir*/cacerts crl_dir=@confdir@/crls cert_policy=none auth required pam_securetty.so auth required pam_stack.so service=system-auth auth required pam_nologin.so diff --git a/etc/pam_pkcs11.conf.example.in b/etc/pam_pkcs11.conf.example.in index 648ec7a..294515a 100644 --- a/etc/pam_pkcs11.conf.example.in +++ b/etc/pam_pkcs11.conf.example.in @@ -23,6 +23,30 @@ pam_pkcs11 { # previously set (intended for stacking password modules only). use_authtok = false; + # card_only means: + # 1) always get the userid from the certificate. + # 2) don't prompt for the user name if the card is present. + # 3) if the token is present, then we must use the cardAuth mechanism. + card_only = false; + + # wait_for_card means: + # 1) nothing if card_only isn't set + # 2) if logged in, block in pam conversation until the token used for login + # is inserted + # 3) if not logged in, block until a token that could be used for logging in + # is inserted + # right now, logged in means PKC11_LOGIN_TOKEN_NAME is set, + # but we could something else later (like set some per-user state in + # a pam session module keyed off uid) + wait_for_card = false; + + # List of screen saver services. + # This list is only parsed if card_only is set. Basically the screen saver + # will bypass pam_pkcs11 if a token was not used to login (The basic idea is + # you always unlock the screen saver with the same mechanism you used to + # login). + screen_savers = mate-screensaver, gnome-screensaver, kde4-kscreensaver, kscreensaver, xscreensaver; + # Filename of the PKCS #11 module. The default value is "default" use_pkcs11_module = opensc; @@ -54,14 +78,14 @@ pam_pkcs11 { # 1- A directory with openssl hash-links to all certificates # 2- A CA file in PEM (.pem) or ASN1 (.cer) format, # containing all allowed CA certs - # The default value is /etc/pam_pkcs11/cacerts. - ca_dir = /etc/pam_pkcs11/cacerts; + # The default value is @confdir@. + ca_dir = @confdir@/cacerts; # Path to the directory where the local (offline) CRLs are stored. # Same convention as above is applied: you can choose either # hash-link directory or CRL file - # The default value is /etc/pam_pkcs11/crls. - crl_dir = /etc/pam_pkcs11/crls; + # The default value is @confdir@/crls. + crl_dir = @confdir@/crls; # Some pcks#11 libraries can handle multithreading. So # set it to true to properly call C_Initialize() @@ -89,17 +113,6 @@ pam_pkcs11 { token_type = "Smart card"; } - # Aladdin eTokenPRO 32 - pkcs11_module etoken { - module = /usr/local/lib/libetpkcs11.so - description = "Aladdin eTokenPRO-32"; - slot_num = 0; - support_threads = true; - ca_dir = /etc/pam_pkcs11/cacerts; - crl_dir = /etc/pam_pkcs11/crls; - cert_policy = ca,signature; - } - # NSS (Network Security Service) config pkcs11_module nss { nss_dir = /etc/ssl/nssdb; @@ -112,8 +125,8 @@ pam_pkcs11 { description = "Default pkcs#11 module"; slot_num = 0; support_threads = false; - ca_dir = /etc/pam_pkcs11/cacerts; - crl_dir = /etc/pam_pkcs11/crls; + ca_dir = @confdir@/cacerts; + crl_dir = @confdir@/crls; cert_policy = none; } @@ -138,7 +151,7 @@ pam_pkcs11 { # If used null mapper should be the last in the list :-) # Also you should select at least one mapper, otherwise # certificate will not match :-) - use_mappers = digest, cn, pwent, uid, mail, subject, null; + use_mappers = openssh, opensc; # When no absolute path or module info is provided, use this # value as module search path @@ -157,7 +170,7 @@ pam_pkcs11 { # Use one of "cn" , "subject" , "kpn" , "email" , "upn" , "uid" or "serial" cert_item = cn; # Define mapfile if needed, else select "none" - mapfile = file:///etc/pam_pkcs11/generic_mapping; + mapfile = file://@confdir@/generic_mapping; # Decide if use getpwent() to map login use_getpwent = false; } @@ -169,7 +182,7 @@ pam_pkcs11 { # module = @libdir@/pam_pkcs11/subject_mapper.so; module = internal; ignorecase = false; - mapfile = file:///etc/pam_pkcs11/subject_mapping; + mapfile = file://@confdir@/subject_mapping; } # Search public keys from $HOME/.ssh/authorized_keys to match users @@ -255,7 +268,7 @@ pam_pkcs11 { module = internal; # module = @libdir@/pam_pkcs11/cn_mapper.so; ignorecase = true; - # mapfile = file:///etc/pam_pkcs11/cn_map; + # mapfile = file://@confdir@/cn_map; mapfile = "none"; } @@ -266,7 +279,7 @@ pam_pkcs11 { # module = @libdir@/pam_pkcs11/mail_mapper.so; # Declare mapfile or # leave empty "" or "none" to use no map - mapfile = file:///etc/pam_pkcs11/mail_mapping; + mapfile = file://@confdir@/mail_mapping; # Some certs store email in uppercase. take care on this ignorecase = true; # Also check that host matches mx domain @@ -313,7 +326,7 @@ pam_pkcs11 { # Select one of: # "null","md2","md4","md5","sha","sha1","dss","dss1","ripemd160" algorithm = "sha1"; - mapfile = file:///etc/pam_pkcs11/digest_mapping; + mapfile = file://@confdir@/digest_mapping; # mapfile = "none"; } diff --git a/etc/profiles/opensc b/etc/profiles/opensc new file mode 100644 index 0000000..46709cd --- /dev/null +++ b/etc/profiles/opensc @@ -0,0 +1,9 @@ +# OpenSC default +# +pam_pkcs11 { + nullok = true; + use_first_pass = false; + try_first_pass = false; + use_authtok = false; + use_pkcs11_module = opensc; +} diff --git a/etc/system-auth-pkcs11_strict b/etc/system-auth-pkcs11_strict new file mode 100644 index 0000000..1cc6342 --- /dev/null +++ b/etc/system-auth-pkcs11_strict @@ -0,0 +1,11 @@ +#%PAM-1.0 +auth [success=done ignore=ignore default=die] pam_pkcs11.so +auth requisite pam_succeed_if.so user ingroup wheel +auth include system-auth-local + +account include system-auth-local + +password include system-auth-local + +session include system-auth-local +session required pam_mkhomedir.so silent diff --git a/etc/system-auth-use_first_pass-pkcs11_strict b/etc/system-auth-use_first_pass-pkcs11_strict new file mode 100644 index 0000000..201f033 --- /dev/null +++ b/etc/system-auth-use_first_pass-pkcs11_strict @@ -0,0 +1,6 @@ +#%PAM-1.0 +auth [success=done ignore=ignore default=die] pam_pkcs11.so use_first_pass +auth requisite pam_succeed_if.so user ingroup wheel +auth include system-auth-use_first_pass-local + +password include system-auth-use_first_pass-local