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

net-snmpに対してSat Sep 5 13:14:26 2009に生成されました。  doxygen 1.4.7