snmpksm.c
上传用户:wxp200602
上传日期:2007-10-30
资源大小:4028k
文件大小:57k
- /*
- * snmpksm.c
- *
- * This code implements the Kerberos Security Model (KSM) for SNMP.
- *
- * Security number - 2066432
- */
- #include <net-snmp/net-snmp-config.h>
- #include <sys/types.h>
- #if HAVE_WINSOCK_H
- #include <winsock.h>
- #endif
- #include <stdio.h>
- #ifdef HAVE_STDLIB_H
- #include <stdlib.h>
- #endif
- #if TIME_WITH_SYS_TIME
- # ifdef WIN32
- # include <sys/timeb.h>
- # else
- # include <sys/time.h>
- # endif
- # include <time.h>
- #else
- # if HAVE_SYS_TIME_H
- # include <sys/time.h>
- # else
- # include <time.h>
- # endif
- #endif
- #if HAVE_STRING_H
- #include <string.h>
- #else
- #include <strings.h>
- #endif
- #ifdef HAVE_NETINET_IN_H
- #include <netinet/in.h>
- #endif
- #include <errno.h>
- #if HAVE_DMALLOC_H
- #include <dmalloc.h>
- #endif
- #ifdef HEIMDAL
- #ifndef MIT_NEW_CRYPTO
- #define OLD_HEIMDAL
- #endif /* ! MIT_NEW_CRYPTO */
- #endif /* HEIMDAL */
- #ifdef HEIMDAL
- #define oid heimdal_oid_renamed
- #endif /* HEIMDAL */
- #include <krb5.h>
- #include <com_err.h>
- #ifdef HEIMDAL
- #undef oid
- #endif /* HEIMDAL */
- #ifdef HEIMDAL
- #define CHECKSUM_TYPE(x) (x)->cksumtype
- #define CHECKSUM_CONTENTS(x) ((char *)((x)->checksum.data))
- #define CHECKSUM_LENGTH(x) (x)->checksum.length
- #define TICKET_CLIENT(x) (x)->client
- #else /* HEIMDAL */
- #define CHECKSUM_TYPE(x) (x)->checksum_type
- #define CHECKSUM_CONTENTS(x) (x)->contents
- #define CHECKSUM_LENGTH(x) (x)->length
- #define TICKET_CLIENT(x) (x)->enc_part2->client
- #endif /* HEIMDAL */
- #include <net-snmp/output_api.h>
- #include <net-snmp/config_api.h>
- #include <net-snmp/utilities.h>
- #include <net-snmp/library/asn1.h>
- #include <net-snmp/library/snmp_api.h>
- #include <net-snmp/library/callback.h>
- #include <net-snmp/library/keytools.h>
- #include <net-snmp/library/snmpv3.h>
- #include <net-snmp/library/lcd_time.h>
- #include <net-snmp/library/scapi.h>
- #include <net-snmp/library/callback.h>
- #include <net-snmp/library/snmp_secmod.h>
- #include <net-snmp/library/snmpksm.h>
- static krb5_context kcontext = NULL;
- static krb5_rcache rcache = NULL;
- static krb5_keytab keytab = NULL;
- static int keytab_setup = 0;
- static const char *service_name = NULL;
- static int ksm_session_init(netsnmp_session *);
- static void ksm_free_state_ref(void *);
- static int ksm_free_pdu(netsnmp_pdu *);
- static int ksm_clone_pdu(netsnmp_pdu *, netsnmp_pdu *);
- static int ksm_insert_cache(long, krb5_auth_context, u_char *,
- size_t);
- static void ksm_decrement_ref_count(long);
- static void ksm_increment_ref_count(long);
- static struct ksm_cache_entry *ksm_get_cache(long);
- #define HASHSIZE 64
- /*
- * Our information stored for the response PDU.
- */
- struct ksm_secStateRef {
- krb5_auth_context auth_context;
- krb5_cksumtype cksumtype;
- };
- /*
- * A KSM outgoing pdu cache entry
- */
- struct ksm_cache_entry {
- long msgid;
- int refcount;
- krb5_auth_context auth_context;
- u_char *secName;
- size_t secNameLen;
- struct ksm_cache_entry *next;
- };
- /*
- * Poor man's hash table
- */
- static struct ksm_cache_entry *ksm_hash_table[HASHSIZE];
- /*
- * Stuff to deal with config values
- * Note the conditionals that wrap these--i don't know if these are
- * needed, since i don't know how library initialization and callbacks
- * and stuff work
- */
- static int
- init_snmpksm_post_config(int majorid, int minorid, void *serverarg,
- void *clientarg)
- {
- if (kcontext == NULL) {
- /* not reached, i'd imagine */
- return SNMPERR_KRB5;
- }
- if (service_name == NULL) {
- /* always reached, i'd imagine */
- char *c = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
- NETSNMP_DS_LIB_KSM_SERVICE_NAME);
- if (c != NULL) {
- service_name = c;
- }
- else {
- service_name = "host";
- }
- }
- if (keytab_setup == 0) {
- /* always reached, i'd imagine */
- char *c = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
- NETSNMP_DS_LIB_KSM_KEYTAB);
- if (c) {
- krb5_error_code retval;
- DEBUGMSGTL(("ksm", "Using keytab %sn", c));
- retval = krb5_kt_resolve(kcontext, c, &keytab);
- if (retval) {
- DEBUGMSGTL(("ksm", "krb5_kt_resolve("%s") failed. KSM "
- "config callback failingn", error_message(retval)));
- return SNMPERR_KRB5;
- }
- }
- else {
- DEBUGMSGTL(("ksm", "Using default keytabn", c));
- }
- keytab_setup = 1;
- }
- return SNMPERR_SUCCESS;
- }
- /*
- * Initialize all of the state required for Kerberos (right now, just call
- * krb5_init_context).
- */
- void
- init_ksm(void)
- {
- krb5_error_code retval;
- struct snmp_secmod_def *def;
- int i;
- netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defKSMKeytab",
- NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_KSM_KEYTAB);
- netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defKSMServiceName",
- NETSNMP_DS_LIBRARY_ID,
- NETSNMP_DS_LIB_KSM_SERVICE_NAME);
- snmp_register_callback(SNMP_CALLBACK_LIBRARY,
- SNMP_CALLBACK_POST_READ_CONFIG,
- init_snmpksm_post_config, NULL);
- if (kcontext == NULL) {
- retval = krb5_init_context(&kcontext);
- if (retval) {
- DEBUGMSGTL(("ksm", "krb5_init_context failed (%s), not "
- "registering KSMn", error_message(retval)));
- return;
- }
- }
- for (i = 0; i < HASHSIZE; i++)
- ksm_hash_table[i] = NULL;
- def = SNMP_MALLOC_STRUCT(snmp_secmod_def);
- if (!def) {
- DEBUGMSGTL(("ksm", "Unable to malloc snmp_secmod struct, not "
- "registering KSMn"));
- return;
- }
- def->encode_reverse = ksm_rgenerate_out_msg;
- def->decode = ksm_process_in_msg;
- def->session_open = ksm_session_init;
- def->pdu_free_state_ref = ksm_free_state_ref;
- def->pdu_free = ksm_free_pdu;
- def->pdu_clone = ksm_clone_pdu;
- register_sec_mod(2066432, "ksm", def);
- }
- /*
- * These routines implement a simple cache for information we need to
- * process responses. When we send out a request, it contains an AP_REQ;
- * we get back an AP_REP, and we need the authorization context from the
- * AP_REQ to decrypt the AP_REP. But because right now there's nothing
- * that gets preserved across calls to rgenerate_out_msg to process_in_msg,
- * we cache these internally based on the message ID (we also cache the
- * passed-in security name, for reasons that are mostly stupid).
- */
- static int
- ksm_insert_cache(long msgid, krb5_auth_context auth_context,
- u_char * secName, size_t secNameLen)
- {
- struct ksm_cache_entry *entry;
- int bucket;
- int retval;
- entry = SNMP_MALLOC_STRUCT(ksm_cache_entry);
- if (!entry)
- return SNMPERR_MALLOC;
- entry->msgid = msgid;
- entry->auth_context = auth_context;
- entry->refcount = 1;
- retval = memdup(&entry->secName, secName, secNameLen);
- if (retval != SNMPERR_SUCCESS) {
- free(entry);
- return retval;
- }
- entry->secNameLen = secNameLen;
- bucket = msgid % HASHSIZE;
- entry->next = ksm_hash_table[bucket];
- ksm_hash_table[bucket] = entry;
- return SNMPERR_SUCCESS;
- }
- static struct ksm_cache_entry *
- ksm_get_cache(long msgid)
- {
- struct ksm_cache_entry *entry;
- int bucket;
- bucket = msgid % HASHSIZE;
- for (entry = ksm_hash_table[bucket]; entry != NULL;
- entry = entry->next)
- if (entry->msgid == msgid)
- return entry;
- return NULL;
- }
- static void
- ksm_decrement_ref_count(long msgid)
- {
- struct ksm_cache_entry *entry, *entry1;
- int bucket;
- bucket = msgid % HASHSIZE;
- if (ksm_hash_table[bucket] && ksm_hash_table[bucket]->msgid == msgid) {
- entry = ksm_hash_table[bucket];
- /*
- * If the reference count is zero, then free it
- */
- if (--entry->refcount <= 0) {
- DEBUGMSGTL(("ksm", "Freeing entry for msgid %ldn", msgid));
- krb5_auth_con_free(kcontext, entry->auth_context);
- free(entry->secName);
- ksm_hash_table[bucket] = entry->next;
- free(entry);
- }
- return;
- } else if (ksm_hash_table[bucket])
- for (entry1 = ksm_hash_table[bucket], entry = entry1->next;
- entry != NULL; entry1 = entry, entry = entry->next)
- if (entry->msgid == msgid) {
- if (--entry->refcount <= 0) {
- DEBUGMSGTL(("ksm", "Freeing entry for msgid %ldn",
- msgid));
- krb5_auth_con_free(kcontext, entry->auth_context);
- free(entry->secName);
- entry1->next = entry->next;
- free(entry);
- }
- return;
- }
- DEBUGMSGTL(("ksm",
- "KSM: Unable to decrement cache entry for msgid %ld.n",
- msgid));
- }
- static void
- ksm_increment_ref_count(long msgid)
- {
- struct ksm_cache_entry *entry = ksm_get_cache(msgid);
- if (!entry) {
- DEBUGMSGTL(("ksm", "Unable to find cache entry for msgid %ld "
- "for incrementn", msgid));
- return;
- }
- entry->refcount++;
- }
- /*
- * Initialize specific session information (right now, just set up things to
- * not do an engineID probe)
- */
- static int
- ksm_session_init(netsnmp_session * sess)
- {
- DEBUGMSGTL(("ksm",
- "KSM: Reached our session initialization callbackn"));
- sess->flags |= SNMP_FLAGS_DONT_PROBE;
- return SNMPERR_SUCCESS;
- }
- /*
- * Free our state information (this is only done on the agent side)
- */
- static void
- ksm_free_state_ref(void *ptr)
- {
- struct ksm_secStateRef *ref = (struct ksm_secStateRef *) ptr;
- DEBUGMSGTL(("ksm", "KSM: Freeing state referencen"));
- krb5_auth_con_free(kcontext, ref->auth_context);
- free(ref);
- }
- /*
- * This is called when the PDU is freed; this will decrement reference counts
- * for entries in our state cache.
- */
- static int
- ksm_free_pdu(netsnmp_pdu *pdu)
- {
- ksm_decrement_ref_count(pdu->msgid);
- DEBUGMSGTL(("ksm", "Decrementing cache entry for PDU msgid %ldn",
- pdu->msgid));
- return SNMPERR_SUCCESS;
- }
- /*
- * This is called when a PDU is cloned (to increase reference counts)
- */
- static int
- ksm_clone_pdu(netsnmp_pdu *pdu, netsnmp_pdu *pdu2)
- {
- ksm_increment_ref_count(pdu->msgid);
- DEBUGMSGTL(("ksm", "Incrementing cache entry for PDU msgid %ldn",
- pdu->msgid));
- return SNMPERR_SUCCESS;
- }
- /****************************************************************************
- *
- * ksm_generate_out_msg
- *
- * Parameters:
- * (See list below...)
- *
- * Returns:
- * SNMPERR_GENERIC On success.
- * SNMPERR_KRB5
- * ... and others
- *
- *
- * Generate an outgoing message.
- *
- ****************************************************************************/
- int
- ksm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms)
- {
- krb5_auth_context auth_context = NULL;
- krb5_error_code retcode;
- krb5_ccache cc = NULL;
- int retval = SNMPERR_SUCCESS;
- krb5_data outdata, ivector;
- krb5_keyblock *subkey = NULL;
- #ifdef MIT_NEW_CRYPTO
- krb5_data input;
- krb5_enc_data output;
- unsigned int numcksumtypes;
- krb5_cksumtype *cksumtype_array;
- #elif defined OLD_HEIMDAL /* MIT_NEW_CRYPTO */
- krb5_crypto heim_crypto = NULL;
- #else /* MIT_NEW_CRYPTO */
- krb5_encrypt_block eblock;
- #endif /* MIT_NEW_CRYPTO */
- size_t blocksize, encrypted_length;
- unsigned char *encrypted_data = NULL;
- int zero = 0, i;
- u_char *cksum_pointer, *endp = *parms->wholeMsg;
- krb5_cksumtype cksumtype;
- krb5_checksum pdu_checksum;
- u_char **wholeMsg = parms->wholeMsg;
- size_t *offset = parms->wholeMsgOffset, seq_offset;
- struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *)
- parms->secStateRef;
- #ifdef OLD_HEIMDAL
- krb5_data encrypted_scoped_pdu;
- #endif /* OLD_HEIMDAL */
- int rc;
- char *colon = NULL;
- DEBUGMSGTL(("ksm", "Starting KSM processingn"));
- outdata.length = 0;
- outdata.data = NULL;
- ivector.length = 0;
- ivector.data = NULL;
- CHECKSUM_CONTENTS(&pdu_checksum) = NULL;
- if (!ksm_state) {
- /*
- * If we've got a port number as part of the "peername", then
- * suppress this (temporarily) while we build the credential info.
- * XXX - what about "udp:host" style addresses?
- */
- colon = strrchr(params->session->peername, ':');
- if (colon != NULL) {
- *colon=' ';
- }
- /*
- * If we don't have a ksm_state, then we're a request. Get a
- * credential cache and build a ap_req.
- */
- retcode = krb5_cc_default(kcontext, &cc);
- if (retcode) {
- DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- DEBUGMSGTL(("ksm", "KSM: Set credential cache successfullyn"));
- /*
- * This seems odd, since we don't need this until later (or earlier,
- * depending on how you look at it), but because the most likely
- * errors are Kerberos at this point, I'll get this now to save
- * time not encoding the rest of the packet.
- *
- * Also, we need the subkey to encrypt the PDU (if required).
- */
- retcode =
- krb5_mk_req(kcontext, &auth_context,
- AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
- (char *) service_name, parms->session->peername, NULL,
- cc, &outdata);
- if (colon != NULL)
- *colon=':';
- if (retcode) {
- DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully for "%s/%s" "
- "(may not be actual ticket sname)n", service_name,
- parms->session->peername));
- } else {
- /*
- * Grab the auth_context from our security state reference
- */
- auth_context = ksm_state->auth_context;
- /*
- * Bundle up an AP_REP. Note that we do this only when we
- * have a security state reference (which means we're in an agent
- * and we're sending a response).
- */
- DEBUGMSGTL(("ksm", "KSM: Starting reply processing.n"));
- retcode = krb5_mk_rep(kcontext, auth_context, &outdata);
- if (retcode) {
- DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()n"));
- }
- /*
- * If we have to encrypt the PDU, do that now
- */
- if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
- DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.n"));
- /*
- * It's weird -
- *
- * If we're on the manager, it's a local subkey (because that's in
- * our AP_REQ)
- *
- * If we're on the agent, it's a remote subkey (because that comes
- * FROM the received AP_REQ).
- */
- if (ksm_state)
- retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
- &subkey);
- else
- retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
- &subkey);
- if (retcode) {
- DEBUGMSGTL(("ksm",
- "KSM: krb5_auth_con_getlocalsubkey failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- /*
- * Note that here we need to handle different things between the
- * old and new crypto APIs. First, we need to get the final encrypted
- * length of the PDU.
- */
- #ifdef MIT_NEW_CRYPTO
- retcode = krb5_c_encrypt_length(kcontext, subkey->enctype,
- parms->scopedPduLen,
- &encrypted_length);
- if (retcode) {
- DEBUGMSGTL(("ksm",
- "Encryption length calculation failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- #elif defined OLD_HEIMDAL
- retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto);
- if (retcode) {
- DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- encrypted_length = krb5_get_wrapped_length(kcontext, heim_crypto,
- parms->scopedPduLen);
- #else /* MIT_NEW_CRYPTO */
- krb5_use_enctype(kcontext, &eblock, subkey->enctype);
- retcode = krb5_process_key(kcontext, &eblock, subkey);
- if (retcode) {
- DEBUGMSGTL(("ksm", "krb5_process_key failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- encrypted_length = krb5_encrypt_size(parms->scopedPduLen,
- eblock.crypto_entry);
- #endif /* MIT_NEW_CRYPTO */
- #ifndef OLD_HEIMDAL /* since heimdal allocs the space for us */
- encrypted_data = malloc(encrypted_length);
- if (!encrypted_data) {
- DEBUGMSGTL(("ksm",
- "KSM: Unable to malloc %d bytes for encrypt "
- "buffer: %sn", parms->scopedPduLen,
- strerror(errno)));
- retval = SNMPERR_MALLOC;
- #ifndef MIT_NEW_CRYPTO
- krb5_finish_key(kcontext, &eblock);
- #endif /* ! MIT_NEW_CRYPTO */
- goto error;
- }
- #endif /* ! OLD_HEIMDAL */
- /*
- * We need to set up a blank initialization vector for the encryption.
- * Use a block of all zero's (which is dependent on the block size
- * of the encryption method).
- */
- #ifdef MIT_NEW_CRYPTO
- retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);
- if (retcode) {
- DEBUGMSGTL(("ksm",
- "Unable to determine crypto block size: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- #elif defined (OLD_HEIMDAL) /* MIT_NEW_CRYPTO */
- #else /* MIT_NEW_CRYPTO */
- blocksize =
- krb5_enctype_array[subkey->enctype]->system->block_length;
- #endif /* MIT_NEW_CRYPTO */
- #ifndef OLD_HEIMDAL /* since allocs the space for us */
- ivector.data = malloc(blocksize);
- if (!ivector.data) {
- DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivectorn",
- blocksize));
- retval = SNMPERR_MALLOC;
- goto error;
- }
- ivector.length = blocksize;
- memset(ivector.data, 0, blocksize);
- #endif /* OLD_HEIMDAL */
- /*
- * Finally! Do the encryption!
- */
- #ifdef MIT_NEW_CRYPTO
- input.data = (char *) parms->scopedPdu;
- input.length = parms->scopedPduLen;
- output.ciphertext.data = (char *) encrypted_data;
- output.ciphertext.length = encrypted_length;
- retcode =
- krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
- &ivector, &input, &output);
- #elif defined OLD_HEIMDAL /* MIT_NEW_CRYPTO */
- krb5_data_zero(&encrypted_scoped_pdu);
- retcode = krb5_encrypt(kcontext, heim_crypto, KSM_KEY_USAGE_ENCRYPTION,
- parms->scopedPdu, parms->scopedPduLen,
- &encrypted_scoped_pdu);
- if (retcode == 0) {
- encrypted_length = encrypted_scoped_pdu.length;
- encrypted_data = encrypted_scoped_pdu.data;
- krb5_data_zero(&encrypted_scoped_pdu);
- }
- #else /* MIT_NEW_CRYPTO */
- retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu,
- (krb5_pointer) encrypted_data,
- parms->scopedPduLen, &eblock, ivector.data);
- krb5_finish_key(kcontext, &eblock);
- #endif /* MIT_NEW_CRYPTO */
- if (retcode) {
- DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- snmp_set_detail(error_message(retcode));
- goto error;
- }
- *offset = 0;
- rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
- offset, 1,
- (u_char) (ASN_UNIVERSAL |
- ASN_PRIMITIVE |
- ASN_OCTET_STR),
- encrypted_data,
- encrypted_length);
- if (rc == 0) {
- DEBUGMSGTL(("ksm", "Building encrypted payload failed.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- DEBUGMSGTL(("ksm", "KSM: Encryption complete.n"));
- } else {
- /*
- * Plaintext PDU (not encrypted)
- */
- if (*parms->wholeMsgLen < parms->scopedPduLen) {
- DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- }
- /*
- * Start encoding the msgSecurityParameters
- *
- * For now, use 0 for the response hint
- */
- DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payloadn"));
- seq_offset = *offset;
- rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
- offset, 1,
- (u_char) (ASN_UNIVERSAL |
- ASN_PRIMITIVE |
- ASN_INTEGER),
- (long *) &zero, sizeof(zero));
- if (rc == 0) {
- DEBUGMSGTL(("ksm", "Building ksm security parameters failed.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
- offset, 1,
- (u_char) (ASN_UNIVERSAL |
- ASN_PRIMITIVE |
- ASN_OCTET_STR),
- (u_char *) outdata.data,
- outdata.length);
- if (rc == 0) {
- DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- /*
- * If we didn't encrypt the packet, we haven't yet got the subkey.
- * Get that now.
- */
- if (!subkey) {
- if (ksm_state)
- retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
- &subkey);
- else
- retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
- &subkey);
- if (retcode) {
- DEBUGMSGTL(("ksm", "krb5_auth_con_getlocalsubkey failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- #ifdef OLD_HEIMDAL
- retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto);
- if (retcode) {
- DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- #endif /* OLD_HEIMDAL */
- }
- /*
- * Now, we need to pick the "right" checksum algorithm. For old
- * crypto, just pick CKSUMTYPE_RSA_MD5_DES; for new crypto, pick
- * one of the "approved" ones.
- */
- #ifdef MIT_NEW_CRYPTO
- retcode = krb5_c_keyed_checksum_types(kcontext, subkey->enctype,
- &numcksumtypes, &cksumtype_array);
- if (retcode) {
- DEBUGMSGTL(("ksm", "Unable to find appropriate keyed checksum: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- if (numcksumtypes <= 0) {
- DEBUGMSGTL(("ksm", "We received a list of zero cksumtypes for this "
- "enctype (%d)n", subkey->enctype));
- snmp_set_detail("No valid checksum type for this encryption type");
- retval = SNMPERR_KRB5;
- goto error;
- }
- /*
- * It's not clear to me from the API which checksum you're supposed
- * to support, so I'm taking a guess at the first one
- */
- cksumtype = cksumtype_array[0];
- krb5_free_cksumtypes(kcontext, cksumtype_array);
- DEBUGMSGTL(("ksm", "KSM: Choosing checksum type of %d (subkey type "
- "of %d)n", cksumtype, subkey->enctype));
- retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize);
- if (retcode) {
- DEBUGMSGTL(("ksm", "Unable to determine checksum length: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- CHECKSUM_LENGTH(&pdu_checksum) = blocksize;
- #else /* MIT_NEW_CRYPTO */
- if (ksm_state)
- cksumtype = ksm_state->cksumtype;
- else
- #ifdef OLD_HEIMDAL
- {
- /* no way to tell what kind of checksum to use without trying */
- retval = krb5_create_checksum(kcontext, heim_crypto,
- KSM_KEY_USAGE_CHECKSUM, 0,
- parms->scopedPdu, parms->scopedPduLen,
- &pdu_checksum);
- if (retval) {
- DEBUGMSGTL(("ksm", "Unable to create a checksum: %sn",
- error_message(retval)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- cksumtype = CHECKSUM_TYPE(&pdu_checksum);
- }
- #else /* OLD_HEIMDAL */
- cksumtype = CKSUMTYPE_RSA_MD5_DES;
- #endif /* OLD_HEIMDAL */
- #ifdef OLD_HEIMDAL
- if (!krb5_checksum_is_keyed(kcontext, cksumtype)) {
- #else /* OLD_HEIMDAL */
- if (!is_keyed_cksum(cksumtype)) {
- #endif /* OLD_HEIMDAL */
- DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksumn",
- cksumtype));
- snmp_set_detail("Checksum is not a keyed checksum");
- retval = SNMPERR_KRB5;
- goto error;
- }
- #ifdef OLD_HEIMDAL
- if (!krb5_checksum_is_collision_proof(kcontext, cksumtype)) {
- #else /* OLD_HEIMDAL */
- if (!is_coll_proof_cksum(cksumtype)) {
- #endif /* OLD_HEIMDAL */
- DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
- "checksumn", cksumtype));
- snmp_set_detail("Checksum is not a collision-proof checksum");
- retval = SNMPERR_KRB5;
- goto error;
- }
- #ifdef OLD_HEIMDAL
- if (CHECKSUM_CONTENTS(&pdu_checksum) != NULL ) {
- /* we did the bogus checksum--don't need to ask for the size again
- * or initialize cksumtype; just free the bits */
- free(CHECKSUM_CONTENTS(&pdu_checksum));
- CHECKSUM_CONTENTS(&pdu_checksum) = NULL;
- }
- else {
- retval = krb5_checksumsize(kcontext, cksumtype,
- &CHECKSUM_LENGTH(&pdu_checksum));
- if (retval) {
- DEBUGMSGTL(("ksm", "Unable to determine checksum length: %sn",
- error_message(retval)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- #else /* OLD_HEIMDAL */
- CHECKSUM_LENGTH(&pdu_checksum) = krb5_checksum_size(kcontext, cksumtype);
- #endif /* OLD_HEIMDAL */
- CHECKSUM_TYPE(&pdu_checksum) = cksumtype;
- #ifdef OLD_HEIMDAL
- }
- #endif /* OLD_HEIMDAL */
- #endif /* MIT_NEW_CRYPTO */
- /*
- * Note that here, we're just leaving blank space for the checksum;
- * we remember where that is, and we'll fill it in later.
- */
- *offset += CHECKSUM_LENGTH(&pdu_checksum);
- memset(*wholeMsg + *parms->wholeMsgLen - *offset, 0, CHECKSUM_LENGTH(&pdu_checksum));
- cksum_pointer = *wholeMsg + *parms->wholeMsgLen - *offset;
- rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
- parms->wholeMsgOffset, 1,
- (u_char) (ASN_UNIVERSAL |
- ASN_PRIMITIVE |
- ASN_OCTET_STR),
- CHECKSUM_LENGTH(&pdu_checksum));
- if (rc == 0) {
- DEBUGMSGTL(("ksm", "Building ksm security parameters failed.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
- parms->wholeMsgOffset, 1,
- (u_char) (ASN_UNIVERSAL |
- ASN_PRIMITIVE |
- ASN_OCTET_STR),
- (long *) &cksumtype,
- sizeof(cksumtype));
- if (rc == 0) {
- DEBUGMSGTL(("ksm", "Building ksm security parameters failed.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
- parms->wholeMsgOffset, 1,
- (u_char) (ASN_SEQUENCE |
- ASN_CONSTRUCTOR),
- *offset - seq_offset);
- if (rc == 0) {
- DEBUGMSGTL(("ksm", "Building ksm security parameters failed.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
- parms->wholeMsgOffset, 1,
- (u_char) (ASN_UNIVERSAL |
- ASN_PRIMITIVE |
- ASN_OCTET_STR),
- *offset - seq_offset);
- if (rc == 0) {
- DEBUGMSGTL(("ksm", "Building ksm security parameters failed.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completedn"));
- /*
- * We're done with the KSM security parameters - now we do the global
- * header and wrap up the whole PDU.
- */
- if (*parms->wholeMsgLen < parms->globalDataLen) {
- DEBUGMSGTL(("ksm", "Building global data failed.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- *offset += parms->globalDataLen;
- memcpy(*wholeMsg + *parms->wholeMsgLen - *offset,
- parms->globalData, parms->globalDataLen);
- rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
- offset, 1,
- (u_char) (ASN_SEQUENCE |
- ASN_CONSTRUCTOR),
- *offset);
- if (rc == 0) {
- DEBUGMSGTL(("ksm", "Building master packet sequence.n"));
- retval = SNMPERR_TOO_LONG;
- goto error;
- }
- DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.n"));
- /*
- * Now we need to checksum the entire PDU (since it's built).
- */
- #ifndef OLD_HEIMDAL /* since heimdal allocs the mem for us */
- CHECKSUM_CONTENTS(&pdu_checksum) = malloc(CHECKSUM_LENGTH(&pdu_checksum));
- if (!CHECKSUM_CONTENTS(&pdu_checksum)) {
- DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksumn",
- CHECKSUM_LENGTH(&pdu_checksum)));
- retval = SNMPERR_MALLOC;
- goto error;
- }
- #endif /* ! OLD_HEIMDAL */
- #ifdef MIT_NEW_CRYPTO
- input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset);
- input.length = *offset;
- retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey,
- KSM_KEY_USAGE_CHECKSUM, &input,
- &pdu_checksum);
- #elif defined(OLD_HEIMDAL) /* MIT_NEW_CRYPTO */
- retcode = krb5_create_checksum(kcontext, heim_crypto,
- KSM_KEY_USAGE_CHECKSUM, cksumtype,
- *wholeMsg + *parms->wholeMsgLen
- - *offset, *offset, &pdu_checksum);
- #else /* MIT_NEW_CRYPTO */
- retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg +
- *parms->wholeMsgLen - *offset,
- *offset,
- (krb5_pointer) subkey->contents,
- subkey->length, &pdu_checksum);
- #endif /* MIT_NEW_CRYPTO */
- if (retcode) {
- DEBUGMSGTL(("ksm", "Calculate checksum failed: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- snmp_set_detail(error_message(retcode));
- goto error;
- }
- DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.n"));
- memcpy(cksum_pointer, CHECKSUM_CONTENTS(&pdu_checksum), CHECKSUM_LENGTH(&pdu_checksum));
- DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %dn",
- CHECKSUM_LENGTH(&pdu_checksum), cksum_pointer - (*wholeMsg + 1)));
- DEBUGMSGTL(("ksm", "KSM: Checksum:"));
- for (i = 0; i < CHECKSUM_LENGTH(&pdu_checksum); i++)
- DEBUGMSG(("ksm", " %02x",
- (unsigned int) CHECKSUM_CONTENTS(&pdu_checksum)[i]));
- DEBUGMSG(("ksm", "n"));
- /*
- * If we're _not_ called as part of a response (null ksm_state),
- * then save the auth_context for later using our cache routines.
- */
- if (!ksm_state) {
- if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context,
- (u_char *) parms->secName,
- parms->secNameLen)) !=
- SNMPERR_SUCCESS)
- goto error;
- auth_context = NULL;
- }
- DEBUGMSGTL(("ksm", "KSM processing complete!n"));
- error:
- if (CHECKSUM_CONTENTS(&pdu_checksum))
- #ifdef MIT_NEW_CRYPTO
- krb5_free_checksum_contents(kcontext, &pdu_checksum);
- #else /* MIT_NEW_CRYPTO */
- free(CHECKSUM_CONTENTS(&pdu_checksum));
- #endif /* MIT_NEW_CRYPTO */
- if (ivector.data)
- free(ivector.data);
- if (subkey)
- krb5_free_keyblock(kcontext, subkey);
- #ifdef OLD_HEIMDAL /* OLD_HEIMDAL */
- if (heim_crypto)
- krb5_crypto_destroy(kcontext, heim_crypto);
- #endif /* OLD_HEIMDAL */
- if (encrypted_data)
- free(encrypted_data);
- if (cc)
- krb5_cc_close(kcontext, cc);
- if (auth_context && !ksm_state)
- krb5_auth_con_free(kcontext, auth_context);
- return retval;
- }
- /****************************************************************************
- *
- * ksm_process_in_msg
- *
- * Parameters:
- * (See list below...)
- *
- * Returns:
- * KSM_ERR_NO_ERROR On success.
- * SNMPERR_KRB5
- * KSM_ERR_GENERIC_ERROR
- * KSM_ERR_UNSUPPORTED_SECURITY_LEVEL
- *
- *
- * Processes an incoming message.
- *
- ****************************************************************************/
- int
- ksm_process_in_msg(struct snmp_secmod_incoming_params *parms)
- {
- long temp;
- krb5_cksumtype cksumtype;
- krb5_auth_context auth_context = NULL;
- krb5_error_code retcode;
- krb5_checksum checksum;
- krb5_data ap_req, ivector;
- krb5_flags flags;
- krb5_keyblock *subkey = NULL;
- #ifdef MIT_NEW_CRYPTO
- krb5_data input, output;
- krb5_boolean valid;
- krb5_enc_data in_crypt;
- #elif defined OLD_HEIMDAL /* MIT_NEW_CRYPTO */
- krb5_data output;
- krb5_crypto heim_crypto = NULL;
- #else /* MIT_NEW_CRYPTO */
- krb5_encrypt_block eblock;
- #endif /* MIT_NEW_CRYPTO */
- krb5_ticket *ticket = NULL;
- int retval = SNMPERR_SUCCESS, response = 0;
- size_t length =
- parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg);
- u_char *current = parms->secParams, type;
- size_t cksumlength, blocksize;
- long hint;
- char *cname;
- struct ksm_secStateRef *ksm_state;
- struct ksm_cache_entry *entry;
- DEBUGMSGTL(("ksm", "Processing has begunn"));
- CHECKSUM_CONTENTS(&checksum) = NULL;
- ap_req.data = NULL;
- ivector.length = 0;
- ivector.data = NULL;
- /*
- * First, parse the security parameters (because we need the subkey inside
- * of the ticket to do anything
- */
- if ((current = asn_parse_sequence(current, &length, &type,
- (ASN_UNIVERSAL | ASN_PRIMITIVE |
- ASN_OCTET_STR),
- "ksm first octet")) == NULL) {
- DEBUGMSGTL(("ksm", "Initial security paramter parsing failedn"));
- retval = SNMPERR_ASN_PARSE_ERR;
- goto error;
- }
- if ((current = asn_parse_sequence(current, &length, &type,
- (ASN_SEQUENCE | ASN_CONSTRUCTOR),
- "ksm sequence")) == NULL) {
- DEBUGMSGTL(("ksm",
- "Security parameter sequence parsing failedn"));
- retval = SNMPERR_ASN_PARSE_ERR;
- goto error;
- }
- if ((current = asn_parse_int(current, &length, &type, &temp,
- sizeof(temp))) == NULL) {
- DEBUGMSGTL(("ksm", "Security parameter checksum type parsing"
- "failedn"));
- retval = SNMPERR_ASN_PARSE_ERR;
- goto error;
- }
- cksumtype = temp;
- #ifdef MIT_NEW_CRYPTO
- if (!krb5_c_valid_cksumtype(cksumtype)) {
- DEBUGMSGTL(("ksm", "Invalid checksum type (%d)n", cksumtype));
- retval = SNMPERR_KRB5;
- snmp_set_detail("Invalid checksum type");
- goto error;
- }
- if (!krb5_c_is_keyed_cksum(cksumtype)) {
- DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksumn",
- cksumtype));
- snmp_set_detail("Checksum is not a keyed checksum");
- retval = SNMPERR_KRB5;
- goto error;
- }
- if (!krb5_c_is_coll_proof_cksum(cksumtype)) {
- DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
- "checksumn", cksumtype));
- snmp_set_detail("Checksum is not a collision-proof checksum");
- retval = SNMPERR_KRB5;
- goto error;
- }
- #else /* ! MIT_NEW_CRYPTO */
- #ifdef OLD_HEIMDAL
- /* kludge */
- if (krb5_checksumsize(kcontext, cksumtype, &cksumlength)) {
- #else /* OLD_HEIMDAL */
- if (!valid_cksumtype(cksumtype)) {
- #endif /* OLD_HEIMDAL */
- DEBUGMSGTL(("ksm", "Invalid checksum type (%d)n", cksumtype));
- retval = SNMPERR_KRB5;
- snmp_set_detail("Invalid checksum type");
- goto error;
- }
- #ifdef OLD_HEIMDAL
- if (!krb5_checksum_is_keyed(kcontext, cksumtype)) {
- #else /* OLD_HEIMDAL */
- if (!is_keyed_cksum(cksumtype)) {
- #endif /* OLD_HEIMDAL */
- DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksumn",
- cksumtype));
- snmp_set_detail("Checksum is not a keyed checksum");
- retval = SNMPERR_KRB5;
- goto error;
- }
- #ifdef OLD_HEIMDAL
- if (!krb5_checksum_is_collision_proof(kcontext, cksumtype)) {
- #else /* OLD_HEIMDAL */
- if (!is_coll_proof_cksum(cksumtype)) {
- #endif /* OLD_HEIMDAL */
- DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
- "checksumn", cksumtype));
- snmp_set_detail("Checksum is not a collision-proof checksum");
- retval = SNMPERR_KRB5;
- goto error;
- }
- #endif /* MIT_NEW_CRYPTO */
- CHECKSUM_TYPE(&checksum) = cksumtype;
- cksumlength = length;
- if ((current = asn_parse_sequence(current, &cksumlength, &type,
- (ASN_UNIVERSAL | ASN_PRIMITIVE |
- ASN_OCTET_STR), "ksm checksum")) ==
- NULL) {
- DEBUGMSGTL(("ksm",
- "Security parameter checksum parsing failedn"));
- retval = SNMPERR_ASN_PARSE_ERR;
- goto error;
- }
- CHECKSUM_CONTENTS(&checksum) = malloc(cksumlength);
- if (!CHECKSUM_CONTENTS(&checksum)) {
- DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.n",
- cksumlength));
- retval = SNMPERR_MALLOC;
- goto error;
- }
- memcpy(CHECKSUM_CONTENTS(&checksum), current, cksumlength);
- CHECKSUM_LENGTH(&checksum) = cksumlength;
- CHECKSUM_TYPE(&checksum) = cksumtype;
- /*
- * Zero out the checksum so the validation works correctly
- */
- memset(current, 0, cksumlength);
- current += cksumlength;
- length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);
- if ((current = asn_parse_sequence(current, &length, &type,
- (ASN_UNIVERSAL | ASN_PRIMITIVE |
- ASN_OCTET_STR), "ksm ap_req")) ==
- NULL) {
- DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing "
- "failedn"));
- retval = SNMPERR_ASN_PARSE_ERR;
- goto error;
- }
- ap_req.length = length;
- ap_req.data = malloc(length);
- if (!ap_req.data) {
- DEBUGMSGTL(("ksm",
- "KSM unable to malloc %d bytes for AP_REQ/REP.n",
- length));
- retval = SNMPERR_MALLOC;
- goto error;
- }
- memcpy(ap_req.data, current, length);
- current += length;
- length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);
- if ((current = asn_parse_int(current, &length, &type, &hint,
- sizeof(hint))) == NULL) {
- DEBUGMSGTL(("ksm",
- "KSM security parameter hint parsing failedn"));
- retval = SNMPERR_ASN_PARSE_ERR;
- goto error;
- }
- /*
- * Okay! We've got it all! Now try decoding the damn ticket.
- *
- * But of course there's a WRINKLE! We need to figure out if we're
- * processing a AP_REQ or an AP_REP. How do we do that? We're going
- * to cheat, and look at the first couple of bytes (which is what
- * the Kerberos library routines do anyway).
- *
- * If there are ever new Kerberos message formats, we'll need to fix
- * this here.
- *
- * If it's a _response_, then we need to get the auth_context
- * from our cache.
- */
- if (ap_req.length
- #ifndef HEIMDAL
- && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) {
- #else /* HEIMDAL */
- && (((char *)ap_req.data)[0] == 0x6e || ((char *)ap_req.data)[0] == 0x4e)) {
- #endif
- /*
- * We need to initalize the authorization context, and set the
- * replay cache in it (and initialize the replay cache if we
- * haven't already
- */
- retcode = krb5_auth_con_init(kcontext, &auth_context);
- if (retcode) {
- DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- snmp_set_detail(error_message(retcode));
- goto error;
- }
- if (!rcache) {
- krb5_data server;
- server.data = "host";
- server.length = strlen(server.data);
- retcode = krb5_get_server_rcache(kcontext, &server, &rcache);
- if (retcode) {
- DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- snmp_set_detail(error_message(retcode));
- goto error;
- }
- }
- retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache);
- if (retcode) {
- DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- snmp_set_detail(error_message(retcode));
- goto error;
- }
- retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL,
- keytab, &flags, &ticket);
- krb5_auth_con_setrcache(kcontext, auth_context, NULL);
- if (retcode) {
- DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- snmp_set_detail(error_message(retcode));
- goto error;
- }
- retcode =
- krb5_unparse_name(kcontext, TICKET_CLIENT(ticket), &cname);
- if (retcode == 0) {
- DEBUGMSGTL(("ksm", "KSM authenticated principal name: %sn",
- cname));
- free(cname);
- }
- /*
- * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set
- */
- if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) {
- DEBUGMSGTL(("ksm",
- "KSM MUTUAL_REQUIRED not set in request!n"));
- retval = SNMPERR_KRB5;
- snmp_set_detail("MUTUAL_REQUIRED not set in message");
- goto error;
- }
- retcode =
- krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey);
- if (retcode) {
- DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- snmp_set_detail(error_message(retcode));
- goto error;
- }
- #ifndef HEIMDAL
- } else if (ap_req.length && (ap_req.data[0] == 0x6f ||
- ap_req.data[0] == 0x4f)) {
- #else /* HEIMDAL */
- } else if (ap_req.length && (((char *)ap_req.data)[0] == 0x6f ||
- ((char *)ap_req.data)[0] == 0x4f)) {
- #endif /* HEIMDAL */
- /*
- * Looks like a response; let's see if we've got that auth_context
- * in our cache.
- */
- krb5_ap_rep_enc_part *repl = NULL;
- response = 1;
- entry = ksm_get_cache(parms->pdu->msgid);
- if (!entry) {
- DEBUGMSGTL(("ksm",
- "KSM: Unable to find auth_context for PDU with "
- "message ID of %ldn", parms->pdu->msgid));
- retval = SNMPERR_KRB5;
- goto error;
- }
- auth_context = entry->auth_context;
- /*
- * In that case, let's call the rd_rep function
- */
- retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl);
- if (repl)
- krb5_free_ap_rep_enc_part(kcontext, repl);
- if (retcode) {
- DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- goto error;
- }
- DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.n"));
- retcode =
- krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey);
- if (retcode) {
- DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- snmp_set_detail("Unable to retrieve local subkey");
- goto error;
- }
- } else {
- #ifndef HEIMDAL
- DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)n",
- ap_req.data[0]));
- #else /* HEIMDAL */
- DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)n",
- ((char *)ap_req.data)[0]));
- #endif
- retval = SNMPERR_KRB5;
- snmp_set_detail("Unknown Kerberos message type");
- goto error;
- }
- #ifdef MIT_NEW_CRYPTO
- input.data = (char *) parms->wholeMsg;
- input.length = parms->wholeMsgLen;
- retcode =
- krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM,
- &input, &checksum, &valid);
- #elif defined(OLD_HEIMDAL) /* MIT_NEW_CRYPTO */
- retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto);
- if (retcode) {
- DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- retcode = krb5_verify_checksum(kcontext, heim_crypto,
- KSM_KEY_USAGE_CHECKSUM, parms->wholeMsg,
- parms->wholeMsgLen, &checksum);
- #else /* MIT_NEW_CRYPTO */
- retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum,
- parms->wholeMsg, parms->wholeMsgLen,
- (krb5_pointer) subkey->contents,
- subkey->length);
- #endif /* MIT_NEW_CRYPTO */
- if (retcode) {
- DEBUGMSGTL(("ksm", "KSM checksum verification failed: %sn",
- error_message(retcode)));
- retval = SNMPERR_KRB5;
- snmp_set_detail(error_message(retcode));
- goto error;
- }
- /*
- * Don't ask me why they didn't simply return an error, but we have
- * to check to see if "valid" is false.
- */
- #ifdef MIT_NEW_CRYPTO
- if (!valid) {
- DEBUGMSGTL(("ksm", "Computed checksum did not match supplied "
- "checksum!n"));
- retval = SNMPERR_KRB5;
- snmp_set_detail
- ("Computed checksum did not match supplied checksum");
- goto error;
- }
- #endif /* MIT_NEW_CRYPTO */
- /*
- * Handle an encrypted PDU. Note that it's an OCTET_STRING of the
- * output of whatever Kerberos cryptosystem you're using (defined by
- * the encryption type). Note that this is NOT the EncryptedData
- * sequence - it's what goes in the "cipher" field of EncryptedData.
- */
- if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
- if ((current = asn_parse_sequence(current, &length, &type,
- (ASN_UNIVERSAL | ASN_PRIMITIVE |
- ASN_OCTET_STR), "ksm pdu")) ==
- NULL) {
- DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failedn"));
- retval = SNMPERR_ASN_PARSE_ERR;
- goto error;
- }
- /*
- * The PDU is now pointed at by "current", and the length is in
- * "length".
- */
- DEBUGMSGTL(("ksm", "KSM starting sPDU decoden"));
- /*
- * We need to set up a blank initialization vector for the decryption.
- * Use a block of all zero's (which is dependent on the block size
- * of the encryption method).
- */
- #ifdef MIT_NEW_CRYPTO
- retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);
- if (retcode) {
- DEBUGMSGTL(("ksm",
- "Unable to determine crypto block size: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- #elif defined(OLD_HEIMDAL) /* MIT_NEW_CRYPTO */
- #else /* MIT_NEW_CRYPTO */
- blocksize =
- krb5_enctype_array[subkey->enctype]->system->block_length;
- #endif /* MIT_NEW_CRYPTO */
- #ifndef OLD_HEIMDAL
- ivector.data = malloc(blocksize);
- if (!ivector.data) {
- DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivectorn",
- blocksize));
- retval = SNMPERR_MALLOC;
- goto error;
- }
- ivector.length = blocksize;
- memset(ivector.data, 0, blocksize);
- #ifndef MIT_NEW_CRYPTO
- krb5_use_enctype(kcontext, &eblock, subkey->enctype);
- retcode = krb5_process_key(kcontext, &eblock, subkey);
- if (retcode) {
- DEBUGMSGTL(("ksm", "KSM key post-processing failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- #endif /* !MIT_NEW_CRYPTO */
- #endif /* ! OLD_HEIMDAL */
- if (length > *parms->scopedPduLen) {
- DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to "
- "decrypt but only %d bytes availablen", length,
- *parms->scopedPduLen));
- retval = SNMPERR_TOO_LONG;
- #ifndef MIT_NEW_CRYPTO
- #ifndef OLD_HEIMDAL
- krb5_finish_key(kcontext, &eblock);
- #endif /* ! OLD_HEIMDAL */
- #endif /* ! MIT_NEW_CRYPTO */
- goto error;
- }
- #ifdef MIT_NEW_CRYPTO
- in_crypt.ciphertext.data = (char *) current;
- in_crypt.ciphertext.length = length;
- in_crypt.enctype = subkey->enctype;
- output.data = (char *) *parms->scopedPdu;
- output.length = *parms->scopedPduLen;
- retcode =
- krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
- &ivector, &in_crypt, &output);
- #elif defined (OLD_HEIMDAL) /* MIT_NEW_CRYPTO */
- retcode = krb5_decrypt(kcontext, heim_crypto, KSM_KEY_USAGE_ENCRYPTION,
- current, length, &output);
- if (retcode == 0) {
- *parms->scopedPdu = (char *) output.data;
- *parms->scopedPduLen = output.length;
- krb5_data_zero(&output);
- }
- #else /* MIT_NEW_CRYPTO */
- retcode = krb5_decrypt(kcontext, (krb5_pointer) current,
- *parms->scopedPdu, length, &eblock,
- ivector.data);
- krb5_finish_key(kcontext, &eblock);
- #endif /* MIT_NEW_CRYPTO */
- if (retcode) {
- DEBUGMSGTL(("ksm", "Decryption failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- *parms->scopedPduLen = length;
- } else {
- /*
- * Clear PDU
- */
- *parms->scopedPdu = current;
- *parms->scopedPduLen =
- parms->wholeMsgLen - (current - parms->wholeMsg);
- }
- /*
- * A HUGE GROSS HACK
- */
- *parms->maxSizeResponse = parms->maxMsgSize - 200;
- DEBUGMSGTL(("ksm", "KSM processing completen"));
- /*
- * Set the secName to the right value (a hack for now). But that's
- * only used for when we're processing a request, not a response.
- */
- if (!response) {
- retcode = krb5_unparse_name(kcontext, TICKET_CLIENT(ticket),
- &cname);
- if (retcode) {
- DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %sn",
- error_message(retcode)));
- snmp_set_detail(error_message(retcode));
- retval = SNMPERR_KRB5;
- goto error;
- }
- if (strlen(cname) > *parms->secNameLen + 1) {
- DEBUGMSGTL(("ksm",
- "KSM: Principal length (%d) is too long (%d)n",
- strlen(cname), parms->secNameLen));
- retval = SNMPERR_TOO_LONG;
- free(cname);
- goto error;
- }
- strcpy(parms->secName, cname);
- *parms->secNameLen = strlen(cname);
- free(cname);
- /*
- * Also, if we're not a response, keep around our auth_context so we
- * can encode the reply message correctly
- */
- ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef);
- if (!ksm_state) {
- DEBUGMSGTL(("ksm", "KSM unable to malloc memory for "
- "ksm_secStateRefn"));
- retval = SNMPERR_MALLOC;
- goto error;
- }
- ksm_state->auth_context = auth_context;
- auth_context = NULL;
- ksm_state->cksumtype = cksumtype;
- *parms->secStateRef = ksm_state;
- } else {
- /*
- * We _still_ have to set the secName in process_in_msg(). Do
- * that now with what we were passed in before (we cached it,
- * remember?)
- */
- memcpy(parms->secName, entry->secName, entry->secNameLen);
- *parms->secNameLen = entry->secNameLen;
- }
- /*
- * Just in case
- */
- parms->secEngineID = (u_char *) "";
- *parms->secEngineIDLen = 0;
- auth_context = NULL; /* So we don't try to free it on success */
- error:
- if (retval == SNMPERR_ASN_PARSE_ERR &&
- snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0)
- DEBUGMSGTL(("ksm", "Failed to increment statistics.n"));
- if (subkey)
- krb5_free_keyblock(kcontext, subkey);
- #ifdef OLD_HEIMDAL /* OLD_HEIMDAL */
- if (heim_crypto)
- krb5_crypto_destroy(kcontext, heim_crypto);
- #endif /* OLD_HEIMDAL */
- if (CHECKSUM_CONTENTS(&checksum))
- free(CHECKSUM_CONTENTS(&checksum));
- if (ivector.data)
- free(ivector.data);
- if (ticket)
- krb5_free_ticket(kcontext, ticket);
- if (!response && auth_context)
- krb5_auth_con_free(kcontext, auth_context);
- if (ap_req.data)
- free(ap_req.data);
- return retval;
- }