From edec1fa69714ed4d97197dc7ed81aeb93f8aebb3 Mon Sep 17 00:00:00 2001 From: Sam Hartman Date: Fri, 1 Oct 2010 17:12:37 +0000 Subject: [PATCH] Initial securid2 support. builds but untested git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24405 dc483132-0cff-0310-8789-dd5450dbe970 --- src/configure.in | 1 + src/plugins/preauth/securid_sam2/Makefile.in | 38 + src/plugins/preauth/securid_sam2/extern.h | 18 + src/plugins/preauth/securid_sam2/securid2.c | 674 ++++++++++++++++++ .../preauth/securid_sam2/securid_sam2.exports | 1 + .../preauth/securid_sam2/securid_sam2_main.c | 274 +++++++ 6 files changed, 1006 insertions(+) create mode 100644 src/plugins/preauth/securid_sam2/Makefile.in create mode 100644 src/plugins/preauth/securid_sam2/extern.h create mode 100644 src/plugins/preauth/securid_sam2/securid2.c create mode 100644 src/plugins/preauth/securid_sam2/securid_sam2.exports create mode 100644 src/plugins/preauth/securid_sam2/securid_sam2_main.c diff --git a/src/configure.in b/src/configure.in index a5ec65cf8..7d59a56d8 100644 --- a/src/configure.in +++ b/src/configure.in @@ -1116,6 +1116,7 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test plugins/kdb/db2/libdb2/test plugins/kdb/hdb plugins/preauth/cksum_body plugins/preauth/encrypted_challenge + plugins/preauth/securid_sam2 plugins/preauth/wpse plugins/authdata/greet plugins/authdata/greet_client diff --git a/src/plugins/preauth/securid_sam2/Makefile.in b/src/plugins/preauth/securid_sam2/Makefile.in new file mode 100644 index 000000000..dd4441456 --- /dev/null +++ b/src/plugins/preauth/securid_sam2/Makefile.in @@ -0,0 +1,38 @@ +mydir=plugins/preauth/securid_sam2 +BUILDTOP=$(REL)..$(S)..$(S).. +KRB5_RUN_ENV = @KRB5_RUN_ENV@ +KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KRB5_CONFIG ; +PROG_LIBPATH=-L$(TOPLIBD) +PROG_RPATH=$(KRB5_LIBDIR) +MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR) +DEFS=@DEFS@ -DARL_SECURID_PREAUTH + +LOCALINCLUDES = -I../../../include/krb5 -I. + +LIBBASE=securid_sam2 +LIBMAJOR=0 +LIBMINOR=0 +SO_EXT=.so +RELDIR=../plugins/preauth/securid_sam2 +# Depends on libk5crypto and libkrb5 +SHLIB_EXPDEPS = \ + $(TOPLIBD)/libk5crypto$(SHLIBEXT) \ + $(TOPLIBD)/libkrb5$(SHLIBEXT) +SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto $(SUPPORT_LIB) $(LIBS) + +SHLIB_DIRS=-L$(TOPLIBD) +SHLIB_RDIRS=$(KRB5_LIBDIR) +STOBJLISTS=OBJS.ST +STLIBOBJS=securid_sam2_main.o securid2.o + +SRCS= $(srcdir)/securid_sam2_main.c $(srcdir)/securid2.c 2 + +all-unix:: all-liblinks +install-unix:: install-libs +clean-unix:: clean-libs clean-libobjs + +clean:: + $(RM) lib$(LIBBASE)$(SO_EXT) + +@libnover_frag@ +@libobj_frag@ diff --git a/src/plugins/preauth/securid_sam2/extern.h b/src/plugins/preauth/securid_sam2/extern.h new file mode 100644 index 000000000..8bea143bb --- /dev/null +++ b/src/plugins/preauth/securid_sam2/extern.h @@ -0,0 +1,18 @@ + krb5_error_code sam_get_db_entry(krb5_context , krb5_principal, + int *, krb5_db_entry **); + +krb5_error_code +securid_make_sam_challenge_2_and_cksum (krb5_context context, + krb5_sam_challenge_2 *sc2, krb5_sam_challenge_2_body *sc2b, + krb5_keyblock *cksum_key); +krb5_error_code get_securid_edata_2(krb5_context context, + krb5_db_entry *client, + krb5_sam_challenge_2_body *sc2b, + krb5_sam_challenge_2 *sc2); + +krb5_error_code verify_securid_data_2(krb5_context context, + krb5_db_entry *client, + krb5_sam_response_2 *sr2, + krb5_enc_tkt_part *enc_tkt_reply, + krb5_pa_data *pa, + krb5_sam_challenge_2 **sc2_out); diff --git a/src/plugins/preauth/securid_sam2/securid2.c b/src/plugins/preauth/securid_sam2/securid2.c new file mode 100644 index 000000000..b7d744e60 --- /dev/null +++ b/src/plugins/preauth/securid_sam2/securid2.c @@ -0,0 +1,674 @@ + +#include "k5-int.h" +#include +#include +#include + + +#include +#include +#include +#include +#include "extern.h" + +#define KRB5_SAM_SECURID_NEXT_CHALLENGE_MAGIC 0x5ec1d000 +struct securid_track_data { + SDI_HANDLE handle; + char state; + char passcode[LENPRNST+1]; + long hostid; +}; + +#define SECURID_STATE_NEW_PIN 1 /* Ask for a new pin */ +#define SECURID_STATE_NEW_PIN_AGAIN 2 /* Ask for new pin again */ +#define SECURID_STATE_NEXT_CODE 3 /* Ask for the next pin code */ +#define SECURID_STATE_INITIAL 4 + + +static char *PASSCODE_message = "SecurID Passcode"; +static char *NEXT_PASSCODE_message = "Next Passcode"; +static char *NEW_PIN_AGAIN_message = "New PIN Again"; +static char PIN_message[64]; /* Max length should be 50 chars */ + +/* krb5_error_code get_securid_key(): + * inputs: context: from KDC process + * client: database entry of client executing + * SecurID SAM preauthentication + * outputs: client_securid_key: pointer to krb5_keyblock + * which is key for the client's SecurID + * database entry. + * returns: 0 on success + * KRB5 error codes otherwise + * + * builds pricipal name with final instance of "SECURID" and + * finds the database entry, decrypts the key out of the database + * and passes the key back to the calling process + */ + +static krb5_error_code get_securid_key(krb5_context context, + krb5_db_entry *client, + krb5_keyblock *client_securid_key) +{ + krb5_db_entry *sam_securid_entry = NULL; + krb5_key_data *client_securid_key_data = NULL; + int sam_type = PA_SAM_TYPE_SECURID; + krb5_error_code retval = 0; + + if (!client_securid_key) return(KRB5_PREAUTH_NO_KEY); + + retval = sam_get_db_entry(context, client->princ, + &sam_type, &sam_securid_entry); + + if (retval) + return(KRB5_PREAUTH_NO_KEY); + + + /* Find key with key_type = salt_type = kvno = -1. This finds the */ + /* latest kvno in the list. */ + + retval = krb5_dbe_find_enctype(context, sam_securid_entry, + -1, -1, -1, &client_securid_key_data); + if (retval) { + krb5_set_error_message(context, retval, + "while getting key from client's SAM SecurID entry"); + goto cleanup; + } + retval = krb5_dbe_decrypt_key_data(context, NULL, + client_securid_key_data, client_securid_key, NULL); + if (retval) { + krb5_set_error_message(context, retval, + "while decrypting key from client's SAM SecurID entry "); + goto cleanup; + } + cleanup: + if (sam_securid_entry) + krb5_db_free_principal(context, sam_securid_entry); + return retval; +} + +krb5_error_code +securid_make_sam_challenge_2_and_cksum (krb5_context context, + krb5_sam_challenge_2 *sc2, krb5_sam_challenge_2_body *sc2b, + krb5_keyblock *cksum_key) +{ + krb5_error_code retval; + krb5_checksum **cksum_array = NULL; + krb5_checksum *cksum = NULL; + krb5_cksumtype cksumtype; + krb5_data *encoded_challenge_body = NULL; + + if (!cksum_key) return(KRB5_PREAUTH_NO_KEY); + if (!sc2 || !sc2b) return(KRB5KDC_ERR_PREAUTH_FAILED); + + retval = encode_krb5_sam_challenge_2_body(sc2b, &encoded_challenge_body); + if (retval || !encoded_challenge_body) { + encoded_challenge_body = NULL; + goto cksum_cleanup; + } + + cksum_array = calloc(2, sizeof(krb5_checksum *)); + if (!cksum_array) { + retval = ENOMEM; + goto cksum_cleanup; + } + + cksum = (krb5_checksum *)k5alloc(sizeof(krb5_checksum), &retval); + if (retval) + goto cksum_cleanup; + cksum_array[0] = cksum; + cksum_array[1] = NULL; + + retval = krb5int_c_mandatory_cksumtype(context, cksum_key->enctype, &cksumtype); + if (retval) goto cksum_cleanup; + + retval = krb5_c_make_checksum(context, cksumtype, + cksum_key, KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM, + encoded_challenge_body, cksum); + if (retval) goto cksum_cleanup; + + sc2->sam_cksum = cksum_array; + sc2->sam_challenge_2_body = *encoded_challenge_body; + return(0); + +cksum_cleanup: + if (encoded_challenge_body) krb5_free_data(context, encoded_challenge_body); + if (cksum_array) krb5_xfree(cksum_array); + if (cksum) krb5_xfree(cksum); + return (retval); +} + + +static krb5_error_code +securid_decrypt_track_data_2(krb5_context context, krb5_db_entry *client, + krb5_data *enc_track_data, krb5_data *output) { + krb5_error_code retval; + krb5_keyblock sam_key; + krb5_enc_data tmp_enc_data; + sam_key.contents = NULL; + + if ((retval = get_securid_key(context, client, + &sam_key) ) != 0) + return(retval); + + tmp_enc_data.ciphertext = *enc_track_data; + tmp_enc_data.enctype = ENCTYPE_UNKNOWN; + tmp_enc_data.kvno = 0; + + output->length = tmp_enc_data.ciphertext.length; + if (output->data) + free(output->data); + output->data = k5alloc(output->length, &retval); + if (retval) goto cleanup; + retval = krb5_c_decrypt(context, &sam_key, + KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0, &tmp_enc_data, output); + cleanup: + krb5_free_keyblock_contents(context, &sam_key); + + if (retval) { + output->length = 0; + if (output->data) + free(output->data); + output->data = NULL; + return(retval); + } + + return(0); +} + +static krb5_error_code +securid_encrypt_track_data_2(krb5_context context, krb5_db_entry *client, + krb5_data *track_data, krb5_data *output) { + krb5_error_code retval; + size_t olen; + krb5_keyblock sam_key; + krb5_enc_data tmp_enc_data; + output->data = NULL; + + if ((retval = get_securid_key(context,client, + &sam_key)) != 0) + return(retval); + + if ((retval = krb5_c_encrypt_length(context, sam_key.enctype, + track_data->length, &olen)) != 0) + goto cleanup; + assert (olen <= 65536); + output->length = olen; + output->data = k5alloc(output->length, &retval); + if (retval) + goto cleanup; + tmp_enc_data.ciphertext = *output; + tmp_enc_data.enctype = sam_key.enctype; + tmp_enc_data.kvno = 0; + + retval = krb5_c_encrypt(context, &sam_key, + KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0, track_data, &tmp_enc_data); + cleanup: + krb5_free_keyblock_contents(context, &sam_key); + + if (retval) { + output->length = 0; + if (output->data) + krb5_xfree(output->data); + output->data = NULL; + return(retval); + } + return(0); +} + + +krb5_error_code get_securid_edata_2(krb5_context context, + krb5_db_entry *client, + krb5_sam_challenge_2_body *sc2b, + krb5_sam_challenge_2 *sc2) +{ + krb5_error_code retval; + krb5_data scratch; + krb5_keyblock client_key; + char *user = NULL; + char *def_user = ""; + struct securid_track_data sid_track_data; + krb5_data tmp_data; + client_key.contents = NULL; + scratch.data = NULL; + sc2b->sam_track_id.data = NULL; + + retval = krb5_unparse_name(context, client->princ, &user); + if (retval) + goto cleanup; + + + sc2b->sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD; + sc2b->sam_type_name.length = 0; + sc2b->sam_challenge_label.length = 0; + sc2b->sam_challenge.length = 0; + sc2b->sam_response_prompt.data = PASSCODE_message; + sc2b->sam_response_prompt.length = + strlen(sc2b->sam_response_prompt.data); + sc2b->sam_pk_for_sad.length = 0; + + sid_track_data.state = SECURID_STATE_INITIAL; + sid_track_data.hostid = gethostid(); + tmp_data.data = (char *) &sid_track_data; + tmp_data.length = sizeof(sid_track_data); + if((retval = securid_encrypt_track_data_2(context, client, + &tmp_data, + &sc2b->sam_track_id)) != 0) { + krb5_set_error_message(context, retval, "While encrypting nonce track data"); + goto cleanup; + } + + + scratch.data = (char *)&sc2b->sam_nonce; + scratch.length = sizeof(sc2b->sam_nonce); + retval = krb5_c_random_make_octets(context, &scratch); + if (retval) { + krb5_set_error_message(context, retval, + "while generating nonce data in get_securid_edata_2 (%s)", + user ? user : def_user); + goto cleanup; + } + + /* Get the client's key */ + if ((retval = get_securid_key(context, client, &client_key)) != 0) { + krb5_set_error_message(context, retval, + "while getting SecurID SAM key in get_securid_edata_2 (%s)", + user ? user : def_user); + goto cleanup; + } + sc2b->sam_etype = client_key.enctype; + + retval = securid_make_sam_challenge_2_and_cksum(context, + sc2, sc2b, &client_key); + if (retval) { + krb5_set_error_message(context, retval, + "while making SAM_CHALLENGE_2 checksum (%s)", + user ? user : def_user); + } + + cleanup: + krb5_free_keyblock_contents(context, &client_key); + if (user) krb5_xfree(user); + if (retval) { + krb5_free_data_contents(context, &sc2b->sam_track_id); + sc2b->sam_track_id.data = NULL; + } + return(retval); +} + +krb5_error_code verify_securid_data_2(krb5_context context, + krb5_db_entry *client, + krb5_sam_response_2 *sr2, + krb5_enc_tkt_part *enc_tkt_reply, + krb5_pa_data *pa, + krb5_sam_challenge_2 **sc2_out) +{ + krb5_error_code retval; + int new_pin = 0; + krb5_key_data *client_key_data = NULL; + krb5_keyblock client_key; + krb5_data scratch; + krb5_enc_sam_response_enc_2 *esre2 = NULL; + struct securid_track_data sid_track_data, *trackp = NULL; + krb5_data tmp_data; + SDI_HANDLE sd_handle = SDI_HANDLE_NONE; + krb5_sam_challenge_2 *sc2p = NULL; + char *cp, *user = NULL; + char *securid_user = NULL; + char passcode[LENPRNST+1]; + char max_pin_len, min_pin_len, alpha_pin; + + memset(&client_key, 0, sizeof(client_key)); + memset(&scratch, 0, sizeof(scratch)); + *sc2_out = NULL; + + if ((retval = krb5_unparse_name(context, client->princ, &user)) !=0 ) { + krb5_set_error_message(context, retval, + "while unparsing client name in verify_securid_data_2"); + return(retval); + } + + if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) || + (sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0)) { + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, + "No preauth data supplied in verify_securid_data_2 (%s)", user); + goto cleanup; + } + + retval = krb5_dbe_find_enctype(context, client, + sr2->sam_enc_nonce_or_sad.enctype, KRB5_KDB_SALTTYPE_NORMAL, + sr2->sam_enc_nonce_or_sad.kvno , &client_key_data); + if (retval) { + krb5_set_error_message(context, retval, + "while getting client key in verify_securid_data_2 (%s)", user); + goto cleanup; + } + + if ((retval = krb5_dbe_decrypt_key_data(context, NULL, + client_key_data, &client_key, NULL)) != 0) { + krb5_set_error_message(context, retval, + "while decrypting client key in verify_securid_data_2 (%s)", + user); + goto cleanup; + } + + scratch.length = sr2->sam_enc_nonce_or_sad.ciphertext.length; + scratch.data = k5alloc(scratch.length, &retval); + if (retval) + goto cleanup; + retval = krb5_c_decrypt(context, &client_key, + KRB5_KEYUSAGE_PA_SAM_RESPONSE, 0, + &sr2->sam_enc_nonce_or_sad, &scratch); + if (retval) { + krb5_set_error_message(context, retval, + "while decrypting SAD in verify_securid_data_2 (%s)", user); + goto cleanup; + } + + retval = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2); + if (retval) { + krb5_set_error_message(context, retval, + "while decoding SAD in verify_securid_data_2 (%s)", user); + esre2 = NULL; + goto cleanup; + } + + if (sr2->sam_nonce != esre2->sam_nonce) { + krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, + "while checking nonce in verify_securid_data_2 (%s)", user); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; + } + + if ((esre2->sam_sad.length == 0) || (esre2->sam_sad.data == NULL)) { + krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, + "No SecurID passcode in verify_securid_data_2 (%s)", user); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; + } + + /* Copy out SAD to null-terminated buffer */ + memset(passcode, 0, sizeof(passcode)); + if (esre2->sam_sad.length > (sizeof(passcode) - 1)) { + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, + "SecurID passcode/PIN too long (%d bytes) in verify_securid_data_2 (%s)", + esre2->sam_sad.length, user); + goto cleanup; + } + memcpy(passcode, esre2->sam_sad.data, esre2->sam_sad.length); + + securid_user = strdup(user); + if (!securid_user) { + retval = ENOMEM; + krb5_set_error_message(context, ENOMEM, + "while copying user name in verify_securid_data_2 (%s)", user); + goto cleanup; + } + if ((cp = strchr(securid_user, '@'))) + *cp = (char)0; + + /* Check for any track_id data that may have state from a previous */ + /* attempt at SecurID authentication */ + + if (sr2->sam_track_id.data && (sr2->sam_track_id.length > 0)) { + krb5_data track_id_data; + memset(&track_id_data, 0, sizeof(track_id_data)); + retval = securid_decrypt_track_data_2(context, client, + &sr2->sam_track_id, &track_id_data); + if (retval) { + krb5_set_error_message(context, retval, + "while decrypting SecurID trackID in verify_securid_data_2 (%s)", + user); + goto cleanup; + } + if (track_id_data.length <= sizeof (struct securid_track_data)) { + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_set_error_message(context, retval, "Length of track data incorrect"); + goto cleanup; + } + trackp = (struct securid_track_data *)track_id_data.data; + + if(trackp->hostid != gethostid()) { + krb5_klog_syslog(LOG_INFO, "Unexpected challenge response"); + retval = KRB5KDC_ERR_DISCARD; + goto cleanup; + } + + switch(trackp->state) { + case SECURID_STATE_INITIAL: + goto initial; + break; + case SECURID_STATE_NEW_PIN_AGAIN: + { + int pin1_len, pin2_len; + + trackp->handle = ntohl(trackp->handle); + pin2_len = strlen(passcode); + pin1_len = strlen(trackp->passcode); + + if ((pin1_len != pin2_len) || + (memcmp(passcode, trackp->passcode, pin1_len) != 0)) { + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_klog_syslog(LOG_INFO, + "New SecurID PIN Failed for user %s: PIN mis-match", + user); + break; + } + retval = SD_Pin(trackp->handle, passcode); + SD_Close(trackp->handle); + if (retval == ACM_NEW_PIN_ACCEPTED) { + enc_tkt_reply->flags|= TKT_FLG_HW_AUTH; + enc_tkt_reply->flags|= TKT_FLG_PRE_AUTH; + krb5_klog_syslog(LOG_INFO, + "SecurID PIN Accepted for %s in verify_securid_data_2", + securid_user); + retval = 0; + } else { + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_klog_syslog(LOG_INFO, + "SecurID PIN Failed for user %s (AceServer returns %d) in verify_securid_data_2", + user, retval); + } + break; + } + case SECURID_STATE_NEW_PIN: { + krb5_sam_challenge_2_body sc2b; + sc2p = k5alloc(sizeof *sc2p, &retval); + if (retval) + goto cleanup; + memset(sc2p, 0, sizeof(*sc2p)); + memset(&sc2b, 0, sizeof(sc2b)); + sc2b.sam_type = PA_SAM_TYPE_SECURID; + sc2b.sam_response_prompt.data = NEW_PIN_AGAIN_message; + sc2b.sam_response_prompt.length = + strlen(sc2b.sam_response_prompt.data); + sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD; + sc2b.sam_etype = client_key.enctype; + + tmp_data.data = (char *)&sc2b.sam_nonce; + tmp_data.length = sizeof(sc2b.sam_nonce); + if ((retval = krb5_c_random_make_octets(context, &tmp_data))) { + krb5_set_error_message(context, retval, + "while making nonce for SecurID new PIN2 SAM_CHALLENGE_2 (%s)", user); + goto cleanup; + } + sid_track_data.state = SECURID_STATE_NEW_PIN_AGAIN; + sid_track_data.handle = trackp->handle; + sid_track_data.hostid = gethostid(); + /* Should we complain if sizes don't work ?? */ + memcpy(sid_track_data.passcode, passcode, + sizeof(sid_track_data.passcode)); + tmp_data.data = (char *)&sid_track_data; + tmp_data.length = sizeof(sid_track_data); + if ((retval = securid_encrypt_track_data_2(context, client, + &tmp_data, &sc2b.sam_track_id))) { + krb5_set_error_message(context, retval, + "while encrypting NEW PIN2 SecurID track data for SAM_CHALLENGE_2 (%s)", + securid_user); + goto cleanup; + } + retval = securid_make_sam_challenge_2_and_cksum(context, sc2p, + &sc2b, &client_key); + if (retval) { + krb5_set_error_message(context, retval, + "while making cksum for SAM_CHALLENGE_2 (new PIN2) (%s)", + securid_user); + goto cleanup; + } + krb5_klog_syslog(LOG_INFO, + "Requesting verification of new PIN for user %s", + securid_user); + *sc2_out = sc2p; + sc2p = NULL; + /*sc2_out may be set even on error path*/ + retval = KRB5KDC_ERR_PREAUTH_REQUIRED; + goto cleanup; + } + case SECURID_STATE_NEXT_CODE: + trackp->handle = ntohl(trackp->handle); + retval = SD_Next(trackp->handle, passcode); + SD_Close(trackp->handle); + if (retval == ACM_OK) { + enc_tkt_reply->flags |= TKT_FLG_HW_AUTH + | TKT_FLG_PRE_AUTH; + + krb5_klog_syslog(LOG_INFO, + "Next SecurID Code Accepted for user %s", securid_user); + retval = 0; + } else { + krb5_klog_syslog(LOG_INFO, + "Next SecurID Code Failed for user %s (AceServer returns %d) in verify_securid_data_2", + user, retval); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + } + break; + } + } else { /* No track data, this is first of N attempts */ +initial: + if ((retval = SD_Init(&sd_handle))) { + krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, + "SD_Init() returns error %d in verify_securid_data_2 (%s)", + retval, securid_user); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; + } + + if((retval = SD_Lock(sd_handle, securid_user)) != ACM_OK) { + SD_Close(sd_handle); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_klog_syslog(LOG_INFO, + "SD_Lock() failed (AceServer returns %d) for %s", + retval, securid_user); + goto cleanup; + } + + retval = SD_Check(sd_handle, passcode, securid_user); + switch (retval) { + case ACM_OK: + SD_Close(sd_handle); + enc_tkt_reply->flags|= TKT_FLG_HW_AUTH; + enc_tkt_reply->flags|= TKT_FLG_PRE_AUTH; + krb5_klog_syslog(LOG_INFO, + "SecurID passcode accepted for user %s", user); + retval = 0; + break; + case ACM_ACCESS_DENIED: + SD_Close(sd_handle); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + krb5_klog_syslog(LOG_INFO, + "AceServer returns Access Denied for user %s (SAM2)",user); + goto cleanup; + case ACM_NEW_PIN_REQUIRED: + new_pin = 1; + /*fall through*/ + case ACM_NEXT_CODE_REQUIRED: { + krb5_sam_challenge_2_body sc2b; + sc2p = k5alloc(sizeof *sc2p, &retval); + if (retval) + goto cleanup; + + memset(sc2p, 0, sizeof(*sc2p)); + memset(&sc2b, 0, sizeof(sc2b)); + + sc2b.sam_type = PA_SAM_TYPE_SECURID; + sc2b.sam_response_prompt.data = NEXT_PASSCODE_message; + sc2b.sam_response_prompt.length = + strlen(sc2b.sam_response_prompt.data); + sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD; + sc2b.sam_etype = client_key.enctype; + if (new_pin) { + if ((AceGetMaxPinLen(sd_handle, &max_pin_len) == ACE_SUCCESS) + && (AceGetMinPinLen(sd_handle, &min_pin_len) == ACE_SUCCESS) + && (AceGetAlphanumeric(sd_handle, &alpha_pin) == ACE_SUCCESS)) + { + sprintf(PIN_message, + "New PIN must contain %d to %d %sdigits", + min_pin_len, max_pin_len, + (alpha_pin == 0) ? "" : "alphanumeric "); + sc2b.sam_challenge_label.data = PIN_message; + sc2b.sam_challenge_label.length = + strlen(sc2b.sam_challenge_label.data); + } else { + sc2b.sam_challenge_label.length = 0; + } + } + + tmp_data.data = (char *)&sc2b.sam_nonce; + tmp_data.length = sizeof(sc2b.sam_nonce); + if ((retval = krb5_c_random_make_octets(context, &tmp_data))) { + krb5_set_error_message(context, retval, + "while making nonce for SecurID SAM_CHALLENGE_2 (%s)", user); + goto cleanup; + } + if (new_pin) + sid_track_data.state = SECURID_STATE_NEW_PIN; + else sid_track_data.state = SECURID_STATE_NEXT_CODE; + sid_track_data.handle = htonl(sd_handle); + sid_track_data.hostid = gethostid(); + tmp_data.data = (char *)&sid_track_data; + tmp_data.length = sizeof(sid_track_data); + if ((retval = securid_encrypt_track_data_2(context, client, + &tmp_data, &sc2b.sam_track_id))) { + krb5_set_error_message(context, retval, + "while encrypting SecurID track data for SAM_CHALLENGE_2 (%s)", + securid_user); + goto cleanup; + } + retval = securid_make_sam_challenge_2_and_cksum(context, sc2p, + &sc2b, &client_key); + if (retval) { + krb5_set_error_message(context, retval, + "while making cksum for SAM_CHALLENGE_2 (%s)", + securid_user); + } + if (new_pin) + krb5_klog_syslog(LOG_INFO, + "New SecurID PIN required for user %s", securid_user); + else krb5_klog_syslog(LOG_INFO, + "Next SecurID passcode required for user %s", securid_user); + *sc2_out = sc2p; + sc2p = NULL; + retval = KRB5KDC_ERR_PREAUTH_FAILED; + /*sc2_out is permitted as an output on error path*/ + goto cleanup; + } + default: + krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED, + "AceServer returns unknown error code %d in verify_securid_data_2\n", retval); + retval = KRB5KDC_ERR_PREAUTH_FAILED; + goto cleanup; + } + } /* no track_id data */ + +cleanup: + if (client_key.contents) krb5_free_keyblock_contents(context, &client_key); + if (scratch.data) krb5_xfree(scratch.data); + if (esre2) krb5_free_enc_sam_response_enc_2(context, esre2); + if (user) krb5_xfree(user); + if (securid_user) krb5_xfree(securid_user); + if (trackp) krb5_xfree(trackp); + if (sc2p) + krb5_free_sam_challenge_2(context, sc2p); + return(retval); +} diff --git a/src/plugins/preauth/securid_sam2/securid_sam2.exports b/src/plugins/preauth/securid_sam2/securid_sam2.exports new file mode 100644 index 000000000..cb26d4722 --- /dev/null +++ b/src/plugins/preauth/securid_sam2/securid_sam2.exports @@ -0,0 +1 @@ +preauthentication_server_1 diff --git a/src/plugins/preauth/securid_sam2/securid_sam2_main.c b/src/plugins/preauth/securid_sam2/securid_sam2_main.c new file mode 100644 index 000000000..f61414d54 --- /dev/null +++ b/src/plugins/preauth/securid_sam2/securid_sam2_main.c @@ -0,0 +1,274 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * plugins/preauth/encrypted_challenge/encrypted_challenge.c + * + * Copyright (C) 2009, 2010 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * Copyright (c) 2002 Naval Research Laboratory (NRL/CCS) + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the software, + * derivative works or modified versions, and any portions thereof. + * + * NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND + * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER + * RESULTING FROM THE USE OF THIS SOFTWARE. + * + */ + +#include +#include +#include +#include "extern.h" + + +static struct { + char* name; + int sam_type; +} *sam_ptr, sam_inst_map[] = { + { "SECURID", PA_SAM_TYPE_SECURID, }, + { "GRAIL", PA_SAM_TYPE_GRAIL, }, + { 0, 0 }, +}; + + krb5_error_code sam_get_db_entry( + krb5_context context, + krb5_principal client, + int *sam_type, + struct _krb5_db_entry_new **db_entry) +{ + struct _krb5_db_entry_new *assoc = NULL; + krb5_principal newp = NULL; + int probeslot; + void *ptr = NULL; + krb5_error_code retval; + if (db_entry) + *db_entry = NULL; + retval = krb5_copy_principal(context, client, &newp); + if (retval) { + krb5_set_error_message(context, retval, "copying client name for preauth probe"); + return retval; + } + + probeslot = krb5_princ_size(context, newp)++; + ptr = + realloc(krb5_princ_name(context, newp), + krb5_princ_size(context, newp) * sizeof(krb5_data)); + if (ptr == NULL) { + retval = ENOMEM; + goto cleanup; + } else krb5_princ_name(context, newp) = ptr; + + for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) { + if (*sam_type && (*sam_type != sam_ptr->sam_type)) continue; + + krb5_princ_component(context,newp,probeslot)->data = sam_ptr->name; + krb5_princ_component(context,newp,probeslot)->length = + strlen(sam_ptr->name); + retval = krb5_db_get_principal(context, newp, 0, &assoc); + if(!retval ) + break; + } +cleanup: + if (ptr) { + krb5_princ_component(context,newp,probeslot)->data = 0; + krb5_princ_component(context,newp,probeslot)->length = 0; + krb5_free_principal(context, newp); + } + if (probeslot) + krb5_princ_size(context, newp)--; + if (retval) + return retval; + if (sam_ptr->sam_type) { + /* Found entry of type sam_ptr->sam_type */ + if (sam_type) *sam_type = sam_ptr->sam_type; + if (db_entry) *db_entry = assoc; + else krb5_db_free_principal( context, assoc); + return(0); + } else { + return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + } +} + +static krb5_error_code +kdc_include_padata(krb5_context context, krb5_kdc_req *request, + struct _krb5_db_entry_new *client, + struct _krb5_db_entry_new *server, + preauth_get_entry_data_proc get_entry_proc, + void *pa_module_context, krb5_pa_data *pa_data) +{ + krb5_error_code retval; + krb5_sam_challenge_2 sc2; + krb5_sam_challenge_2_body sc2b; + int sam_type = 0; /* unknown */ + krb5_db_entry *sam_db_entry = NULL; + krb5_data *encoded_challenge = NULL; + memset(&sc2, 0, sizeof(sc2)); + memset(&sc2b, 0, sizeof(sc2b)); + sc2b.magic = KV5M_SAM_CHALLENGE_2; + sc2b.sam_type = sam_type; + + retval = sam_get_db_entry(context, client->princ, &sam_type, + &sam_db_entry); + if (retval) return (retval); + + if (sam_type == 0) { + retval = KRB5_PREAUTH_BAD_TYPE; + goto cleanup; + } + + /* Defer getting the key for the SAM principal associated with the */ + /* client until the mechanism-specific code. The mechanism may want */ + /* to get a specific keytype */ + + + switch (sam_type) { +#ifdef ARL_SECURID_PREAUTH + case PA_SAM_TYPE_SECURID: + if ((retval = get_securid_edata_2(context, client, &sc2b, &sc2))) + goto cleanup; + + retval = encode_krb5_sam_challenge_2(&sc2, &encoded_challenge); + if (retval) { + krb5_set_error_message(context, retval,"while encoding SECURID SAM_CHALLENGE_2"); + goto cleanup; + } + + pa_data->magic = KV5M_PA_DATA; + pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE_2; + pa_data->contents = (krb5_octet *) encoded_challenge->data; + pa_data->length = encoded_challenge->length; + encoded_challenge->data = NULL; + + retval = 0; + break; +#endif /* ARL_SECURID_PREAUTH */ + default: + retval = KRB5_PREAUTH_BAD_TYPE; + goto cleanup; + } + +cleanup: + krb5_free_data( context, encoded_challenge); + if (sam_db_entry) + krb5_db_free_principal(context, sam_db_entry); + return (retval); +} + +static krb5_error_code +kdc_verify_preauth(krb5_context context, struct _krb5_db_entry_new *client, + krb5_data *req_pkt, krb5_kdc_req *request, + krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa_data, + preauth_get_entry_data_proc get_entry_proc, + void *pa_module_context, void **opaque, + krb5_data **e_data, krb5_authdata ***authz_data) +{ + krb5_error_code retval; + krb5_sam_response_2 *sr2 = NULL; + krb5_data scratch, *scratch2; + char *client_name = NULL; + krb5_sam_challenge_2 *out_sc2 = NULL; + + scratch.data = (char *) pa_data->contents; + scratch.length = pa_data->length; + + retval = krb5_unparse_name(context, client->princ, &client_name); + if (retval) + goto cleanup; + + retval = decode_krb5_sam_response_2(&scratch, &sr2); + if (retval) { + krb5_set_error_message(context, retval, + "while decoding SAM_RESPONSE_2 in verify_sam_response_2"); + sr2 = NULL; + goto cleanup; + } + + switch (sr2->sam_type) { +#ifdef ARL_SECURID_PREAUTH + case PA_SAM_TYPE_SECURID: + retval = verify_securid_data_2(context, client, sr2, + enc_tkt_reply, pa_data, &out_sc2); + if (retval) goto cleanup; + break; +#endif /* ARL_SECURID_PREAUTH */ + default: + retval = KRB5_PREAUTH_BAD_TYPE; + krb5_set_error_message(context , retval, "while verifying SAM 2 data"); + break; + } + + /* It is up to the method-specific verify routine to set the ticket flags */ + /* to indicate TKT_FLG_HW_AUTH and/or TKT_FLG_PRE_AUTH. Some methods */ + /* may require more than one round of dialog with the client and must */ + /* return successfully from their verify routine. If does not set the */ + /* TGT flags, the required_preauth conditions will not be met and it will */ + /* try again to get enough preauth data from the client. Do not set TGT */ + /* flags here. */ +cleanup: + /*Note that e_data is an output even in error conditions.*/ + if (out_sc2) { + krb5_pa_data pa_out; + krb5_pa_data *pa_array[2]; + pa_array[0] = &pa_out; + pa_array[1] = NULL; + pa_out.pa_type = KRB5_PADATA_SAM_CHALLENGE_2; + retval = encode_krb5_sam_challenge_2(out_sc2, &scratch2); + krb5_free_sam_challenge_2(context, out_sc2); + if (retval) + goto encode_error; + pa_out.contents = (krb5_octet *) scratch2->data; + pa_out.length = scratch2->length; + retval = encode_krb5_padata_sequence(pa_array, e_data); + krb5_free_data(context, scratch2); + } +encode_error: if (sr2) + krb5_free_sam_response_2(context, sr2); + if (client_name) +free(client_name); + + return(retval); +} + + +static int +kdc_preauth_flags(krb5_context context, krb5_preauthtype patype) +{ + return 0; +} + +krb5_preauthtype supported_pa_types[] = { + KRB5_PADATA_SAM_RESPONSE_2, 0}; + +struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = { + "SAM2", + &supported_pa_types[0], + NULL, + NULL, + kdc_preauth_flags, + kdc_include_padata, + kdc_verify_preauth, + NULL, + NULL +}; -- 2.26.2