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 NETSNMP_USE_KERBEROS_HEIMDAL 00049 #ifndef NETSNMP_USE_KERBEROS_MIT 00050 #define OLD_HEIMDAL 00051 #endif /* ! NETSNMP_USE_KERBEROS_MIT */ 00052 #endif /* NETSNMP_USE_KERBEROS_HEIMDAL */ 00053 00054 #ifdef NETSNMP_USE_KERBEROS_HEIMDAL 00055 #define oid heimdal_oid_renamed 00056 #endif /* NETSNMP_USE_KERBEROS_HEIMDAL */ 00057 #include <krb5.h> 00058 #include <com_err.h> 00059 #ifdef NETSNMP_USE_KERBEROS_HEIMDAL 00060 #undef oid 00061 #endif /* NETSNMP_USE_KERBEROS_HEIMDAL */ 00062 00063 #ifdef NETSNMP_USE_KERBEROS_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 /* NETSNMP_USE_KERBEROS_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 /* NETSNMP_USE_KERBEROS_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 NETSNMP_USE_KERBEROS_MIT 00453 krb5_data input; 00454 krb5_enc_data output; 00455 unsigned int numcksumtypes; 00456 krb5_cksumtype *cksumtype_array; 00457 #elif defined OLD_HEIMDAL /* NETSNMP_USE_KERBEROS_MIT */ 00458 krb5_crypto heim_crypto = NULL; 00459 #else /* NETSNMP_USE_KERBEROS_MIT */ 00460 krb5_encrypt_block eblock; 00461 #endif /* NETSNMP_USE_KERBEROS_MIT */ 00462 size_t blocksize, encrypted_length; 00463 unsigned char *encrypted_data = NULL; 00464 long zero = 0, tmp; 00465 int i; 00466 u_char *cksum_pointer, *endp = *parms->wholeMsg; 00467 krb5_cksumtype cksumtype; 00468 krb5_checksum pdu_checksum; 00469 u_char **wholeMsg = parms->wholeMsg; 00470 size_t *offset = parms->wholeMsgOffset, seq_offset; 00471 struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *) 00472 parms->secStateRef; 00473 #ifdef OLD_HEIMDAL 00474 krb5_data encrypted_scoped_pdu; 00475 #endif /* OLD_HEIMDAL */ 00476 int rc; 00477 char *colon = NULL; 00478 00479 DEBUGMSGTL(("ksm", "Starting KSM processing\n")); 00480 00481 outdata.length = 0; 00482 outdata.data = NULL; 00483 ivector.length = 0; 00484 ivector.data = NULL; 00485 CHECKSUM_CONTENTS(&pdu_checksum) = NULL; 00486 00487 if (!ksm_state) { 00488 /* 00489 * If we've got a port number as part of the "peername", then 00490 * suppress this (temporarily) while we build the credential info. 00491 * XXX - what about "udp:host" style addresses? 00492 */ 00493 colon = strrchr(params->session->peername, ':'); 00494 if (colon != NULL) { 00495 *colon='\0'; 00496 } 00497 00498 /* 00499 * If we don't have a ksm_state, then we're a request. Get a 00500 * credential cache and build a ap_req. 00501 */ 00502 retcode = krb5_cc_default(kcontext, &cc); 00503 00504 if (retcode) { 00505 DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %s\n", 00506 error_message(retcode))); 00507 snmp_set_detail(error_message(retcode)); 00508 retval = SNMPERR_KRB5; 00509 goto error; 00510 } 00511 00512 DEBUGMSGTL(("ksm", "KSM: Set credential cache successfully\n")); 00513 00514 /* 00515 * This seems odd, since we don't need this until later (or earlier, 00516 * depending on how you look at it), but because the most likely 00517 * errors are Kerberos at this point, I'll get this now to save 00518 * time not encoding the rest of the packet. 00519 * 00520 * Also, we need the subkey to encrypt the PDU (if required). 00521 */ 00522 00523 retcode = 00524 krb5_mk_req(kcontext, &auth_context, 00525 AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, 00526 (char *) service_name, parms->session->peername, NULL, 00527 cc, &outdata); 00528 00529 if (colon != NULL) 00530 *colon=':'; 00531 00532 if (retcode) { 00533 DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %s\n", 00534 error_message(retcode))); 00535 snmp_set_detail(error_message(retcode)); 00536 retval = SNMPERR_KRB5; 00537 goto error; 00538 } 00539 00540 DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully for \"%s/%s\" " 00541 "(may not be actual ticket sname)\n", service_name, 00542 parms->session->peername)); 00543 00544 } else { 00545 00546 /* 00547 * Grab the auth_context from our security state reference 00548 */ 00549 00550 auth_context = ksm_state->auth_context; 00551 00552 /* 00553 * Bundle up an AP_REP. Note that we do this only when we 00554 * have a security state reference (which means we're in an agent 00555 * and we're sending a response). 00556 */ 00557 00558 DEBUGMSGTL(("ksm", "KSM: Starting reply processing.\n")); 00559 00560 retcode = krb5_mk_rep(kcontext, auth_context, &outdata); 00561 00562 if (retcode) { 00563 DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %s\n", 00564 error_message(retcode))); 00565 snmp_set_detail(error_message(retcode)); 00566 retval = SNMPERR_KRB5; 00567 goto error; 00568 } 00569 00570 DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()\n")); 00571 } 00572 00573 /* 00574 * If we have to encrypt the PDU, do that now 00575 */ 00576 00577 if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { 00578 00579 DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.\n")); 00580 00581 /* 00582 * It's weird - 00583 * 00584 * If we're on the manager, it's a local subkey (because that's in 00585 * our AP_REQ) 00586 * 00587 * If we're on the agent, it's a remote subkey (because that comes 00588 * FROM the received AP_REQ). 00589 */ 00590 00591 if (ksm_state) 00592 retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, 00593 &subkey); 00594 else 00595 retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, 00596 &subkey); 00597 00598 if (retcode) { 00599 DEBUGMSGTL(("ksm", 00600 "KSM: krb5_auth_con_getlocalsubkey failed: %s\n", 00601 error_message(retcode))); 00602 snmp_set_detail(error_message(retcode)); 00603 retval = SNMPERR_KRB5; 00604 goto error; 00605 } 00606 00607 /* 00608 * Note that here we need to handle different things between the 00609 * old and new crypto APIs. First, we need to get the final encrypted 00610 * length of the PDU. 00611 */ 00612 00613 #ifdef NETSNMP_USE_KERBEROS_MIT 00614 retcode = krb5_c_encrypt_length(kcontext, subkey->enctype, 00615 parms->scopedPduLen, 00616 &encrypted_length); 00617 00618 if (retcode) { 00619 DEBUGMSGTL(("ksm", 00620 "Encryption length calculation failed: %s\n", 00621 error_message(retcode))); 00622 snmp_set_detail(error_message(retcode)); 00623 retval = SNMPERR_KRB5; 00624 goto error; 00625 } 00626 #elif defined OLD_HEIMDAL 00627 retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto); 00628 if (retcode) { 00629 DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %s\n", 00630 error_message(retcode))); 00631 snmp_set_detail(error_message(retcode)); 00632 retval = SNMPERR_KRB5; 00633 goto error; 00634 } 00635 encrypted_length = krb5_get_wrapped_length(kcontext, heim_crypto, 00636 parms->scopedPduLen); 00637 #else /* NETSNMP_USE_KERBEROS_MIT */ 00638 00639 krb5_use_enctype(kcontext, &eblock, subkey->enctype); 00640 retcode = krb5_process_key(kcontext, &eblock, subkey); 00641 00642 if (retcode) { 00643 DEBUGMSGTL(("ksm", "krb5_process_key failed: %s\n", 00644 error_message(retcode))); 00645 snmp_set_detail(error_message(retcode)); 00646 retval = SNMPERR_KRB5; 00647 goto error; 00648 } 00649 00650 encrypted_length = krb5_encrypt_size(parms->scopedPduLen, 00651 eblock.crypto_entry); 00652 #endif /* NETSNMP_USE_KERBEROS_MIT */ 00653 00654 #ifndef OLD_HEIMDAL /* since heimdal allocs the space for us */ 00655 encrypted_data = malloc(encrypted_length); 00656 00657 if (!encrypted_data) { 00658 DEBUGMSGTL(("ksm", 00659 "KSM: Unable to malloc %d bytes for encrypt " 00660 "buffer: %s\n", parms->scopedPduLen, 00661 strerror(errno))); 00662 retval = SNMPERR_MALLOC; 00663 #ifndef NETSNMP_USE_KERBEROS_MIT 00664 krb5_finish_key(kcontext, &eblock); 00665 #endif /* ! NETSNMP_USE_KERBEROS_MIT */ 00666 00667 goto error; 00668 } 00669 #endif /* ! OLD_HEIMDAL */ 00670 00671 /* 00672 * We need to set up a blank initialization vector for the encryption. 00673 * Use a block of all zero's (which is dependent on the block size 00674 * of the encryption method). 00675 */ 00676 00677 #ifdef NETSNMP_USE_KERBEROS_MIT 00678 00679 retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize); 00680 00681 if (retcode) { 00682 DEBUGMSGTL(("ksm", 00683 "Unable to determine crypto block size: %s\n", 00684 error_message(retcode))); 00685 snmp_set_detail(error_message(retcode)); 00686 retval = SNMPERR_KRB5; 00687 goto error; 00688 } 00689 #elif defined (OLD_HEIMDAL) /* NETSNMP_USE_KERBEROS_MIT */ 00690 #else /* NETSNMP_USE_KERBEROS_MIT */ 00691 00692 blocksize = 00693 krb5_enctype_array[subkey->enctype]->system->block_length; 00694 00695 #endif /* NETSNMP_USE_KERBEROS_MIT */ 00696 00697 #ifndef OLD_HEIMDAL /* since allocs the space for us */ 00698 ivector.data = malloc(blocksize); 00699 00700 if (!ivector.data) { 00701 DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n", 00702 blocksize)); 00703 retval = SNMPERR_MALLOC; 00704 goto error; 00705 } 00706 00707 ivector.length = blocksize; 00708 memset(ivector.data, 0, blocksize); 00709 #endif /* OLD_HEIMDAL */ 00710 00711 /* 00712 * Finally! Do the encryption! 00713 */ 00714 00715 #ifdef NETSNMP_USE_KERBEROS_MIT 00716 00717 input.data = (char *) parms->scopedPdu; 00718 input.length = parms->scopedPduLen; 00719 output.ciphertext.data = (char *) encrypted_data; 00720 output.ciphertext.length = encrypted_length; 00721 00722 retcode = 00723 krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, 00724 &ivector, &input, &output); 00725 00726 #elif defined OLD_HEIMDAL /* NETSNMP_USE_KERBEROS_MIT */ 00727 00728 krb5_data_zero(&encrypted_scoped_pdu); 00729 retcode = krb5_encrypt(kcontext, heim_crypto, KSM_KEY_USAGE_ENCRYPTION, 00730 parms->scopedPdu, parms->scopedPduLen, 00731 &encrypted_scoped_pdu); 00732 if (retcode == 0) { 00733 encrypted_length = encrypted_scoped_pdu.length; 00734 encrypted_data = encrypted_scoped_pdu.data; 00735 krb5_data_zero(&encrypted_scoped_pdu); 00736 } 00737 #else /* NETSNMP_USE_KERBEROS_MIT */ 00738 00739 retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu, 00740 (krb5_pointer) encrypted_data, 00741 parms->scopedPduLen, &eblock, ivector.data); 00742 00743 krb5_finish_key(kcontext, &eblock); 00744 00745 #endif /* NETSNMP_USE_KERBEROS_MIT */ 00746 00747 if (retcode) { 00748 DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %s\n", 00749 error_message(retcode))); 00750 retval = SNMPERR_KRB5; 00751 snmp_set_detail(error_message(retcode)); 00752 goto error; 00753 } 00754 00755 *offset = 0; 00756 00757 rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen, 00758 offset, 1, 00759 (u_char) (ASN_UNIVERSAL | 00760 ASN_PRIMITIVE | 00761 ASN_OCTET_STR), 00762 encrypted_data, 00763 encrypted_length); 00764 00765 if (rc == 0) { 00766 DEBUGMSGTL(("ksm", "Building encrypted payload failed.\n")); 00767 retval = SNMPERR_TOO_LONG; 00768 goto error; 00769 } 00770 00771 DEBUGMSGTL(("ksm", "KSM: Encryption complete.\n")); 00772 00773 } else { 00774 /* 00775 * Plaintext PDU (not encrypted) 00776 */ 00777 00778 if (*parms->wholeMsgLen < parms->scopedPduLen) { 00779 DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.\n")); 00780 retval = SNMPERR_TOO_LONG; 00781 goto error; 00782 } 00783 } 00784 00785 /* 00786 * Start encoding the msgSecurityParameters 00787 * 00788 * For now, use 0 for the response hint 00789 */ 00790 00791 DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payload\n")); 00792 00793 seq_offset = *offset; 00794 00795 rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen, 00796 offset, 1, 00797 (u_char) (ASN_UNIVERSAL | 00798 ASN_PRIMITIVE | 00799 ASN_INTEGER), 00800 (long *) &zero, sizeof(zero)); 00801 00802 if (rc == 0) { 00803 DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); 00804 retval = SNMPERR_TOO_LONG; 00805 goto error; 00806 } 00807 00808 rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen, 00809 offset, 1, 00810 (u_char) (ASN_UNIVERSAL | 00811 ASN_PRIMITIVE | 00812 ASN_OCTET_STR), 00813 (u_char *) outdata.data, 00814 outdata.length); 00815 00816 if (rc == 0) { 00817 DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.\n")); 00818 retval = SNMPERR_TOO_LONG; 00819 goto error; 00820 } 00821 00822 /* 00823 * If we didn't encrypt the packet, we haven't yet got the subkey. 00824 * Get that now. 00825 */ 00826 00827 if (!subkey) { 00828 if (ksm_state) 00829 retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, 00830 &subkey); 00831 else 00832 retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, 00833 &subkey); 00834 if (retcode) { 00835 DEBUGMSGTL(("ksm", "krb5_auth_con_getlocalsubkey failed: %s\n", 00836 error_message(retcode))); 00837 snmp_set_detail(error_message(retcode)); 00838 retval = SNMPERR_KRB5; 00839 goto error; 00840 } 00841 #ifdef OLD_HEIMDAL 00842 retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto); 00843 if (retcode) { 00844 DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %s\n", 00845 error_message(retcode))); 00846 snmp_set_detail(error_message(retcode)); 00847 retval = SNMPERR_KRB5; 00848 goto error; 00849 } 00850 #endif /* OLD_HEIMDAL */ 00851 } 00852 00853 /* 00854 * Now, we need to pick the "right" checksum algorithm. For old 00855 * crypto, just pick CKSUMTYPE_RSA_MD5_DES; for new crypto, pick 00856 * one of the "approved" ones. 00857 */ 00858 00859 #ifdef NETSNMP_USE_KERBEROS_MIT 00860 retcode = krb5_c_keyed_checksum_types(kcontext, subkey->enctype, 00861 &numcksumtypes, &cksumtype_array); 00862 00863 if (retcode) { 00864 DEBUGMSGTL(("ksm", "Unable to find appropriate keyed checksum: %s\n", 00865 error_message(retcode))); 00866 snmp_set_detail(error_message(retcode)); 00867 retval = SNMPERR_KRB5; 00868 goto error; 00869 } 00870 00871 if (numcksumtypes <= 0) { 00872 DEBUGMSGTL(("ksm", "We received a list of zero cksumtypes for this " 00873 "enctype (%d)\n", subkey->enctype)); 00874 snmp_set_detail("No valid checksum type for this encryption type"); 00875 retval = SNMPERR_KRB5; 00876 goto error; 00877 } 00878 00879 /* 00880 * It's not clear to me from the API which checksum you're supposed 00881 * to support, so I'm taking a guess at the first one 00882 */ 00883 00884 cksumtype = cksumtype_array[0]; 00885 00886 krb5_free_cksumtypes(kcontext, cksumtype_array); 00887 00888 DEBUGMSGTL(("ksm", "KSM: Choosing checksum type of %d (subkey type " 00889 "of %d)\n", cksumtype, subkey->enctype)); 00890 00891 retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize); 00892 00893 if (retcode) { 00894 DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n", 00895 error_message(retcode))); 00896 snmp_set_detail(error_message(retcode)); 00897 retval = SNMPERR_KRB5; 00898 goto error; 00899 } 00900 00901 CHECKSUM_LENGTH(&pdu_checksum) = blocksize; 00902 00903 #else /* NETSNMP_USE_KERBEROS_MIT */ 00904 if (ksm_state) 00905 cksumtype = ksm_state->cksumtype; 00906 else 00907 #ifdef OLD_HEIMDAL 00908 { 00909 /* no way to tell what kind of checksum to use without trying */ 00910 retval = krb5_create_checksum(kcontext, heim_crypto, 00911 KSM_KEY_USAGE_CHECKSUM, 0, 00912 parms->scopedPdu, parms->scopedPduLen, 00913 &pdu_checksum); 00914 if (retval) { 00915 DEBUGMSGTL(("ksm", "Unable to create a checksum: %s\n", 00916 error_message(retval))); 00917 snmp_set_detail(error_message(retcode)); 00918 retval = SNMPERR_KRB5; 00919 goto error; 00920 } 00921 cksumtype = CHECKSUM_TYPE(&pdu_checksum); 00922 } 00923 #else /* OLD_HEIMDAL */ 00924 cksumtype = CKSUMTYPE_RSA_MD5_DES; 00925 #endif /* OLD_HEIMDAL */ 00926 00927 #ifdef OLD_HEIMDAL 00928 if (!krb5_checksum_is_keyed(kcontext, cksumtype)) { 00929 #else /* OLD_HEIMDAL */ 00930 if (!is_keyed_cksum(cksumtype)) { 00931 #endif /* OLD_HEIMDAL */ 00932 DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", 00933 cksumtype)); 00934 snmp_set_detail("Checksum is not a keyed checksum"); 00935 retval = SNMPERR_KRB5; 00936 goto error; 00937 } 00938 00939 #ifdef OLD_HEIMDAL 00940 if (!krb5_checksum_is_collision_proof(kcontext, cksumtype)) { 00941 #else /* OLD_HEIMDAL */ 00942 if (!is_coll_proof_cksum(cksumtype)) { 00943 #endif /* OLD_HEIMDAL */ 00944 DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " 00945 "checksum\n", cksumtype)); 00946 snmp_set_detail("Checksum is not a collision-proof checksum"); 00947 retval = SNMPERR_KRB5; 00948 goto error; 00949 } 00950 00951 #ifdef OLD_HEIMDAL 00952 if (CHECKSUM_CONTENTS(&pdu_checksum) != NULL ) { 00953 /* we did the bogus checksum--don't need to ask for the size again 00954 * or initialize cksumtype; just free the bits */ 00955 free(CHECKSUM_CONTENTS(&pdu_checksum)); 00956 CHECKSUM_CONTENTS(&pdu_checksum) = NULL; 00957 } 00958 else { 00959 retval = krb5_checksumsize(kcontext, cksumtype, 00960 &CHECKSUM_LENGTH(&pdu_checksum)); 00961 if (retval) { 00962 DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n", 00963 error_message(retval))); 00964 snmp_set_detail(error_message(retcode)); 00965 retval = SNMPERR_KRB5; 00966 goto error; 00967 } 00968 #else /* OLD_HEIMDAL */ 00969 CHECKSUM_LENGTH(&pdu_checksum) = krb5_checksum_size(kcontext, cksumtype); 00970 #endif /* OLD_HEIMDAL */ 00971 CHECKSUM_TYPE(&pdu_checksum) = cksumtype; 00972 #ifdef OLD_HEIMDAL 00973 } 00974 #endif /* OLD_HEIMDAL */ 00975 00976 #endif /* NETSNMP_USE_KERBEROS_MIT */ 00977 00978 /* 00979 * Note that here, we're just leaving blank space for the checksum; 00980 * we remember where that is, and we'll fill it in later. 00981 */ 00982 00983 *offset += CHECKSUM_LENGTH(&pdu_checksum); 00984 memset(*wholeMsg + *parms->wholeMsgLen - *offset, 0, CHECKSUM_LENGTH(&pdu_checksum)); 00985 00986 cksum_pointer = *wholeMsg + *parms->wholeMsgLen - *offset; 00987 00988 rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen, 00989 parms->wholeMsgOffset, 1, 00990 (u_char) (ASN_UNIVERSAL | 00991 ASN_PRIMITIVE | 00992 ASN_OCTET_STR), 00993 CHECKSUM_LENGTH(&pdu_checksum)); 00994 00995 if (rc == 0) { 00996 DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); 00997 retval = SNMPERR_TOO_LONG; 00998 goto error; 00999 } 01000 01001 tmp = cksumtype; 01002 rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen, 01003 parms->wholeMsgOffset, 1, 01004 (u_char) (ASN_UNIVERSAL | 01005 ASN_PRIMITIVE | 01006 ASN_OCTET_STR), 01007 &tmp, sizeof(tmp)); 01008 01009 if (rc == 0) { 01010 DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); 01011 retval = SNMPERR_TOO_LONG; 01012 goto error; 01013 } 01014 01015 rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen, 01016 parms->wholeMsgOffset, 1, 01017 (u_char) (ASN_SEQUENCE | 01018 ASN_CONSTRUCTOR), 01019 *offset - seq_offset); 01020 01021 if (rc == 0) { 01022 DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); 01023 retval = SNMPERR_TOO_LONG; 01024 goto error; 01025 } 01026 01027 rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen, 01028 parms->wholeMsgOffset, 1, 01029 (u_char) (ASN_UNIVERSAL | 01030 ASN_PRIMITIVE | 01031 ASN_OCTET_STR), 01032 *offset - seq_offset); 01033 01034 if (rc == 0) { 01035 DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); 01036 retval = SNMPERR_TOO_LONG; 01037 goto error; 01038 } 01039 01040 DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completed\n")); 01041 01042 /* 01043 * We're done with the KSM security parameters - now we do the global 01044 * header and wrap up the whole PDU. 01045 */ 01046 01047 if (*parms->wholeMsgLen < parms->globalDataLen) { 01048 DEBUGMSGTL(("ksm", "Building global data failed.\n")); 01049 retval = SNMPERR_TOO_LONG; 01050 goto error; 01051 } 01052 01053 *offset += parms->globalDataLen; 01054 memcpy(*wholeMsg + *parms->wholeMsgLen - *offset, 01055 parms->globalData, parms->globalDataLen); 01056 01057 rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen, 01058 offset, 1, 01059 (u_char) (ASN_SEQUENCE | 01060 ASN_CONSTRUCTOR), 01061 *offset); 01062 01063 if (rc == 0) { 01064 DEBUGMSGTL(("ksm", "Building master packet sequence.\n")); 01065 retval = SNMPERR_TOO_LONG; 01066 goto error; 01067 } 01068 01069 DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.\n")); 01070 01071 /* 01072 * Now we need to checksum the entire PDU (since it's built). 01073 */ 01074 01075 #ifndef OLD_HEIMDAL /* since heimdal allocs the mem for us */ 01076 CHECKSUM_CONTENTS(&pdu_checksum) = malloc(CHECKSUM_LENGTH(&pdu_checksum)); 01077 01078 if (!CHECKSUM_CONTENTS(&pdu_checksum)) { 01079 DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum\n", 01080 CHECKSUM_LENGTH(&pdu_checksum))); 01081 retval = SNMPERR_MALLOC; 01082 goto error; 01083 } 01084 #endif /* ! OLD_HEIMDAL */ 01085 #ifdef NETSNMP_USE_KERBEROS_MIT 01086 01087 input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset); 01088 input.length = *offset; 01089 retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey, 01090 KSM_KEY_USAGE_CHECKSUM, &input, 01091 &pdu_checksum); 01092 01093 #elif defined(OLD_HEIMDAL) /* NETSNMP_USE_KERBEROS_MIT */ 01094 01095 retcode = krb5_create_checksum(kcontext, heim_crypto, 01096 KSM_KEY_USAGE_CHECKSUM, cksumtype, 01097 *wholeMsg + *parms->wholeMsgLen 01098 - *offset, *offset, &pdu_checksum); 01099 #else /* NETSNMP_USE_KERBEROS_MIT */ 01100 01101 retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg + 01102 *parms->wholeMsgLen - *offset, 01103 *offset, 01104 (krb5_pointer) subkey->contents, 01105 subkey->length, &pdu_checksum); 01106 01107 #endif /* NETSNMP_USE_KERBEROS_MIT */ 01108 01109 if (retcode) { 01110 DEBUGMSGTL(("ksm", "Calculate checksum failed: %s\n", 01111 error_message(retcode))); 01112 retval = SNMPERR_KRB5; 01113 snmp_set_detail(error_message(retcode)); 01114 goto error; 01115 } 01116 01117 DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.\n")); 01118 01119 memcpy(cksum_pointer, CHECKSUM_CONTENTS(&pdu_checksum), CHECKSUM_LENGTH(&pdu_checksum)); 01120 01121 DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %d\n", 01122 CHECKSUM_LENGTH(&pdu_checksum), cksum_pointer - (*wholeMsg + 1))); 01123 01124 DEBUGMSGTL(("ksm", "KSM: Checksum:")); 01125 01126 for (i = 0; i < CHECKSUM_LENGTH(&pdu_checksum); i++) 01127 DEBUGMSG(("ksm", " %02x", 01128 (unsigned int) CHECKSUM_CONTENTS(&pdu_checksum)[i])); 01129 01130 DEBUGMSG(("ksm", "\n")); 01131 01132 /* 01133 * If we're _not_ called as part of a response (null ksm_state), 01134 * then save the auth_context for later using our cache routines. 01135 */ 01136 01137 if (!ksm_state) { 01138 if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context, 01139 (u_char *) parms->secName, 01140 parms->secNameLen)) != 01141 SNMPERR_SUCCESS) 01142 goto error; 01143 auth_context = NULL; 01144 } 01145 01146 DEBUGMSGTL(("ksm", "KSM processing complete!\n")); 01147 01148 error: 01149 01150 if (CHECKSUM_CONTENTS(&pdu_checksum)) 01151 #ifdef NETSNMP_USE_KERBEROS_MIT 01152 krb5_free_checksum_contents(kcontext, &pdu_checksum); 01153 #else /* NETSNMP_USE_KERBEROS_MIT */ 01154 free(CHECKSUM_CONTENTS(&pdu_checksum)); 01155 #endif /* NETSNMP_USE_KERBEROS_MIT */ 01156 01157 if (ivector.data) 01158 free(ivector.data); 01159 01160 if (subkey) 01161 krb5_free_keyblock(kcontext, subkey); 01162 01163 #ifdef OLD_HEIMDAL /* OLD_HEIMDAL */ 01164 if (heim_crypto) 01165 krb5_crypto_destroy(kcontext, heim_crypto); 01166 #endif /* OLD_HEIMDAL */ 01167 01168 if (encrypted_data) 01169 free(encrypted_data); 01170 01171 if (cc) 01172 krb5_cc_close(kcontext, cc); 01173 01174 if (auth_context && !ksm_state) 01175 krb5_auth_con_free(kcontext, auth_context); 01176 01177 return retval; 01178 } 01179 01180 /**************************************************************************** 01181 * 01182 * ksm_process_in_msg 01183 * 01184 * Parameters: 01185 * (See list below...) 01186 * 01187 * Returns: 01188 * KSM_ERR_NO_ERROR On success. 01189 * SNMPERR_KRB5 01190 * KSM_ERR_GENERIC_ERROR 01191 * KSM_ERR_UNSUPPORTED_SECURITY_LEVEL 01192 * 01193 * 01194 * Processes an incoming message. 01195 * 01196 ****************************************************************************/ 01197 01198 int 01199 ksm_process_in_msg(struct snmp_secmod_incoming_params *parms) 01200 { 01201 long temp; 01202 krb5_cksumtype cksumtype; 01203 krb5_auth_context auth_context = NULL; 01204 krb5_error_code retcode; 01205 krb5_checksum checksum; 01206 krb5_data ap_req, ivector; 01207 krb5_flags flags; 01208 krb5_keyblock *subkey = NULL; 01209 #ifdef NETSNMP_USE_KERBEROS_MIT 01210 krb5_data input, output; 01211 krb5_boolean valid; 01212 krb5_enc_data in_crypt; 01213 #elif defined OLD_HEIMDAL /* NETSNMP_USE_KERBEROS_MIT */ 01214 krb5_data output; 01215 krb5_crypto heim_crypto = NULL; 01216 #else /* NETSNMP_USE_KERBEROS_MIT */ 01217 krb5_encrypt_block eblock; 01218 #endif /* NETSNMP_USE_KERBEROS_MIT */ 01219 krb5_ticket *ticket = NULL; 01220 int retval = SNMPERR_SUCCESS, response = 0; 01221 size_t length = 01222 parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg); 01223 u_char *current = parms->secParams, type; 01224 size_t cksumlength, blocksize; 01225 long hint; 01226 char *cname; 01227 struct ksm_secStateRef *ksm_state; 01228 struct ksm_cache_entry *entry; 01229 01230 DEBUGMSGTL(("ksm", "Processing has begun\n")); 01231 01232 CHECKSUM_CONTENTS(&checksum) = NULL; 01233 ap_req.data = NULL; 01234 ivector.length = 0; 01235 ivector.data = NULL; 01236 01237 /* 01238 * First, parse the security parameters (because we need the subkey inside 01239 * of the ticket to do anything 01240 */ 01241 01242 if ((current = asn_parse_sequence(current, &length, &type, 01243 (ASN_UNIVERSAL | ASN_PRIMITIVE | 01244 ASN_OCTET_STR), 01245 "ksm first octet")) == NULL) { 01246 DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n")); 01247 01248 retval = SNMPERR_ASN_PARSE_ERR; 01249 goto error; 01250 } 01251 01252 if ((current = asn_parse_sequence(current, &length, &type, 01253 (ASN_SEQUENCE | ASN_CONSTRUCTOR), 01254 "ksm sequence")) == NULL) { 01255 DEBUGMSGTL(("ksm", 01256 "Security parameter sequence parsing failed\n")); 01257 01258 retval = SNMPERR_ASN_PARSE_ERR; 01259 goto error; 01260 } 01261 01262 if ((current = asn_parse_int(current, &length, &type, &temp, 01263 sizeof(temp))) == NULL) { 01264 DEBUGMSGTL(("ksm", "Security parameter checksum type parsing" 01265 "failed\n")); 01266 01267 retval = SNMPERR_ASN_PARSE_ERR; 01268 goto error; 01269 } 01270 01271 cksumtype = temp; 01272 01273 #ifdef NETSNMP_USE_KERBEROS_MIT 01274 if (!krb5_c_valid_cksumtype(cksumtype)) { 01275 DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); 01276 01277 retval = SNMPERR_KRB5; 01278 snmp_set_detail("Invalid checksum type"); 01279 goto error; 01280 } 01281 01282 if (!krb5_c_is_keyed_cksum(cksumtype)) { 01283 DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", 01284 cksumtype)); 01285 snmp_set_detail("Checksum is not a keyed checksum"); 01286 retval = SNMPERR_KRB5; 01287 goto error; 01288 } 01289 01290 if (!krb5_c_is_coll_proof_cksum(cksumtype)) { 01291 DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " 01292 "checksum\n", cksumtype)); 01293 snmp_set_detail("Checksum is not a collision-proof checksum"); 01294 retval = SNMPERR_KRB5; 01295 goto error; 01296 } 01297 #else /* ! NETSNMP_USE_KERBEROS_MIT */ 01298 #ifdef OLD_HEIMDAL 01299 /* kludge */ 01300 if (krb5_checksumsize(kcontext, cksumtype, &cksumlength)) { 01301 #else /* OLD_HEIMDAL */ 01302 if (!valid_cksumtype(cksumtype)) { 01303 #endif /* OLD_HEIMDAL */ 01304 DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); 01305 01306 retval = SNMPERR_KRB5; 01307 snmp_set_detail("Invalid checksum type"); 01308 goto error; 01309 } 01310 01311 #ifdef OLD_HEIMDAL 01312 if (!krb5_checksum_is_keyed(kcontext, cksumtype)) { 01313 #else /* OLD_HEIMDAL */ 01314 if (!is_keyed_cksum(cksumtype)) { 01315 #endif /* OLD_HEIMDAL */ 01316 DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", 01317 cksumtype)); 01318 snmp_set_detail("Checksum is not a keyed checksum"); 01319 retval = SNMPERR_KRB5; 01320 goto error; 01321 } 01322 01323 #ifdef OLD_HEIMDAL 01324 if (!krb5_checksum_is_collision_proof(kcontext, cksumtype)) { 01325 #else /* OLD_HEIMDAL */ 01326 if (!is_coll_proof_cksum(cksumtype)) { 01327 #endif /* OLD_HEIMDAL */ 01328 DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " 01329 "checksum\n", cksumtype)); 01330 snmp_set_detail("Checksum is not a collision-proof checksum"); 01331 retval = SNMPERR_KRB5; 01332 goto error; 01333 } 01334 #endif /* NETSNMP_USE_KERBEROS_MIT */ 01335 01336 CHECKSUM_TYPE(&checksum) = cksumtype; 01337 01338 cksumlength = length; 01339 01340 if ((current = asn_parse_sequence(current, &cksumlength, &type, 01341 (ASN_UNIVERSAL | ASN_PRIMITIVE | 01342 ASN_OCTET_STR), "ksm checksum")) == 01343 NULL) { 01344 DEBUGMSGTL(("ksm", 01345 "Security parameter checksum parsing failed\n")); 01346 01347 retval = SNMPERR_ASN_PARSE_ERR; 01348 goto error; 01349 } 01350 01351 CHECKSUM_CONTENTS(&checksum) = malloc(cksumlength); 01352 if (!CHECKSUM_CONTENTS(&checksum)) { 01353 DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n", 01354 cksumlength)); 01355 retval = SNMPERR_MALLOC; 01356 goto error; 01357 } 01358 01359 memcpy(CHECKSUM_CONTENTS(&checksum), current, cksumlength); 01360 01361 CHECKSUM_LENGTH(&checksum) = cksumlength; 01362 CHECKSUM_TYPE(&checksum) = cksumtype; 01363 01364 /* 01365 * Zero out the checksum so the validation works correctly 01366 */ 01367 01368 memset(current, 0, cksumlength); 01369 01370 current += cksumlength; 01371 length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); 01372 01373 if ((current = asn_parse_sequence(current, &length, &type, 01374 (ASN_UNIVERSAL | ASN_PRIMITIVE | 01375 ASN_OCTET_STR), "ksm ap_req")) == 01376 NULL) { 01377 DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing " 01378 "failed\n")); 01379 01380 retval = SNMPERR_ASN_PARSE_ERR; 01381 goto error; 01382 } 01383 01384 ap_req.length = length; 01385 ap_req.data = malloc(length); 01386 if (!ap_req.data) { 01387 DEBUGMSGTL(("ksm", 01388 "KSM unable to malloc %d bytes for AP_REQ/REP.\n", 01389 length)); 01390 retval = SNMPERR_MALLOC; 01391 goto error; 01392 } 01393 01394 memcpy(ap_req.data, current, length); 01395 01396 current += length; 01397 length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); 01398 01399 if ((current = asn_parse_int(current, &length, &type, &hint, 01400 sizeof(hint))) == NULL) { 01401 DEBUGMSGTL(("ksm", 01402 "KSM security parameter hint parsing failed\n")); 01403 01404 retval = SNMPERR_ASN_PARSE_ERR; 01405 goto error; 01406 } 01407 01408 /* 01409 * Okay! We've got it all! Now try decoding the damn ticket. 01410 * 01411 * But of course there's a WRINKLE! We need to figure out if we're 01412 * processing a AP_REQ or an AP_REP. How do we do that? We're going 01413 * to cheat, and look at the first couple of bytes (which is what 01414 * the Kerberos library routines do anyway). 01415 * 01416 * If there are ever new Kerberos message formats, we'll need to fix 01417 * this here. 01418 * 01419 * If it's a _response_, then we need to get the auth_context 01420 * from our cache. 01421 */ 01422 01423 if (ap_req.length 01424 #ifndef NETSNMP_USE_KERBEROS_HEIMDAL 01425 && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) { 01426 #else /* NETSNMP_USE_KERBEROS_HEIMDAL */ 01427 && (((char *)ap_req.data)[0] == 0x6e || ((char *)ap_req.data)[0] == 0x4e)) { 01428 #endif 01429 01430 /* 01431 * We need to initalize the authorization context, and set the 01432 * replay cache in it (and initialize the replay cache if we 01433 * haven't already 01434 */ 01435 01436 retcode = krb5_auth_con_init(kcontext, &auth_context); 01437 01438 if (retcode) { 01439 DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n", 01440 error_message(retcode))); 01441 retval = SNMPERR_KRB5; 01442 snmp_set_detail(error_message(retcode)); 01443 goto error; 01444 } 01445 01446 if (!rcache) { 01447 krb5_data server; 01448 server.data = "host"; 01449 server.length = strlen(server.data); 01450 01451 retcode = krb5_get_server_rcache(kcontext, &server, &rcache); 01452 01453 if (retcode) { 01454 DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n", 01455 error_message(retcode))); 01456 retval = SNMPERR_KRB5; 01457 snmp_set_detail(error_message(retcode)); 01458 goto error; 01459 } 01460 } 01461 01462 retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache); 01463 01464 if (retcode) { 01465 DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n", 01466 error_message(retcode))); 01467 retval = SNMPERR_KRB5; 01468 snmp_set_detail(error_message(retcode)); 01469 goto error; 01470 } 01471 01472 retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL, 01473 keytab, &flags, &ticket); 01474 01475 krb5_auth_con_setrcache(kcontext, auth_context, NULL); 01476 01477 if (retcode) { 01478 DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n", 01479 error_message(retcode))); 01480 retval = SNMPERR_KRB5; 01481 snmp_set_detail(error_message(retcode)); 01482 goto error; 01483 } 01484 01485 retcode = 01486 krb5_unparse_name(kcontext, TICKET_CLIENT(ticket), &cname); 01487 01488 if (retcode == 0) { 01489 DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n", 01490 cname)); 01491 free(cname); 01492 } 01493 01494 /* 01495 * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set 01496 */ 01497 01498 if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) { 01499 DEBUGMSGTL(("ksm", 01500 "KSM MUTUAL_REQUIRED not set in request!\n")); 01501 retval = SNMPERR_KRB5; 01502 snmp_set_detail("MUTUAL_REQUIRED not set in message"); 01503 goto error; 01504 } 01505 01506 retcode = 01507 krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); 01508 01509 if (retcode) { 01510 DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n", 01511 error_message(retcode))); 01512 retval = SNMPERR_KRB5; 01513 snmp_set_detail(error_message(retcode)); 01514 goto error; 01515 } 01516 01517 #ifndef NETSNMP_USE_KERBEROS_HEIMDAL 01518 } else if (ap_req.length && (ap_req.data[0] == 0x6f || 01519 ap_req.data[0] == 0x4f)) { 01520 #else /* NETSNMP_USE_KERBEROS_HEIMDAL */ 01521 } else if (ap_req.length && (((char *)ap_req.data)[0] == 0x6f || 01522 ((char *)ap_req.data)[0] == 0x4f)) { 01523 #endif /* NETSNMP_USE_KERBEROS_HEIMDAL */ 01524 /* 01525 * Looks like a response; let's see if we've got that auth_context 01526 * in our cache. 01527 */ 01528 01529 krb5_ap_rep_enc_part *repl = NULL; 01530 01531 response = 1; 01532 01533 entry = ksm_get_cache(parms->pdu->msgid); 01534 01535 if (!entry) { 01536 DEBUGMSGTL(("ksm", 01537 "KSM: Unable to find auth_context for PDU with " 01538 "message ID of %ld\n", parms->pdu->msgid)); 01539 retval = SNMPERR_KRB5; 01540 goto error; 01541 } 01542 01543 auth_context = entry->auth_context; 01544 01545 /* 01546 * In that case, let's call the rd_rep function 01547 */ 01548 01549 retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl); 01550 01551 if (repl) 01552 krb5_free_ap_rep_enc_part(kcontext, repl); 01553 01554 if (retcode) { 01555 DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n", 01556 error_message(retcode))); 01557 retval = SNMPERR_KRB5; 01558 goto error; 01559 } 01560 01561 DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n")); 01562 01563 retcode = 01564 krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); 01565 01566 if (retcode) { 01567 DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n", 01568 error_message(retcode))); 01569 retval = SNMPERR_KRB5; 01570 snmp_set_detail("Unable to retrieve local subkey"); 01571 goto error; 01572 } 01573 01574 } else { 01575 #ifndef NETSNMP_USE_KERBEROS_HEIMDAL 01576 DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n", 01577 ap_req.data[0])); 01578 #else /* NETSNMP_USE_KERBEROS_HEIMDAL */ 01579 DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n", 01580 ((char *)ap_req.data)[0])); 01581 #endif 01582 retval = SNMPERR_KRB5; 01583 snmp_set_detail("Unknown Kerberos message type"); 01584 goto error; 01585 } 01586 01587 #ifdef NETSNMP_USE_KERBEROS_MIT 01588 input.data = (char *) parms->wholeMsg; 01589 input.length = parms->wholeMsgLen; 01590 01591 retcode = 01592 krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM, 01593 &input, &checksum, &valid); 01594 #elif defined(OLD_HEIMDAL) /* NETSNMP_USE_KERBEROS_MIT */ 01595 retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto); 01596 if (retcode) { 01597 DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %s\n", 01598 error_message(retcode))); 01599 snmp_set_detail(error_message(retcode)); 01600 retval = SNMPERR_KRB5; 01601 goto error; 01602 } 01603 retcode = krb5_verify_checksum(kcontext, heim_crypto, 01604 KSM_KEY_USAGE_CHECKSUM, parms->wholeMsg, 01605 parms->wholeMsgLen, &checksum); 01606 #else /* NETSNMP_USE_KERBEROS_MIT */ 01607 retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum, 01608 parms->wholeMsg, parms->wholeMsgLen, 01609 (krb5_pointer) subkey->contents, 01610 subkey->length); 01611 #endif /* NETSNMP_USE_KERBEROS_MIT */ 01612 01613 if (retcode) { 01614 DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n", 01615 error_message(retcode))); 01616 retval = SNMPERR_KRB5; 01617 snmp_set_detail(error_message(retcode)); 01618 goto error; 01619 } 01620 01621 /* 01622 * Don't ask me why they didn't simply return an error, but we have 01623 * to check to see if "valid" is false. 01624 */ 01625 01626 #ifdef NETSNMP_USE_KERBEROS_MIT 01627 if (!valid) { 01628 DEBUGMSGTL(("ksm", "Computed checksum did not match supplied " 01629 "checksum!\n")); 01630 retval = SNMPERR_KRB5; 01631 snmp_set_detail 01632 ("Computed checksum did not match supplied checksum"); 01633 goto error; 01634 } 01635 #endif /* NETSNMP_USE_KERBEROS_MIT */ 01636 01637 /* 01638 * Handle an encrypted PDU. Note that it's an OCTET_STRING of the 01639 * output of whatever Kerberos cryptosystem you're using (defined by 01640 * the encryption type). Note that this is NOT the EncryptedData 01641 * sequence - it's what goes in the "cipher" field of EncryptedData. 01642 */ 01643 01644 if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { 01645 01646 if ((current = asn_parse_sequence(current, &length, &type, 01647 (ASN_UNIVERSAL | ASN_PRIMITIVE | 01648 ASN_OCTET_STR), "ksm pdu")) == 01649 NULL) { 01650 DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n")); 01651 retval = SNMPERR_ASN_PARSE_ERR; 01652 goto error; 01653 } 01654 01655 /* 01656 * The PDU is now pointed at by "current", and the length is in 01657 * "length". 01658 */ 01659 01660 DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n")); 01661 01662 /* 01663 * We need to set up a blank initialization vector for the decryption. 01664 * Use a block of all zero's (which is dependent on the block size 01665 * of the encryption method). 01666 */ 01667 01668 #ifdef NETSNMP_USE_KERBEROS_MIT 01669 01670 retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize); 01671 01672 if (retcode) { 01673 DEBUGMSGTL(("ksm", 01674 "Unable to determine crypto block size: %s\n", 01675 error_message(retcode))); 01676 snmp_set_detail(error_message(retcode)); 01677 retval = SNMPERR_KRB5; 01678 goto error; 01679 } 01680 #elif defined(OLD_HEIMDAL) /* NETSNMP_USE_KERBEROS_MIT */ 01681 #else /* NETSNMP_USE_KERBEROS_MIT */ 01682 01683 blocksize = 01684 krb5_enctype_array[subkey->enctype]->system->block_length; 01685 01686 #endif /* NETSNMP_USE_KERBEROS_MIT */ 01687 01688 #ifndef OLD_HEIMDAL 01689 ivector.data = malloc(blocksize); 01690 01691 if (!ivector.data) { 01692 DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n", 01693 blocksize)); 01694 retval = SNMPERR_MALLOC; 01695 goto error; 01696 } 01697 01698 ivector.length = blocksize; 01699 memset(ivector.data, 0, blocksize); 01700 01701 #ifndef NETSNMP_USE_KERBEROS_MIT 01702 01703 krb5_use_enctype(kcontext, &eblock, subkey->enctype); 01704 01705 retcode = krb5_process_key(kcontext, &eblock, subkey); 01706 01707 if (retcode) { 01708 DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n", 01709 error_message(retcode))); 01710 snmp_set_detail(error_message(retcode)); 01711 retval = SNMPERR_KRB5; 01712 goto error; 01713 } 01714 #endif /* !NETSNMP_USE_KERBEROS_MIT */ 01715 01716 #endif /* ! OLD_HEIMDAL */ 01717 01718 if (length > *parms->scopedPduLen) { 01719 DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to " 01720 "decrypt but only %d bytes available\n", length, 01721 *parms->scopedPduLen)); 01722 retval = SNMPERR_TOO_LONG; 01723 #ifndef NETSNMP_USE_KERBEROS_MIT 01724 #ifndef OLD_HEIMDAL 01725 krb5_finish_key(kcontext, &eblock); 01726 #endif /* ! OLD_HEIMDAL */ 01727 #endif /* ! NETSNMP_USE_KERBEROS_MIT */ 01728 goto error; 01729 } 01730 #ifdef NETSNMP_USE_KERBEROS_MIT 01731 in_crypt.ciphertext.data = (char *) current; 01732 in_crypt.ciphertext.length = length; 01733 in_crypt.enctype = subkey->enctype; 01734 output.data = (char *) *parms->scopedPdu; 01735 output.length = *parms->scopedPduLen; 01736 01737 retcode = 01738 krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, 01739 &ivector, &in_crypt, &output); 01740 #elif defined (OLD_HEIMDAL) /* NETSNMP_USE_KERBEROS_MIT */ 01741 retcode = krb5_decrypt(kcontext, heim_crypto, KSM_KEY_USAGE_ENCRYPTION, 01742 current, length, &output); 01743 if (retcode == 0) { 01744 *parms->scopedPdu = (char *) output.data; 01745 *parms->scopedPduLen = output.length; 01746 krb5_data_zero(&output); 01747 } 01748 #else /* NETSNMP_USE_KERBEROS_MIT */ 01749 01750 retcode = krb5_decrypt(kcontext, (krb5_pointer) current, 01751 *parms->scopedPdu, length, &eblock, 01752 ivector.data); 01753 01754 krb5_finish_key(kcontext, &eblock); 01755 01756 #endif /* NETSNMP_USE_KERBEROS_MIT */ 01757 01758 if (retcode) { 01759 DEBUGMSGTL(("ksm", "Decryption failed: %s\n", 01760 error_message(retcode))); 01761 snmp_set_detail(error_message(retcode)); 01762 retval = SNMPERR_KRB5; 01763 goto error; 01764 } 01765 01766 *parms->scopedPduLen = length; 01767 01768 } else { 01769 /* 01770 * Clear PDU 01771 */ 01772 01773 *parms->scopedPdu = current; 01774 *parms->scopedPduLen = 01775 parms->wholeMsgLen - (current - parms->wholeMsg); 01776 } 01777 01778 /* 01779 * A HUGE GROSS HACK 01780 */ 01781 01782 *parms->maxSizeResponse = parms->maxMsgSize - 200; 01783 01784 DEBUGMSGTL(("ksm", "KSM processing complete\n")); 01785 01786 /* 01787 * Set the secName to the right value (a hack for now). But that's 01788 * only used for when we're processing a request, not a response. 01789 */ 01790 01791 if (!response) { 01792 01793 retcode = krb5_unparse_name(kcontext, TICKET_CLIENT(ticket), 01794 &cname); 01795 01796 if (retcode) { 01797 DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n", 01798 error_message(retcode))); 01799 snmp_set_detail(error_message(retcode)); 01800 retval = SNMPERR_KRB5; 01801 goto error; 01802 } 01803 01804 if (strlen(cname) > *parms->secNameLen + 1) { 01805 DEBUGMSGTL(("ksm", 01806 "KSM: Principal length (%d) is too long (%d)\n", 01807 strlen(cname), parms->secNameLen)); 01808 retval = SNMPERR_TOO_LONG; 01809 free(cname); 01810 goto error; 01811 } 01812 01813 strcpy(parms->secName, cname); 01814 *parms->secNameLen = strlen(cname); 01815 01816 free(cname); 01817 01818 /* 01819 * Also, if we're not a response, keep around our auth_context so we 01820 * can encode the reply message correctly 01821 */ 01822 01823 ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef); 01824 01825 if (!ksm_state) { 01826 DEBUGMSGTL(("ksm", "KSM unable to malloc memory for " 01827 "ksm_secStateRef\n")); 01828 retval = SNMPERR_MALLOC; 01829 goto error; 01830 } 01831 01832 ksm_state->auth_context = auth_context; 01833 auth_context = NULL; 01834 ksm_state->cksumtype = cksumtype; 01835 01836 *parms->secStateRef = ksm_state; 01837 } else { 01838 01839 /* 01840 * We _still_ have to set the secName in process_in_msg(). Do 01841 * that now with what we were passed in before (we cached it, 01842 * remember?) 01843 */ 01844 01845 memcpy(parms->secName, entry->secName, entry->secNameLen); 01846 *parms->secNameLen = entry->secNameLen; 01847 } 01848 01849 /* 01850 * Just in case 01851 */ 01852 01853 parms->secEngineID = (u_char *) ""; 01854 *parms->secEngineIDLen = 0; 01855 01856 auth_context = NULL; /* So we don't try to free it on success */ 01857 01858 error: 01859 if (retval == SNMPERR_ASN_PARSE_ERR && 01860 snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0) 01861 DEBUGMSGTL(("ksm", "Failed to increment statistics.\n")); 01862 01863 if (subkey) 01864 krb5_free_keyblock(kcontext, subkey); 01865 01866 #ifdef OLD_HEIMDAL /* OLD_HEIMDAL */ 01867 if (heim_crypto) 01868 krb5_crypto_destroy(kcontext, heim_crypto); 01869 #endif /* OLD_HEIMDAL */ 01870 01871 if (CHECKSUM_CONTENTS(&checksum)) 01872 free(CHECKSUM_CONTENTS(&checksum)); 01873 01874 if (ivector.data) 01875 free(ivector.data); 01876 01877 if (ticket) 01878 krb5_free_ticket(kcontext, ticket); 01879 01880 if (!response && auth_context) 01881 krb5_auth_con_free(kcontext, auth_context); 01882 01883 if (ap_req.data) 01884 free(ap_req.data); 01885 01886 return retval; 01887 }
Last modified: Wednesday, 01-Aug-2018 04:41:28 UTC
For questions regarding web content and site functionality, please write to the net-snmp-users mail list.