net-snmp 5.7
|
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 }