net-snmp 5.7
snmpksm.c
00001 /*
00002  * snmpksm.c
00003  *
00004  * This code implements the Kerberos Security Model (KSM) for SNMP.
00005  *
00006  * Security number - 2066432
00007  */
00008 
00009 #include <net-snmp/net-snmp-config.h>
00010 
00011 #include <sys/types.h>
00012 #include <stdio.h>
00013 #ifdef HAVE_STDLIB_H
00014 #include <stdlib.h>
00015 #endif
00016 #if TIME_WITH_SYS_TIME
00017 # include <sys/time.h>
00018 # include <time.h>
00019 #else
00020 # if HAVE_SYS_TIME_H
00021 #  include <sys/time.h>
00022 # else
00023 #  include <time.h>
00024 # endif
00025 #endif
00026 #if HAVE_STRING_H
00027 #include <string.h>
00028 #else
00029 #include <strings.h>
00030 #endif
00031 #ifdef HAVE_NETINET_IN_H
00032 #include <netinet/in.h>
00033 #endif
00034 #include <errno.h>
00035 
00036 
00037 #if HAVE_DMALLOC_H
00038 #include <dmalloc.h>
00039 #endif
00040 
00041 #ifdef NETSNMP_USE_KERBEROS_HEIMDAL
00042 #ifndef NETSNMP_USE_KERBEROS_MIT
00043 #define OLD_HEIMDAL
00044 #endif                          /* ! NETSNMP_USE_KERBEROS_MIT */
00045 #endif                          /* NETSNMP_USE_KERBEROS_HEIMDAL */
00046 
00047 #ifdef NETSNMP_USE_KERBEROS_HEIMDAL
00048 #define oid heimdal_oid_renamed
00049 #endif                          /* NETSNMP_USE_KERBEROS_HEIMDAL */
00050 #include <krb5.h>
00051 #include <com_err.h>
00052 #ifdef NETSNMP_USE_KERBEROS_HEIMDAL
00053 #undef oid
00054 #endif                          /* NETSNMP_USE_KERBEROS_HEIMDAL */
00055 
00056 #ifdef NETSNMP_USE_KERBEROS_HEIMDAL
00057 #define CHECKSUM_TYPE(x)        (x)->cksumtype
00058 #define CHECKSUM_CONTENTS(x)    ((char *)((x)->checksum.data))
00059 #define CHECKSUM_LENGTH(x)      (x)->checksum.length
00060 #define TICKET_CLIENT(x)        (x)->client
00061 #else                           /* NETSNMP_USE_KERBEROS_HEIMDAL */
00062 #define CHECKSUM_TYPE(x)        (x)->checksum_type
00063 #define CHECKSUM_CONTENTS(x)    (x)->contents
00064 #define CHECKSUM_LENGTH(x)      (x)->length
00065 #define TICKET_CLIENT(x)        (x)->enc_part2->client
00066 #endif                          /* NETSNMP_USE_KERBEROS_HEIMDAL */
00067 
00068 #include <net-snmp/output_api.h>
00069 #include <net-snmp/config_api.h>
00070 #include <net-snmp/utilities.h>
00071 
00072 #include <net-snmp/library/asn1.h>
00073 #include <net-snmp/library/snmp_api.h>
00074 #include <net-snmp/library/callback.h>
00075 #include <net-snmp/library/keytools.h>
00076 #include <net-snmp/library/snmpv3.h>
00077 #include <net-snmp/library/lcd_time.h>
00078 #include <net-snmp/library/scapi.h>
00079 #include <net-snmp/library/callback.h>
00080 #include <net-snmp/library/snmp_secmod.h>
00081 #include <net-snmp/library/snmpksm.h>
00082 
00083 static krb5_context kcontext = NULL;
00084 static krb5_rcache rcache = NULL;
00085 static krb5_keytab keytab = NULL;
00086 static int keytab_setup = 0;
00087 static const char *service_name = NULL;
00088 
00089 static int      ksm_session_init(netsnmp_session *);
00090 static void     ksm_free_state_ref(void *);
00091 static int      ksm_free_pdu(netsnmp_pdu *);
00092 static int      ksm_clone_pdu(netsnmp_pdu *, netsnmp_pdu *);
00093 
00094 static int      ksm_insert_cache(long, krb5_auth_context, u_char *,
00095                                  size_t);
00096 static void     ksm_decrement_ref_count(long);
00097 static void     ksm_increment_ref_count(long);
00098 static struct ksm_cache_entry *ksm_get_cache(long);
00099 
00100 #define HASHSIZE        64
00101 
00102 /*
00103  * Our information stored for the response PDU.
00104  */
00105 
00106 struct ksm_secStateRef {
00107     krb5_auth_context auth_context;
00108     krb5_cksumtype  cksumtype;
00109 };
00110 
00111 /*
00112  * A KSM outgoing pdu cache entry
00113  */
00114 
00115 struct ksm_cache_entry {
00116     long            msgid;
00117     int             refcount;
00118     krb5_auth_context auth_context;
00119     u_char         *secName;
00120     size_t          secNameLen;
00121     struct ksm_cache_entry *next;
00122 };
00123 
00124 /*
00125  * Poor man's hash table
00126  */
00127 
00128 static struct ksm_cache_entry *ksm_hash_table[HASHSIZE];
00129 
00130 /*
00131  * Stuff to deal with config values
00132  * Note the conditionals that wrap these--i don't know if these are
00133  * needed, since i don't know how library initialization and callbacks
00134  * and stuff work
00135  */
00136 
00137 static int
00138 init_snmpksm_post_config(int majorid, int minorid, void *serverarg,
00139                          void *clientarg)
00140 {
00141 
00142     if (kcontext == NULL) {
00143         /* not reached, i'd imagine */
00144         return SNMPERR_KRB5;
00145     }
00146 
00147     if (service_name == NULL) {
00148         /* always reached, i'd imagine */
00149         char *c = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
00150                                         NETSNMP_DS_LIB_KSM_SERVICE_NAME);
00151         if (c != NULL) {
00152                 service_name = c;
00153         }
00154         else {
00155                 service_name = "host";
00156         }
00157     }
00158 
00159     if (keytab_setup == 0) {
00160         /* always reached, i'd imagine */
00161         char *c = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
00162                                         NETSNMP_DS_LIB_KSM_KEYTAB);
00163         if (c) {
00164             krb5_error_code retval;
00165             DEBUGMSGTL(("ksm", "Using keytab %s\n", c));
00166             retval = krb5_kt_resolve(kcontext, c, &keytab);
00167             if (retval) {
00168                 DEBUGMSGTL(("ksm", "krb5_kt_resolve(\"%s\") failed. KSM "
00169                             "config callback failing\n", error_message(retval)));
00170                 return SNMPERR_KRB5;
00171             }
00172         }
00173         else {
00174             DEBUGMSGTL(("ksm", "Using default keytab\n", c));
00175         }
00176         keytab_setup = 1;
00177     }
00178 
00179     return SNMPERR_SUCCESS;
00180 }
00181 
00182 /*
00183  * Initialize all of the state required for Kerberos (right now, just call
00184  * krb5_init_context).
00185  */
00186 
00187 void
00188 init_ksm(void)
00189 {
00190     krb5_error_code retval;
00191     struct snmp_secmod_def *def;
00192     int             i;
00193 
00194     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defKSMKeytab",
00195                                NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_KSM_KEYTAB);
00196     netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defKSMServiceName",
00197                                NETSNMP_DS_LIBRARY_ID,
00198                                NETSNMP_DS_LIB_KSM_SERVICE_NAME);
00199     snmp_register_callback(SNMP_CALLBACK_LIBRARY,
00200                            SNMP_CALLBACK_POST_READ_CONFIG,
00201                            init_snmpksm_post_config, NULL);
00202 
00203 
00204     if (kcontext == NULL) {
00205         retval = krb5_init_context(&kcontext);
00206 
00207         if (retval) {
00208             DEBUGMSGTL(("ksm", "krb5_init_context failed (%s), not "
00209                         "registering KSM\n", error_message(retval)));
00210             return;
00211         }
00212     }
00213 
00214     for (i = 0; i < HASHSIZE; i++)
00215         ksm_hash_table[i] = NULL;
00216 
00217     def = SNMP_MALLOC_STRUCT(snmp_secmod_def);
00218 
00219     if (!def) {
00220         DEBUGMSGTL(("ksm", "Unable to malloc snmp_secmod struct, not "
00221                     "registering KSM\n"));
00222         return;
00223     }
00224 
00225     def->encode_reverse = ksm_rgenerate_out_msg;
00226     def->decode = ksm_process_in_msg;
00227     def->session_open = ksm_session_init;
00228     def->pdu_free_state_ref = ksm_free_state_ref;
00229     def->pdu_free = ksm_free_pdu;
00230     def->pdu_clone = ksm_clone_pdu;
00231 
00232     register_sec_mod(NETSNMP_SEC_MODEL_KSM, "ksm", def);
00233 }
00234 
00235 void shutdown_ksm(void)
00236 {
00237 }
00238 
00239 /*
00240  * These routines implement a simple cache for information we need to
00241  * process responses.  When we send out a request, it contains an AP_REQ;
00242  * we get back an AP_REP, and we need the authorization context from the
00243  * AP_REQ to decrypt the AP_REP.  But because right now there's nothing
00244  * that gets preserved across calls to rgenerate_out_msg to process_in_msg,
00245  * we cache these internally based on the message ID (we also cache the
00246  * passed-in security name, for reasons that are mostly stupid).
00247  */
00248 
00249 static int
00250 ksm_insert_cache(long msgid, krb5_auth_context auth_context,
00251                  u_char * secName, size_t secNameLen)
00252 {
00253     struct ksm_cache_entry *entry;
00254     int             bucket;
00255     int             retval;
00256 
00257     entry = SNMP_MALLOC_STRUCT(ksm_cache_entry);
00258 
00259     if (!entry)
00260         return SNMPERR_MALLOC;
00261 
00262     entry->msgid = msgid;
00263     entry->auth_context = auth_context;
00264     entry->refcount = 1;
00265 
00266     retval = memdup(&entry->secName, secName, secNameLen);
00267 
00268     if (retval != SNMPERR_SUCCESS) {
00269         free(entry);
00270         return retval;
00271     }
00272 
00273     entry->secNameLen = secNameLen;
00274 
00275     bucket = msgid % HASHSIZE;
00276 
00277     entry->next = ksm_hash_table[bucket];
00278     ksm_hash_table[bucket] = entry;
00279 
00280     return SNMPERR_SUCCESS;
00281 }
00282 
00283 static struct ksm_cache_entry *
00284 ksm_get_cache(long msgid)
00285 {
00286     struct ksm_cache_entry *entry;
00287     int             bucket;
00288 
00289     bucket = msgid % HASHSIZE;
00290 
00291     for (entry = ksm_hash_table[bucket]; entry != NULL;
00292          entry = entry->next)
00293         if (entry->msgid == msgid)
00294             return entry;
00295 
00296     return NULL;
00297 }
00298 
00299 static void
00300 ksm_decrement_ref_count(long msgid)
00301 {
00302     struct ksm_cache_entry *entry, *entry1;
00303     int             bucket;
00304 
00305     bucket = msgid % HASHSIZE;
00306 
00307     if (ksm_hash_table[bucket] && ksm_hash_table[bucket]->msgid == msgid) {
00308         entry = ksm_hash_table[bucket];
00309 
00310         /*
00311          * If the reference count is zero, then free it
00312          */
00313 
00314         if (--entry->refcount <= 0) {
00315             DEBUGMSGTL(("ksm", "Freeing entry for msgid %ld\n", msgid));
00316             krb5_auth_con_free(kcontext, entry->auth_context);
00317             free(entry->secName);
00318             ksm_hash_table[bucket] = entry->next;
00319             free(entry);
00320         }
00321 
00322         return;
00323 
00324     } else if (ksm_hash_table[bucket])
00325         for (entry1 = ksm_hash_table[bucket], entry = entry1->next;
00326              entry != NULL; entry1 = entry, entry = entry->next)
00327             if (entry->msgid == msgid) {
00328 
00329                 if (--entry->refcount <= 0) {
00330                     DEBUGMSGTL(("ksm", "Freeing entry for msgid %ld\n",
00331                                 msgid));
00332                     krb5_auth_con_free(kcontext, entry->auth_context);
00333                     free(entry->secName);
00334                     entry1->next = entry->next;
00335                     free(entry);
00336                 }
00337 
00338                 return;
00339             }
00340 
00341     DEBUGMSGTL(("ksm",
00342                 "KSM: Unable to decrement cache entry for msgid %ld.\n",
00343                 msgid));
00344 }
00345 
00346 static void
00347 ksm_increment_ref_count(long msgid)
00348 {
00349     struct ksm_cache_entry *entry = ksm_get_cache(msgid);
00350 
00351     if (!entry) {
00352         DEBUGMSGTL(("ksm", "Unable to find cache entry for msgid %ld "
00353                     "for increment\n", msgid));
00354         return;
00355     }
00356 
00357     entry->refcount++;
00358 }
00359 
00360 /*
00361  * Initialize specific session information (right now, just set up things to
00362  * not do an engineID probe)
00363  */
00364 
00365 static int
00366 ksm_session_init(netsnmp_session * sess)
00367 {
00368     DEBUGMSGTL(("ksm",
00369                 "KSM: Reached our session initialization callback\n"));
00370 
00371     sess->flags |= SNMP_FLAGS_DONT_PROBE;
00372 
00373     return SNMPERR_SUCCESS;
00374 }
00375 
00376 /*
00377  * Free our state information (this is only done on the agent side)
00378  */
00379 
00380 static void
00381 ksm_free_state_ref(void *ptr)
00382 {
00383     struct ksm_secStateRef *ref = (struct ksm_secStateRef *) ptr;
00384 
00385     DEBUGMSGTL(("ksm", "KSM: Freeing state reference\n"));
00386 
00387     krb5_auth_con_free(kcontext, ref->auth_context);
00388 
00389     free(ref);
00390 }
00391 
00392 /*
00393  * This is called when the PDU is freed; this will decrement reference counts
00394  * for entries in our state cache.
00395  */
00396 
00397 static int
00398 ksm_free_pdu(netsnmp_pdu *pdu)
00399 {
00400     ksm_decrement_ref_count(pdu->msgid);
00401 
00402     DEBUGMSGTL(("ksm", "Decrementing cache entry for PDU msgid %ld\n",
00403                 pdu->msgid));
00404 
00405     return SNMPERR_SUCCESS;
00406 }
00407 
00408 /*
00409  * This is called when a PDU is cloned (to increase reference counts)
00410  */
00411 
00412 static int
00413 ksm_clone_pdu(netsnmp_pdu *pdu, netsnmp_pdu *pdu2)
00414 {
00415     ksm_increment_ref_count(pdu->msgid);
00416 
00417     DEBUGMSGTL(("ksm", "Incrementing cache entry for PDU msgid %ld\n",
00418                 pdu->msgid));
00419 
00420     return SNMPERR_SUCCESS;
00421 }
00422 
00423 /****************************************************************************
00424  *
00425  * ksm_generate_out_msg
00426  *
00427  * Parameters:
00428  *      (See list below...)
00429  *
00430  * Returns:
00431  *      SNMPERR_GENERIC                        On success.
00432  *      SNMPERR_KRB5
00433  *      ... and others
00434  *
00435  *
00436  * Generate an outgoing message.
00437  *
00438  ****************************************************************************/
00439 
00440 int
00441 ksm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms)
00442 {
00443     krb5_auth_context auth_context = NULL;
00444     krb5_error_code retcode;
00445     krb5_ccache     cc = NULL;
00446     int             retval = SNMPERR_SUCCESS;
00447     krb5_data       outdata, ivector;
00448     krb5_keyblock  *subkey = NULL;
00449 #ifdef NETSNMP_USE_KERBEROS_MIT
00450     krb5_data       input;
00451     krb5_enc_data   output;
00452     unsigned int    numcksumtypes;
00453     krb5_cksumtype  *cksumtype_array;
00454 #elif defined OLD_HEIMDAL       /* NETSNMP_USE_KERBEROS_MIT */
00455     krb5_crypto heim_crypto = NULL;
00456 #else                           /* NETSNMP_USE_KERBEROS_MIT */
00457     krb5_encrypt_block eblock;
00458 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
00459     size_t          blocksize, encrypted_length;
00460     unsigned char  *encrypted_data = NULL;
00461     long            zero = 0, tmp;
00462     int             i;
00463     u_char         *cksum_pointer, *endp = *parms->wholeMsg;
00464     krb5_cksumtype  cksumtype;
00465     krb5_checksum   pdu_checksum;
00466     u_char         **wholeMsg = parms->wholeMsg;
00467     size_t         *offset = parms->wholeMsgOffset, seq_offset;
00468     struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *)
00469         parms->secStateRef;
00470 #ifdef OLD_HEIMDAL
00471     krb5_data encrypted_scoped_pdu;
00472 #endif                          /* OLD_HEIMDAL */
00473     int rc;
00474     char *colon = NULL;
00475 
00476     DEBUGMSGTL(("ksm", "Starting KSM processing\n"));
00477 
00478     outdata.length = 0;
00479     outdata.data = NULL;
00480     ivector.length = 0;
00481     ivector.data = NULL;
00482     CHECKSUM_CONTENTS(&pdu_checksum) = NULL;
00483 
00484     if (!ksm_state) {
00485         /*
00486          * If we've got a port number as part of the "peername", then
00487          * suppress this (temporarily) while we build the credential info.
00488          *   XXX - what about "udp:host" style addresses?
00489          */
00490         colon = strrchr(params->session->peername, ':');
00491         if (colon != NULL) {
00492             *colon='\0';
00493         }
00494 
00495         /*
00496          * If we don't have a ksm_state, then we're a request.  Get a
00497          * credential cache and build a ap_req.
00498          */
00499         retcode = krb5_cc_default(kcontext, &cc);
00500 
00501         if (retcode) {
00502             DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %s\n",
00503                         error_message(retcode)));
00504             snmp_set_detail(error_message(retcode));
00505             retval = SNMPERR_KRB5;
00506             goto error;
00507         }
00508 
00509         DEBUGMSGTL(("ksm", "KSM: Set credential cache successfully\n"));
00510 
00511         /*
00512          * This seems odd, since we don't need this until later (or earlier,
00513          * depending on how you look at it), but because the most likely
00514          * errors are Kerberos at this point, I'll get this now to save
00515          * time not encoding the rest of the packet.
00516          *
00517          * Also, we need the subkey to encrypt the PDU (if required).
00518          */
00519 
00520         retcode =
00521             krb5_mk_req(kcontext, &auth_context,
00522                         AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
00523                         (char *) service_name, parms->session->peername, NULL,
00524                         cc, &outdata);
00525 
00526         if (colon != NULL)
00527             *colon=':';
00528 
00529         if (retcode) {
00530             DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %s\n",
00531                         error_message(retcode)));
00532             snmp_set_detail(error_message(retcode));
00533             retval = SNMPERR_KRB5;
00534             goto error;
00535         }
00536 
00537         DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully for \"%s/%s\" "
00538                     "(may not be actual ticket sname)\n", service_name,
00539                     parms->session->peername));
00540 
00541     } else {
00542 
00543         /*
00544          * Grab the auth_context from our security state reference
00545          */
00546 
00547         auth_context = ksm_state->auth_context;
00548 
00549         /*
00550          * Bundle up an AP_REP.  Note that we do this only when we
00551          * have a security state reference (which means we're in an agent
00552          * and we're sending a response).
00553          */
00554 
00555         DEBUGMSGTL(("ksm", "KSM: Starting reply processing.\n"));
00556 
00557         retcode = krb5_mk_rep(kcontext, auth_context, &outdata);
00558 
00559         if (retcode) {
00560             DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %s\n",
00561                         error_message(retcode)));
00562             snmp_set_detail(error_message(retcode));
00563             retval = SNMPERR_KRB5;
00564             goto error;
00565         }
00566 
00567         DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()\n"));
00568     }
00569 
00570     /*
00571      * If we have to encrypt the PDU, do that now
00572      */
00573 
00574     if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
00575 
00576         DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.\n"));
00577 
00578         /*
00579          * It's weird -
00580          *
00581          * If we're on the manager, it's a local subkey (because that's in
00582          * our AP_REQ)
00583          *
00584          * If we're on the agent, it's a remote subkey (because that comes
00585          * FROM the received AP_REQ).
00586          */
00587 
00588         if (ksm_state)
00589             retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
00590                                                     &subkey);
00591         else
00592             retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
00593                                                    &subkey);
00594 
00595         if (retcode) {
00596             DEBUGMSGTL(("ksm",
00597                         "KSM: krb5_auth_con_getlocalsubkey failed: %s\n",
00598                         error_message(retcode)));
00599             snmp_set_detail(error_message(retcode));
00600             retval = SNMPERR_KRB5;
00601             goto error;
00602         }
00603 
00604         /*
00605          * Note that here we need to handle different things between the
00606          * old and new crypto APIs.  First, we need to get the final encrypted
00607          * length of the PDU.
00608          */
00609 
00610 #ifdef NETSNMP_USE_KERBEROS_MIT
00611         retcode = krb5_c_encrypt_length(kcontext, subkey->enctype,
00612                                         parms->scopedPduLen,
00613                                         &encrypted_length);
00614 
00615         if (retcode) {
00616             DEBUGMSGTL(("ksm",
00617                         "Encryption length calculation failed: %s\n",
00618                         error_message(retcode)));
00619             snmp_set_detail(error_message(retcode));
00620             retval = SNMPERR_KRB5;
00621             goto error;
00622         }
00623 #elif defined OLD_HEIMDAL
00624         retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto);
00625         if (retcode) {
00626             DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %s\n",
00627                         error_message(retcode)));
00628             snmp_set_detail(error_message(retcode));
00629             retval = SNMPERR_KRB5;
00630             goto error;
00631         }
00632         encrypted_length = krb5_get_wrapped_length(kcontext, heim_crypto,
00633                                                    parms->scopedPduLen);
00634 #else                           /* NETSNMP_USE_KERBEROS_MIT */
00635 
00636         krb5_use_enctype(kcontext, &eblock, subkey->enctype);
00637         retcode = krb5_process_key(kcontext, &eblock, subkey);
00638 
00639         if (retcode) {
00640             DEBUGMSGTL(("ksm", "krb5_process_key failed: %s\n",
00641                         error_message(retcode)));
00642             snmp_set_detail(error_message(retcode));
00643             retval = SNMPERR_KRB5;
00644             goto error;
00645         }
00646 
00647         encrypted_length = krb5_encrypt_size(parms->scopedPduLen,
00648                                              eblock.crypto_entry);
00649 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
00650 
00651 #ifndef OLD_HEIMDAL /* since heimdal allocs the space for us */
00652         encrypted_data = malloc(encrypted_length);
00653 
00654         if (!encrypted_data) {
00655             DEBUGMSGTL(("ksm",
00656                         "KSM: Unable to malloc %d bytes for encrypt "
00657                         "buffer: %s\n", parms->scopedPduLen,
00658                         strerror(errno)));
00659             retval = SNMPERR_MALLOC;
00660 #ifndef NETSNMP_USE_KERBEROS_MIT
00661             krb5_finish_key(kcontext, &eblock);
00662 #endif                          /* ! NETSNMP_USE_KERBEROS_MIT */
00663 
00664             goto error;
00665         }
00666 #endif /* ! OLD_HEIMDAL */
00667 
00668         /*
00669          * We need to set up a blank initialization vector for the encryption.
00670          * Use a block of all zero's (which is dependent on the block size
00671          * of the encryption method).
00672          */
00673 
00674 #ifdef NETSNMP_USE_KERBEROS_MIT
00675 
00676         retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);
00677 
00678         if (retcode) {
00679             DEBUGMSGTL(("ksm",
00680                         "Unable to determine crypto block size: %s\n",
00681                         error_message(retcode)));
00682             snmp_set_detail(error_message(retcode));
00683             retval = SNMPERR_KRB5;
00684             goto error;
00685         }
00686 #elif defined (OLD_HEIMDAL)     /* NETSNMP_USE_KERBEROS_MIT */
00687 #else                           /* NETSNMP_USE_KERBEROS_MIT */
00688 
00689         blocksize =
00690             krb5_enctype_array[subkey->enctype]->system->block_length;
00691 
00692 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
00693 
00694 #ifndef OLD_HEIMDAL     /* since allocs the space for us */
00695         ivector.data = malloc(blocksize);
00696 
00697         if (!ivector.data) {
00698             DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
00699                         blocksize));
00700             retval = SNMPERR_MALLOC;
00701             goto error;
00702         }
00703 
00704         ivector.length = blocksize;
00705         memset(ivector.data, 0, blocksize);
00706 #endif /* OLD_HEIMDAL */
00707 
00708         /*
00709          * Finally!  Do the encryption!
00710          */
00711 
00712 #ifdef NETSNMP_USE_KERBEROS_MIT
00713 
00714         input.data = (char *) parms->scopedPdu;
00715         input.length = parms->scopedPduLen;
00716         output.ciphertext.data = (char *) encrypted_data;
00717         output.ciphertext.length = encrypted_length;
00718 
00719         retcode =
00720             krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
00721                            &ivector, &input, &output);
00722 
00723 #elif defined OLD_HEIMDAL /* NETSNMP_USE_KERBEROS_MIT */
00724 
00725         krb5_data_zero(&encrypted_scoped_pdu);
00726         retcode = krb5_encrypt(kcontext, heim_crypto, KSM_KEY_USAGE_ENCRYPTION,
00727                                parms->scopedPdu, parms->scopedPduLen,
00728                                &encrypted_scoped_pdu);
00729         if (retcode == 0) {
00730                 encrypted_length = encrypted_scoped_pdu.length;
00731                 encrypted_data = encrypted_scoped_pdu.data;
00732                 krb5_data_zero(&encrypted_scoped_pdu);
00733         }
00734 #else                           /* NETSNMP_USE_KERBEROS_MIT */
00735 
00736         retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu,
00737                                (krb5_pointer) encrypted_data,
00738                                parms->scopedPduLen, &eblock, ivector.data);
00739 
00740         krb5_finish_key(kcontext, &eblock);
00741 
00742 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
00743 
00744         if (retcode) {
00745             DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %s\n",
00746                         error_message(retcode)));
00747             retval = SNMPERR_KRB5;
00748             snmp_set_detail(error_message(retcode));
00749             goto error;
00750         }
00751 
00752         *offset = 0;
00753 
00754         rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
00755                                              offset, 1,
00756                                              (u_char) (ASN_UNIVERSAL |
00757                                                        ASN_PRIMITIVE |
00758                                                        ASN_OCTET_STR),
00759                                              encrypted_data,
00760                                              encrypted_length);
00761 
00762         if (rc == 0) {
00763             DEBUGMSGTL(("ksm", "Building encrypted payload failed.\n"));
00764             retval = SNMPERR_TOO_LONG;
00765             goto error;
00766         }
00767 
00768         DEBUGMSGTL(("ksm", "KSM: Encryption complete.\n"));
00769 
00770     } else {
00771         /*
00772          * Plaintext PDU (not encrypted)
00773          */
00774 
00775         if (*parms->wholeMsgLen < parms->scopedPduLen) {
00776             DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.\n"));
00777             retval = SNMPERR_TOO_LONG;
00778             goto error;
00779         }
00780     }
00781 
00782     /*
00783      * Start encoding the msgSecurityParameters
00784      *
00785      * For now, use 0 for the response hint
00786      */
00787 
00788     DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payload\n"));
00789 
00790     seq_offset = *offset;
00791 
00792     rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
00793                                       offset, 1,
00794                                       (u_char) (ASN_UNIVERSAL |
00795                                                 ASN_PRIMITIVE |
00796                                                 ASN_INTEGER),
00797                                       (long *) &zero, sizeof(zero));
00798 
00799     if (rc == 0) {
00800         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
00801         retval = SNMPERR_TOO_LONG;
00802         goto error;
00803     }
00804 
00805     rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
00806                                          offset, 1,
00807                                          (u_char) (ASN_UNIVERSAL |
00808                                                    ASN_PRIMITIVE |
00809                                                    ASN_OCTET_STR),
00810                                          (u_char *) outdata.data,
00811                                          outdata.length);
00812 
00813     if (rc == 0) {
00814         DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.\n"));
00815         retval = SNMPERR_TOO_LONG;
00816         goto error;
00817     }
00818 
00819     /*
00820      * If we didn't encrypt the packet, we haven't yet got the subkey.
00821      * Get that now.
00822      */
00823 
00824     if (!subkey) {
00825         if (ksm_state)
00826             retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
00827                                                     &subkey);
00828         else
00829             retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
00830                                                    &subkey);
00831         if (retcode) {
00832             DEBUGMSGTL(("ksm", "krb5_auth_con_getlocalsubkey failed: %s\n",
00833                         error_message(retcode)));
00834             snmp_set_detail(error_message(retcode));
00835             retval = SNMPERR_KRB5;
00836             goto error;
00837         }
00838 #ifdef OLD_HEIMDAL
00839          retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto);
00840         if (retcode) {
00841             DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %s\n",
00842                         error_message(retcode)));
00843             snmp_set_detail(error_message(retcode));
00844             retval = SNMPERR_KRB5;
00845             goto error;
00846         }
00847 #endif                                  /* OLD_HEIMDAL */
00848     }
00849 
00850     /*
00851      * Now, we need to pick the "right" checksum algorithm.  For old
00852      * crypto, just pick CKSUMTYPE_RSA_MD5_DES; for new crypto, pick
00853      * one of the "approved" ones.
00854      */
00855 
00856 #ifdef NETSNMP_USE_KERBEROS_MIT
00857     retcode = krb5_c_keyed_checksum_types(kcontext, subkey->enctype,
00858                                           &numcksumtypes, &cksumtype_array);
00859 
00860     if (retcode) {
00861         DEBUGMSGTL(("ksm", "Unable to find appropriate keyed checksum: %s\n",
00862                     error_message(retcode)));
00863         snmp_set_detail(error_message(retcode));
00864         retval = SNMPERR_KRB5;
00865         goto error;
00866     }
00867 
00868     if (numcksumtypes <= 0) {
00869         DEBUGMSGTL(("ksm", "We received a list of zero cksumtypes for this "
00870                     "enctype (%d)\n", subkey->enctype));
00871         snmp_set_detail("No valid checksum type for this encryption type");
00872         retval = SNMPERR_KRB5;
00873         goto error;
00874     }
00875 
00876     /*
00877      * It's not clear to me from the API which checksum you're supposed
00878      * to support, so I'm taking a guess at the first one
00879      */
00880 
00881     cksumtype = cksumtype_array[0];
00882 
00883     krb5_free_cksumtypes(kcontext, cksumtype_array);
00884 
00885     DEBUGMSGTL(("ksm", "KSM: Choosing checksum type of %d (subkey type "
00886                 "of %d)\n", cksumtype, subkey->enctype));
00887 
00888     retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize);
00889 
00890     if (retcode) {
00891         DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n",
00892                     error_message(retcode)));
00893         snmp_set_detail(error_message(retcode));
00894         retval = SNMPERR_KRB5;
00895         goto error;
00896     }
00897 
00898     CHECKSUM_LENGTH(&pdu_checksum) = blocksize;
00899 
00900 #else /* NETSNMP_USE_KERBEROS_MIT */
00901     if (ksm_state)
00902         cksumtype = ksm_state->cksumtype;
00903     else
00904 #ifdef OLD_HEIMDAL
00905     {
00906             /* no way to tell what kind of checksum to use without trying */
00907             retval = krb5_create_checksum(kcontext, heim_crypto, 
00908                                           KSM_KEY_USAGE_CHECKSUM, 0,
00909                                           parms->scopedPdu, parms->scopedPduLen,
00910                                           &pdu_checksum);
00911             if (retval) {
00912                     DEBUGMSGTL(("ksm", "Unable to create a checksum: %s\n",
00913                                 error_message(retval)));
00914                     snmp_set_detail(error_message(retcode));
00915                     retval = SNMPERR_KRB5;
00916                     goto error;
00917             }
00918             cksumtype = CHECKSUM_TYPE(&pdu_checksum);
00919     }
00920 #else                                   /* OLD_HEIMDAL */
00921         cksumtype = CKSUMTYPE_RSA_MD5_DES;
00922 #endif                                  /* OLD_HEIMDAL */
00923 
00924 #ifdef OLD_HEIMDAL
00925         if (!krb5_checksum_is_keyed(kcontext, cksumtype)) {
00926 #else                           /* OLD_HEIMDAL */
00927     if (!is_keyed_cksum(cksumtype)) {
00928 #endif                          /* OLD_HEIMDAL */
00929         DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
00930                     cksumtype));
00931         snmp_set_detail("Checksum is not a keyed checksum");
00932         retval = SNMPERR_KRB5;
00933         goto error;
00934     }
00935 
00936 #ifdef OLD_HEIMDAL
00937     if (!krb5_checksum_is_collision_proof(kcontext, cksumtype)) {
00938 #else                           /* OLD_HEIMDAL */
00939     if (!is_coll_proof_cksum(cksumtype)) {
00940 #endif                          /* OLD_HEIMDAL */
00941         DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
00942                     "checksum\n", cksumtype));
00943         snmp_set_detail("Checksum is not a collision-proof checksum");
00944         retval = SNMPERR_KRB5;
00945         goto error;
00946     }
00947 
00948 #ifdef OLD_HEIMDAL
00949     if (CHECKSUM_CONTENTS(&pdu_checksum) != NULL ) {
00950         /* we did the bogus checksum--don't need to ask for the size again
00951          * or initialize cksumtype; just free the bits */
00952         free(CHECKSUM_CONTENTS(&pdu_checksum));
00953         CHECKSUM_CONTENTS(&pdu_checksum) = NULL;
00954     }
00955     else {
00956         retval = krb5_checksumsize(kcontext, cksumtype,
00957                                    &CHECKSUM_LENGTH(&pdu_checksum));
00958         if (retval) {
00959             DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n",
00960                         error_message(retval)));
00961             snmp_set_detail(error_message(retcode));
00962             retval = SNMPERR_KRB5;
00963             goto error;
00964         }
00965 #else                   /* OLD_HEIMDAL */
00966     CHECKSUM_LENGTH(&pdu_checksum) = krb5_checksum_size(kcontext, cksumtype);
00967 #endif                  /* OLD_HEIMDAL */
00968     CHECKSUM_TYPE(&pdu_checksum) = cksumtype;
00969 #ifdef OLD_HEIMDAL
00970     }
00971 #endif                  /* OLD_HEIMDAL */
00972 
00973 #endif /* NETSNMP_USE_KERBEROS_MIT */
00974 
00975     /*
00976      * Note that here, we're just leaving blank space for the checksum;
00977      * we remember where that is, and we'll fill it in later.
00978      */
00979 
00980     *offset += CHECKSUM_LENGTH(&pdu_checksum);
00981     memset(*wholeMsg + *parms->wholeMsgLen - *offset, 0, CHECKSUM_LENGTH(&pdu_checksum));
00982 
00983     cksum_pointer = *wholeMsg + *parms->wholeMsgLen - *offset;
00984 
00985     rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
00986                                          parms->wholeMsgOffset, 1,
00987                                          (u_char) (ASN_UNIVERSAL |
00988                                                    ASN_PRIMITIVE |
00989                                                    ASN_OCTET_STR),
00990                                          CHECKSUM_LENGTH(&pdu_checksum));
00991 
00992     if (rc == 0) {
00993         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
00994         retval = SNMPERR_TOO_LONG;
00995         goto error;
00996     }
00997 
00998     tmp = cksumtype;
00999     rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
01000                                       parms->wholeMsgOffset, 1,
01001                                       (u_char) (ASN_UNIVERSAL |
01002                                                 ASN_PRIMITIVE |
01003                                                 ASN_OCTET_STR),
01004                                       &tmp, sizeof(tmp));
01005 
01006     if (rc == 0) {
01007         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
01008         retval = SNMPERR_TOO_LONG;
01009         goto error;
01010     }
01011 
01012     rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
01013                                            parms->wholeMsgOffset, 1,
01014                                            (u_char) (ASN_SEQUENCE |
01015                                                      ASN_CONSTRUCTOR),
01016                                            *offset - seq_offset);
01017 
01018     if (rc == 0) {
01019         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
01020         retval = SNMPERR_TOO_LONG;
01021         goto error;
01022     }
01023 
01024     rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
01025                                          parms->wholeMsgOffset, 1,
01026                                          (u_char) (ASN_UNIVERSAL |
01027                                                    ASN_PRIMITIVE |
01028                                                    ASN_OCTET_STR),
01029                                          *offset - seq_offset);
01030 
01031     if (rc == 0) {
01032         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
01033         retval = SNMPERR_TOO_LONG;
01034         goto error;
01035     }
01036 
01037     DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completed\n"));
01038 
01039     /*
01040      * We're done with the KSM security parameters - now we do the global
01041      * header and wrap up the whole PDU.
01042      */
01043 
01044     if (*parms->wholeMsgLen < parms->globalDataLen) {
01045         DEBUGMSGTL(("ksm", "Building global data failed.\n"));
01046         retval = SNMPERR_TOO_LONG;
01047         goto error;
01048     }
01049 
01050     *offset += parms->globalDataLen;
01051     memcpy(*wholeMsg + *parms->wholeMsgLen - *offset,
01052            parms->globalData, parms->globalDataLen);
01053 
01054     rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
01055                                            offset, 1,
01056                                            (u_char) (ASN_SEQUENCE |
01057                                                      ASN_CONSTRUCTOR),
01058                                            *offset);
01059 
01060     if (rc == 0) {
01061         DEBUGMSGTL(("ksm", "Building master packet sequence.\n"));
01062         retval = SNMPERR_TOO_LONG;
01063         goto error;
01064     }
01065 
01066     DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.\n"));
01067 
01068     /*
01069      * Now we need to checksum the entire PDU (since it's built).
01070      */
01071 
01072 #ifndef OLD_HEIMDAL /* since heimdal allocs the mem for us */
01073     CHECKSUM_CONTENTS(&pdu_checksum) = malloc(CHECKSUM_LENGTH(&pdu_checksum));
01074 
01075     if (!CHECKSUM_CONTENTS(&pdu_checksum)) {
01076         DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum\n",
01077                     CHECKSUM_LENGTH(&pdu_checksum)));
01078         retval = SNMPERR_MALLOC;
01079         goto error;
01080     }
01081 #endif                                  /* ! OLD_HEIMDAL */
01082 #ifdef NETSNMP_USE_KERBEROS_MIT
01083 
01084     input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset);
01085     input.length = *offset;
01086         retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey,
01087                                        KSM_KEY_USAGE_CHECKSUM, &input,
01088                                        &pdu_checksum);
01089 
01090 #elif defined(OLD_HEIMDAL)      /* NETSNMP_USE_KERBEROS_MIT */
01091 
01092         retcode = krb5_create_checksum(kcontext, heim_crypto,
01093                                        KSM_KEY_USAGE_CHECKSUM, cksumtype,
01094                                        *wholeMsg + *parms->wholeMsgLen
01095                                        - *offset, *offset, &pdu_checksum);
01096 #else                           /* NETSNMP_USE_KERBEROS_MIT */
01097 
01098     retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg +
01099                                       *parms->wholeMsgLen - *offset,
01100                                       *offset,
01101                                       (krb5_pointer) subkey->contents,
01102                                       subkey->length, &pdu_checksum);
01103 
01104 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
01105 
01106     if (retcode) {
01107         DEBUGMSGTL(("ksm", "Calculate checksum failed: %s\n",
01108                     error_message(retcode)));
01109         retval = SNMPERR_KRB5;
01110         snmp_set_detail(error_message(retcode));
01111         goto error;
01112     }
01113 
01114     DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.\n"));
01115 
01116     memcpy(cksum_pointer, CHECKSUM_CONTENTS(&pdu_checksum), CHECKSUM_LENGTH(&pdu_checksum));
01117 
01118     DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %d\n",
01119                 CHECKSUM_LENGTH(&pdu_checksum), cksum_pointer - (*wholeMsg + 1)));
01120 
01121     DEBUGMSGTL(("ksm", "KSM: Checksum:"));
01122 
01123     for (i = 0; i < CHECKSUM_LENGTH(&pdu_checksum); i++)
01124         DEBUGMSG(("ksm", " %02x",
01125                   (unsigned int) CHECKSUM_CONTENTS(&pdu_checksum)[i]));
01126 
01127     DEBUGMSG(("ksm", "\n"));
01128 
01129     /*
01130      * If we're _not_ called as part of a response (null ksm_state),
01131      * then save the auth_context for later using our cache routines.
01132      */
01133 
01134     if (!ksm_state) {
01135         if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context,
01136                                        (u_char *) parms->secName,
01137                                        parms->secNameLen)) !=
01138             SNMPERR_SUCCESS)
01139             goto error;
01140         auth_context = NULL;
01141     }
01142 
01143     DEBUGMSGTL(("ksm", "KSM processing complete!\n"));
01144 
01145   error:
01146 
01147     if (CHECKSUM_CONTENTS(&pdu_checksum))
01148 #ifdef NETSNMP_USE_KERBEROS_MIT
01149         krb5_free_checksum_contents(kcontext, &pdu_checksum);
01150 #else                           /* NETSNMP_USE_KERBEROS_MIT */
01151         free(CHECKSUM_CONTENTS(&pdu_checksum));
01152 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
01153 
01154     if (ivector.data)
01155         free(ivector.data);
01156 
01157     if (subkey)
01158         krb5_free_keyblock(kcontext, subkey);
01159 
01160 #ifdef OLD_HEIMDAL /* OLD_HEIMDAL */
01161     if (heim_crypto)
01162             krb5_crypto_destroy(kcontext, heim_crypto);
01163 #endif /* OLD_HEIMDAL */
01164 
01165     if (encrypted_data)
01166         free(encrypted_data);
01167 
01168     if (cc)
01169         krb5_cc_close(kcontext, cc);
01170 
01171     if (auth_context && !ksm_state)
01172         krb5_auth_con_free(kcontext, auth_context);
01173 
01174     return retval;
01175 }
01176 
01177 /****************************************************************************
01178  *
01179  * ksm_process_in_msg
01180  *
01181  * Parameters:
01182  *      (See list below...)
01183  *
01184  * Returns:
01185  *      KSM_ERR_NO_ERROR                        On success.
01186  *      SNMPERR_KRB5
01187  *      KSM_ERR_GENERIC_ERROR
01188  *      KSM_ERR_UNSUPPORTED_SECURITY_LEVEL
01189  *
01190  *
01191  * Processes an incoming message.
01192  *
01193  ****************************************************************************/
01194 
01195 int
01196 ksm_process_in_msg(struct snmp_secmod_incoming_params *parms)
01197 {
01198     long            temp;
01199     krb5_cksumtype  cksumtype;
01200     krb5_auth_context auth_context = NULL;
01201     krb5_error_code retcode;
01202     krb5_checksum   checksum;
01203     krb5_data       ap_req, ivector;
01204     krb5_flags      flags;
01205     krb5_keyblock  *subkey = NULL;
01206 #ifdef NETSNMP_USE_KERBEROS_MIT
01207     krb5_data       input, output;
01208     krb5_boolean    valid;
01209     krb5_enc_data   in_crypt;
01210 #elif defined OLD_HEIMDAL       /* NETSNMP_USE_KERBEROS_MIT */
01211     krb5_data output;
01212     krb5_crypto heim_crypto = NULL;
01213 #else                           /* NETSNMP_USE_KERBEROS_MIT */
01214     krb5_encrypt_block eblock;
01215 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
01216     krb5_ticket    *ticket = NULL;
01217     int             retval = SNMPERR_SUCCESS, response = 0;
01218     size_t          length =
01219         parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg);
01220     u_char         *current = parms->secParams, type;
01221     size_t          cksumlength, blocksize;
01222     long            hint;
01223     char           *cname;
01224     struct ksm_secStateRef *ksm_state;
01225     struct ksm_cache_entry *entry;
01226 
01227     DEBUGMSGTL(("ksm", "Processing has begun\n"));
01228 
01229     CHECKSUM_CONTENTS(&checksum) = NULL;
01230     ap_req.data = NULL;
01231     ivector.length = 0;
01232     ivector.data = NULL;
01233 
01234     /*
01235      * First, parse the security parameters (because we need the subkey inside
01236      * of the ticket to do anything
01237      */
01238 
01239     if ((current = asn_parse_sequence(current, &length, &type,
01240                                       (ASN_UNIVERSAL | ASN_PRIMITIVE |
01241                                        ASN_OCTET_STR),
01242                                       "ksm first octet")) == NULL) {
01243         DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n"));
01244 
01245         retval = SNMPERR_ASN_PARSE_ERR;
01246         goto error;
01247     }
01248 
01249     if ((current = asn_parse_sequence(current, &length, &type,
01250                                       (ASN_SEQUENCE | ASN_CONSTRUCTOR),
01251                                       "ksm sequence")) == NULL) {
01252         DEBUGMSGTL(("ksm",
01253                     "Security parameter sequence parsing failed\n"));
01254 
01255         retval = SNMPERR_ASN_PARSE_ERR;
01256         goto error;
01257     }
01258 
01259     if ((current = asn_parse_int(current, &length, &type, &temp,
01260                                  sizeof(temp))) == NULL) {
01261         DEBUGMSGTL(("ksm", "Security parameter checksum type parsing"
01262                     "failed\n"));
01263 
01264         retval = SNMPERR_ASN_PARSE_ERR;
01265         goto error;
01266     }
01267 
01268     cksumtype = temp;
01269 
01270 #ifdef NETSNMP_USE_KERBEROS_MIT
01271     if (!krb5_c_valid_cksumtype(cksumtype)) {
01272         DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));
01273 
01274         retval = SNMPERR_KRB5;
01275         snmp_set_detail("Invalid checksum type");
01276         goto error;
01277     }
01278 
01279     if (!krb5_c_is_keyed_cksum(cksumtype)) {
01280         DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
01281                     cksumtype));
01282         snmp_set_detail("Checksum is not a keyed checksum");
01283         retval = SNMPERR_KRB5;
01284         goto error;
01285     }
01286 
01287     if (!krb5_c_is_coll_proof_cksum(cksumtype)) {
01288         DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
01289                     "checksum\n", cksumtype));
01290         snmp_set_detail("Checksum is not a collision-proof checksum");
01291         retval = SNMPERR_KRB5;
01292         goto error;
01293     }
01294 #else /* ! NETSNMP_USE_KERBEROS_MIT */
01295 #ifdef OLD_HEIMDAL
01296     /* kludge */
01297     if (krb5_checksumsize(kcontext, cksumtype, &cksumlength)) {
01298 #else                                   /* OLD_HEIMDAL */
01299     if (!valid_cksumtype(cksumtype)) {
01300 #endif                                  /* OLD_HEIMDAL */
01301         DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));
01302 
01303         retval = SNMPERR_KRB5;
01304         snmp_set_detail("Invalid checksum type");
01305         goto error;
01306     }
01307 
01308 #ifdef OLD_HEIMDAL
01309     if (!krb5_checksum_is_keyed(kcontext, cksumtype)) {
01310 #else                                   /* OLD_HEIMDAL */
01311     if (!is_keyed_cksum(cksumtype)) {
01312 #endif                                  /* OLD_HEIMDAL */
01313         DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
01314                     cksumtype));
01315         snmp_set_detail("Checksum is not a keyed checksum");
01316         retval = SNMPERR_KRB5;
01317         goto error;
01318     }
01319 
01320 #ifdef OLD_HEIMDAL
01321     if (!krb5_checksum_is_collision_proof(kcontext, cksumtype)) {
01322 #else                                   /* OLD_HEIMDAL */
01323     if (!is_coll_proof_cksum(cksumtype)) {
01324 #endif                                  /* OLD_HEIMDAL */
01325         DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
01326                     "checksum\n", cksumtype));
01327         snmp_set_detail("Checksum is not a collision-proof checksum");
01328         retval = SNMPERR_KRB5;
01329         goto error;
01330     }
01331 #endif /* NETSNMP_USE_KERBEROS_MIT */
01332 
01333     CHECKSUM_TYPE(&checksum) = cksumtype;
01334 
01335     cksumlength = length;
01336 
01337     if ((current = asn_parse_sequence(current, &cksumlength, &type,
01338                                       (ASN_UNIVERSAL | ASN_PRIMITIVE |
01339                                        ASN_OCTET_STR), "ksm checksum")) ==
01340         NULL) {
01341         DEBUGMSGTL(("ksm",
01342                     "Security parameter checksum parsing failed\n"));
01343 
01344         retval = SNMPERR_ASN_PARSE_ERR;
01345         goto error;
01346     }
01347 
01348     CHECKSUM_CONTENTS(&checksum) = malloc(cksumlength);
01349     if (!CHECKSUM_CONTENTS(&checksum)) {
01350         DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n",
01351                     cksumlength));
01352         retval = SNMPERR_MALLOC;
01353         goto error;
01354     }
01355 
01356     memcpy(CHECKSUM_CONTENTS(&checksum), current, cksumlength);
01357 
01358     CHECKSUM_LENGTH(&checksum) = cksumlength;
01359     CHECKSUM_TYPE(&checksum) = cksumtype;
01360 
01361     /*
01362      * Zero out the checksum so the validation works correctly
01363      */
01364 
01365     memset(current, 0, cksumlength);
01366 
01367     current += cksumlength;
01368     length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);
01369 
01370     if ((current = asn_parse_sequence(current, &length, &type,
01371                                       (ASN_UNIVERSAL | ASN_PRIMITIVE |
01372                                        ASN_OCTET_STR), "ksm ap_req")) ==
01373         NULL) {
01374         DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing "
01375                     "failed\n"));
01376 
01377         retval = SNMPERR_ASN_PARSE_ERR;
01378         goto error;
01379     }
01380 
01381     ap_req.length = length;
01382     ap_req.data = malloc(length);
01383     if (!ap_req.data) {
01384         DEBUGMSGTL(("ksm",
01385                     "KSM unable to malloc %d bytes for AP_REQ/REP.\n",
01386                     length));
01387         retval = SNMPERR_MALLOC;
01388         goto error;
01389     }
01390 
01391     memcpy(ap_req.data, current, length);
01392 
01393     current += length;
01394     length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);
01395 
01396     if ((current = asn_parse_int(current, &length, &type, &hint,
01397                                  sizeof(hint))) == NULL) {
01398         DEBUGMSGTL(("ksm",
01399                     "KSM security parameter hint parsing failed\n"));
01400 
01401         retval = SNMPERR_ASN_PARSE_ERR;
01402         goto error;
01403     }
01404 
01405     /*
01406      * Okay!  We've got it all!  Now try decoding the damn ticket.
01407      *
01408      * But of course there's a WRINKLE!  We need to figure out if we're
01409      * processing a AP_REQ or an AP_REP.  How do we do that?  We're going
01410      * to cheat, and look at the first couple of bytes (which is what
01411      * the Kerberos library routines do anyway).
01412      *
01413      * If there are ever new Kerberos message formats, we'll need to fix
01414      * this here.
01415      *
01416      * If it's a _response_, then we need to get the auth_context
01417      * from our cache.
01418      */
01419 
01420     if (ap_req.length
01421 #ifndef NETSNMP_USE_KERBEROS_HEIMDAL
01422         && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) {
01423 #else                           /* NETSNMP_USE_KERBEROS_HEIMDAL */
01424         && (((char *)ap_req.data)[0] == 0x6e || ((char *)ap_req.data)[0] == 0x4e)) {
01425 #endif
01426 
01427         /*
01428          * We need to initalize the authorization context, and set the
01429          * replay cache in it (and initialize the replay cache if we
01430          * haven't already
01431          */
01432 
01433         retcode = krb5_auth_con_init(kcontext, &auth_context);
01434 
01435         if (retcode) {
01436             DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n",
01437                         error_message(retcode)));
01438             retval = SNMPERR_KRB5;
01439             snmp_set_detail(error_message(retcode));
01440             goto error;
01441         }
01442 
01443         if (!rcache) {
01444             krb5_data       server;
01445             server.data = "host";
01446             server.length = strlen(server.data);
01447 
01448             retcode = krb5_get_server_rcache(kcontext, &server, &rcache);
01449 
01450             if (retcode) {
01451                 DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n",
01452                             error_message(retcode)));
01453                 retval = SNMPERR_KRB5;
01454                 snmp_set_detail(error_message(retcode));
01455                 goto error;
01456             }
01457         }
01458 
01459         retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache);
01460 
01461         if (retcode) {
01462             DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n",
01463                         error_message(retcode)));
01464             retval = SNMPERR_KRB5;
01465             snmp_set_detail(error_message(retcode));
01466             goto error;
01467         }
01468 
01469         retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL,
01470                               keytab, &flags, &ticket);
01471 
01472         krb5_auth_con_setrcache(kcontext, auth_context, NULL);
01473 
01474         if (retcode) {
01475             DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n",
01476                         error_message(retcode)));
01477             retval = SNMPERR_KRB5;
01478             snmp_set_detail(error_message(retcode));
01479             goto error;
01480         }
01481 
01482         retcode =
01483             krb5_unparse_name(kcontext, TICKET_CLIENT(ticket), &cname);
01484 
01485         if (retcode == 0) {
01486             DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n",
01487                         cname));
01488             free(cname);
01489         }
01490 
01491         /*
01492          * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set
01493          */
01494 
01495         if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) {
01496             DEBUGMSGTL(("ksm",
01497                         "KSM MUTUAL_REQUIRED not set in request!\n"));
01498             retval = SNMPERR_KRB5;
01499             snmp_set_detail("MUTUAL_REQUIRED not set in message");
01500             goto error;
01501         }
01502 
01503         retcode =
01504             krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey);
01505 
01506         if (retcode) {
01507             DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n",
01508                         error_message(retcode)));
01509             retval = SNMPERR_KRB5;
01510             snmp_set_detail(error_message(retcode));
01511             goto error;
01512         }
01513 
01514 #ifndef NETSNMP_USE_KERBEROS_HEIMDAL
01515     } else if (ap_req.length && (ap_req.data[0] == 0x6f ||
01516                                  ap_req.data[0] == 0x4f)) {
01517 #else                           /* NETSNMP_USE_KERBEROS_HEIMDAL */
01518     } else if (ap_req.length && (((char *)ap_req.data)[0] == 0x6f ||
01519                                  ((char *)ap_req.data)[0] == 0x4f)) {
01520 #endif                          /* NETSNMP_USE_KERBEROS_HEIMDAL */
01521         /*
01522          * Looks like a response; let's see if we've got that auth_context
01523          * in our cache.
01524          */
01525 
01526         krb5_ap_rep_enc_part *repl = NULL;
01527 
01528         response = 1;
01529 
01530         entry = ksm_get_cache(parms->pdu->msgid);
01531 
01532         if (!entry) {
01533             DEBUGMSGTL(("ksm",
01534                         "KSM: Unable to find auth_context for PDU with "
01535                         "message ID of %ld\n", parms->pdu->msgid));
01536             retval = SNMPERR_KRB5;
01537             goto error;
01538         }
01539 
01540         auth_context = entry->auth_context;
01541 
01542         /*
01543          * In that case, let's call the rd_rep function
01544          */
01545 
01546         retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl);
01547 
01548         if (repl)
01549             krb5_free_ap_rep_enc_part(kcontext, repl);
01550 
01551         if (retcode) {
01552             DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n",
01553                         error_message(retcode)));
01554             retval = SNMPERR_KRB5;
01555             goto error;
01556         }
01557 
01558         DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n"));
01559 
01560         retcode =
01561             krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey);
01562 
01563         if (retcode) {
01564             DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n",
01565                         error_message(retcode)));
01566             retval = SNMPERR_KRB5;
01567             snmp_set_detail("Unable to retrieve local subkey");
01568             goto error;
01569         }
01570 
01571     } else {
01572 #ifndef NETSNMP_USE_KERBEROS_HEIMDAL
01573         DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n",
01574                     ap_req.data[0]));
01575 #else                           /* NETSNMP_USE_KERBEROS_HEIMDAL */
01576          DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n",
01577                     ((char *)ap_req.data)[0]));
01578 #endif
01579         retval = SNMPERR_KRB5;
01580         snmp_set_detail("Unknown Kerberos message type");
01581         goto error;
01582     }
01583 
01584 #ifdef NETSNMP_USE_KERBEROS_MIT
01585     input.data = (char *) parms->wholeMsg;
01586     input.length = parms->wholeMsgLen;
01587 
01588     retcode =
01589         krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM,
01590                                &input, &checksum, &valid);
01591 #elif defined(OLD_HEIMDAL)      /* NETSNMP_USE_KERBEROS_MIT */
01592     retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto);
01593     if (retcode) {
01594             DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %s\n",
01595                         error_message(retcode)));
01596             snmp_set_detail(error_message(retcode));
01597             retval = SNMPERR_KRB5;
01598             goto error;
01599     }
01600     retcode = krb5_verify_checksum(kcontext, heim_crypto,
01601                                    KSM_KEY_USAGE_CHECKSUM, parms->wholeMsg,
01602                                    parms->wholeMsgLen, &checksum);
01603 #else                           /* NETSNMP_USE_KERBEROS_MIT */
01604     retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum,
01605                                    parms->wholeMsg, parms->wholeMsgLen,
01606                                    (krb5_pointer) subkey->contents,
01607                                    subkey->length);
01608 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
01609 
01610     if (retcode) {
01611         DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n",
01612                     error_message(retcode)));
01613         retval = SNMPERR_KRB5;
01614         snmp_set_detail(error_message(retcode));
01615         goto error;
01616     }
01617 
01618     /*
01619      * Don't ask me why they didn't simply return an error, but we have
01620      * to check to see if "valid" is false.
01621      */
01622 
01623 #ifdef NETSNMP_USE_KERBEROS_MIT
01624     if (!valid) {
01625         DEBUGMSGTL(("ksm", "Computed checksum did not match supplied "
01626                     "checksum!\n"));
01627         retval = SNMPERR_KRB5;
01628         snmp_set_detail
01629             ("Computed checksum did not match supplied checksum");
01630         goto error;
01631     }
01632 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
01633 
01634     /*
01635      * Handle an encrypted PDU.  Note that it's an OCTET_STRING of the
01636      * output of whatever Kerberos cryptosystem you're using (defined by
01637      * the encryption type).  Note that this is NOT the EncryptedData
01638      * sequence - it's what goes in the "cipher" field of EncryptedData.
01639      */
01640 
01641     if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
01642 
01643         if ((current = asn_parse_sequence(current, &length, &type,
01644                                           (ASN_UNIVERSAL | ASN_PRIMITIVE |
01645                                            ASN_OCTET_STR), "ksm pdu")) ==
01646             NULL) {
01647             DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n"));
01648             retval = SNMPERR_ASN_PARSE_ERR;
01649             goto error;
01650         }
01651 
01652         /*
01653          * The PDU is now pointed at by "current", and the length is in
01654          * "length".
01655          */
01656 
01657         DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n"));
01658 
01659         /*
01660          * We need to set up a blank initialization vector for the decryption.
01661          * Use a block of all zero's (which is dependent on the block size
01662          * of the encryption method).
01663          */
01664 
01665 #ifdef NETSNMP_USE_KERBEROS_MIT
01666 
01667         retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);
01668 
01669         if (retcode) {
01670             DEBUGMSGTL(("ksm",
01671                         "Unable to determine crypto block size: %s\n",
01672                         error_message(retcode)));
01673             snmp_set_detail(error_message(retcode));
01674             retval = SNMPERR_KRB5;
01675             goto error;
01676         }
01677 #elif defined(OLD_HEIMDAL)      /* NETSNMP_USE_KERBEROS_MIT */
01678 #else                           /* NETSNMP_USE_KERBEROS_MIT */
01679 
01680         blocksize =
01681             krb5_enctype_array[subkey->enctype]->system->block_length;
01682 
01683 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
01684 
01685 #ifndef OLD_HEIMDAL
01686         ivector.data = malloc(blocksize);
01687 
01688         if (!ivector.data) {
01689             DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
01690                         blocksize));
01691             retval = SNMPERR_MALLOC;
01692             goto error;
01693         }
01694 
01695         ivector.length = blocksize;
01696         memset(ivector.data, 0, blocksize);
01697 
01698 #ifndef NETSNMP_USE_KERBEROS_MIT
01699 
01700         krb5_use_enctype(kcontext, &eblock, subkey->enctype);
01701 
01702         retcode = krb5_process_key(kcontext, &eblock, subkey);
01703 
01704         if (retcode) {
01705             DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n",
01706                         error_message(retcode)));
01707             snmp_set_detail(error_message(retcode));
01708             retval = SNMPERR_KRB5;
01709             goto error;
01710         }
01711 #endif                          /* !NETSNMP_USE_KERBEROS_MIT */
01712 
01713 #endif /* ! OLD_HEIMDAL */
01714 
01715         if (length > *parms->scopedPduLen) {
01716             DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to "
01717                         "decrypt but only %d bytes available\n", length,
01718                         *parms->scopedPduLen));
01719             retval = SNMPERR_TOO_LONG;
01720 #ifndef NETSNMP_USE_KERBEROS_MIT
01721 #ifndef OLD_HEIMDAL
01722             krb5_finish_key(kcontext, &eblock);
01723 #endif                          /* ! OLD_HEIMDAL */
01724 #endif                          /* ! NETSNMP_USE_KERBEROS_MIT */
01725             goto error;
01726         }
01727 #ifdef NETSNMP_USE_KERBEROS_MIT
01728         in_crypt.ciphertext.data = (char *) current;
01729         in_crypt.ciphertext.length = length;
01730         in_crypt.enctype = subkey->enctype;
01731         output.data = (char *) *parms->scopedPdu;
01732         output.length = *parms->scopedPduLen;
01733 
01734         retcode =
01735             krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
01736                            &ivector, &in_crypt, &output);
01737 #elif defined (OLD_HEIMDAL)     /* NETSNMP_USE_KERBEROS_MIT */
01738         retcode = krb5_decrypt(kcontext, heim_crypto, KSM_KEY_USAGE_ENCRYPTION,
01739                                current, length, &output);
01740         if (retcode == 0) {
01741                 *parms->scopedPdu = (char *) output.data;
01742                 *parms->scopedPduLen = output.length;
01743                 krb5_data_zero(&output);
01744         }
01745 #else                           /* NETSNMP_USE_KERBEROS_MIT */
01746 
01747         retcode = krb5_decrypt(kcontext, (krb5_pointer) current,
01748                                *parms->scopedPdu, length, &eblock,
01749                                ivector.data);
01750 
01751         krb5_finish_key(kcontext, &eblock);
01752 
01753 #endif                          /* NETSNMP_USE_KERBEROS_MIT */
01754 
01755         if (retcode) {
01756             DEBUGMSGTL(("ksm", "Decryption failed: %s\n",
01757                         error_message(retcode)));
01758             snmp_set_detail(error_message(retcode));
01759             retval = SNMPERR_KRB5;
01760             goto error;
01761         }
01762 
01763         *parms->scopedPduLen = length;
01764 
01765     } else {
01766         /*
01767          * Clear PDU
01768          */
01769 
01770         *parms->scopedPdu = current;
01771         *parms->scopedPduLen =
01772             parms->wholeMsgLen - (current - parms->wholeMsg);
01773     }
01774 
01775     /*
01776      * A HUGE GROSS HACK
01777      */
01778 
01779     *parms->maxSizeResponse = parms->maxMsgSize - 200;
01780 
01781     DEBUGMSGTL(("ksm", "KSM processing complete\n"));
01782 
01783     /*
01784      * Set the secName to the right value (a hack for now).  But that's
01785      * only used for when we're processing a request, not a response.
01786      */
01787 
01788     if (!response) {
01789 
01790         retcode = krb5_unparse_name(kcontext, TICKET_CLIENT(ticket),
01791                                     &cname);
01792 
01793         if (retcode) {
01794             DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n",
01795                         error_message(retcode)));
01796             snmp_set_detail(error_message(retcode));
01797             retval = SNMPERR_KRB5;
01798             goto error;
01799         }
01800 
01801         if (strlen(cname) > *parms->secNameLen + 1) {
01802             DEBUGMSGTL(("ksm",
01803                         "KSM: Principal length (%d) is too long (%d)\n",
01804                         strlen(cname), parms->secNameLen));
01805             retval = SNMPERR_TOO_LONG;
01806             free(cname);
01807             goto error;
01808         }
01809 
01810         strcpy(parms->secName, cname);
01811         *parms->secNameLen = strlen(cname);
01812 
01813         free(cname);
01814 
01815         /*
01816          * Also, if we're not a response, keep around our auth_context so we
01817          * can encode the reply message correctly
01818          */
01819 
01820         ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef);
01821 
01822         if (!ksm_state) {
01823             DEBUGMSGTL(("ksm", "KSM unable to malloc memory for "
01824                         "ksm_secStateRef\n"));
01825             retval = SNMPERR_MALLOC;
01826             goto error;
01827         }
01828 
01829         ksm_state->auth_context = auth_context;
01830         auth_context = NULL;
01831         ksm_state->cksumtype = cksumtype;
01832 
01833         *parms->secStateRef = ksm_state;
01834     } else {
01835 
01836         /*
01837          * We _still_ have to set the secName in process_in_msg().  Do
01838          * that now with what we were passed in before (we cached it,
01839          * remember?)
01840          */
01841 
01842         memcpy(parms->secName, entry->secName, entry->secNameLen);
01843         *parms->secNameLen = entry->secNameLen;
01844     }
01845 
01846     /*
01847      * Just in case
01848      */
01849 
01850     parms->secEngineID = (u_char *) "";
01851     *parms->secEngineIDLen = 0;
01852 
01853     auth_context = NULL;        /* So we don't try to free it on success */
01854 
01855   error:
01856     if (retval == SNMPERR_ASN_PARSE_ERR &&
01857         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0)
01858         DEBUGMSGTL(("ksm", "Failed to increment statistics.\n"));
01859 
01860     if (subkey)
01861         krb5_free_keyblock(kcontext, subkey);
01862 
01863 #ifdef OLD_HEIMDAL /* OLD_HEIMDAL */
01864     if (heim_crypto)
01865             krb5_crypto_destroy(kcontext, heim_crypto);
01866 #endif /* OLD_HEIMDAL */
01867 
01868     if (CHECKSUM_CONTENTS(&checksum))
01869         free(CHECKSUM_CONTENTS(&checksum));
01870 
01871     if (ivector.data)
01872         free(ivector.data);
01873 
01874     if (ticket)
01875         krb5_free_ticket(kcontext, ticket);
01876 
01877     if (!response && auth_context)
01878         krb5_auth_con_free(kcontext, auth_context);
01879 
01880     if (ap_req.data)
01881         free(ap_req.data);
01882 
01883     return retval;
01884 }