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