int localsm_process_in_msg(struct snmp_secmod_incoming_params *parms) { u_char type_value; size_t octet_string_length; u_char *data_ptr; /* we don't have one, so set it to 0 */ parms->secEngineID = strdup(""); *parms->secEngineIDLen = 0; /* if this did not come through a tunneled connection, this security model is in appropriate (and would be a HUGE security hole to assume otherwise) */ DEBUGMSGTL(("localsm","checking how we got here\n")); if (!(parms->pdu->flags & UCD_MSG_FLAG_TUNNELED)) { DEBUGMSGTL(("localsm"," not tunneled\n")); return SNMPERR_USM_AUTHENTICATIONFAILURE; } else { DEBUGMSGTL(("localsm"," tunneled\n")); } /* * Eat the first octet header. */ if ((data_ptr = asn_parse_sequence(parms->secParams, &octet_string_length, &type_value, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "usm first octet")) == NULL) { /* * RETURN parse error */ return -1; } strncpy(parms->secName, strdup(getenv("USER")), *parms->secNameLen); *parms->secNameLen = strlen(parms->secName); DEBUGMSGTL(("localsm", "user: %s/%d\n", parms->secName, parms->secNameLen)); *parms->scopedPdu = data_ptr; *parms->scopedPduLen = parms->wholeMsgLen - (data_ptr - parms->wholeMsg); *parms->maxSizeResponse = parms->maxMsgSize; /* XXX */ parms->secEngineID = strdup(""); *parms->secEngineIDLen = 0; parms->secLevel = SNMP_SEC_LEVEL_NOAUTH; /* * maybe set this based on the transport in the future: * */ if (octet_string_length != 0) return -1; return SNMPERR_SUCCESS; }
/*******************************************************************-o-****** * snmp_comstr_parse * * Parameters: * *data (I) Message. * *length (I/O) Bytes left in message. * *psid (O) Community string. * *slen (O) Length of community string. * *version (O) Message version. * * Returns: * Pointer to the remainder of data. * * * Parse the header of a community string-based message such as that found * in SNMPv1 and SNMPv2c. */ u_char * snmp_comstr_parse(u_char * data, size_t * length, u_char * psid, size_t * slen, long *version) { u_char type; long ver; size_t origlen = *slen; /* * Message is an ASN.1 SEQUENCE. */ data = asn_parse_sequence(data, length, &type, (ASN_SEQUENCE | ASN_CONSTRUCTOR), "auth message"); if (data == NULL) { return NULL; } /* * First field is the version. */ DEBUGDUMPHEADER("recv", "SNMP version"); data = asn_parse_int(data, length, &type, &ver, sizeof(ver)); DEBUGINDENTLESS(); *version = ver; if (data == NULL) { ERROR_MSG("bad parse of version"); return NULL; } /* * second field is the community string for SNMPv1 & SNMPv2c */ DEBUGDUMPHEADER("recv", "community string"); data = asn_parse_string(data, length, &type, psid, slen); DEBUGINDENTLESS(); if (data == NULL) { ERROR_MSG("bad parse of community"); return NULL; } psid[SNMP_MIN(*slen, origlen - 1)] = '\0'; return (u_char *) data; } /* end snmp_comstr_parse() */
u_char * snmp_parse_var_op(u_char *data, oid *var_name, size_t *var_name_len, u_char *var_val_type, size_t *var_val_len, u_char **var_val, size_t *listlength) { u_char var_op_type; size_t var_op_len = *listlength; u_char *var_op_start = data; data = asn_parse_sequence(data, &var_op_len, &var_op_type, (ASN_SEQUENCE | ASN_CONSTRUCTOR), "var_op"); if (data == NULL) { /* msg detail is set */ return NULL; } data = asn_parse_objid(data, &var_op_len, &var_op_type, var_name, var_name_len); if (data == NULL) { ERROR_MSG("No OID for variable"); return NULL; } if (var_op_type != (u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID)) return NULL; *var_val = data; /* save pointer to this object */ /* find out what type of object this is */ data = asn_parse_header(data, &var_op_len, var_val_type); if (data == NULL) { ERROR_MSG("No header for value"); return NULL; } /* XXX no check for type! */ *var_val_len = var_op_len; data += var_op_len; *listlength -= (int)(data - var_op_start); return data; }
int tsm_process_in_msg(struct snmp_secmod_incoming_params *parms) { u_char type_value; size_t remaining; u_char *data_ptr; netsnmp_tmStateReference *tmStateRef; netsnmp_tsmSecurityReference *tsmSecRef; u_char ourEngineID[SNMP_MAX_ENG_SIZE]; static size_t ourEngineID_len = sizeof(ourEngineID); /* Section 5.2, step 1 */ ourEngineID_len = snmpv3_get_engineID((u_char*)ourEngineID, ourEngineID_len); if (ourEngineID_len == 0 || ourEngineID_len > *parms->secEngineIDLen) return SNMPERR_GENERR; memcpy(parms->secEngineID, ourEngineID, *parms->secEngineIDLen); /* Section 5.2, step 2 */ if (!parms->pdu->transport_data || sizeof(netsnmp_tmStateReference) != parms->pdu->transport_data_length) { /* if we're not coming in over a proper transport; bail! */ DEBUGMSGTL(("tsm","improper transport data\n")); return -1; } tmStateRef = (netsnmp_tmStateReference *) parms->pdu->transport_data; parms->pdu->transport_data = NULL; if (tmStateRef == NULL || /* not needed: tmStateRef->transportDomain == NULL || */ /* not needed: tmStateRef->transportAddress == NULL || */ tmStateRef->securityName[0] == '\0' /* || seclevel is not valid */ ) { /* XXX: snmpTsmInvalidCaches++ */ return SNMPERR_GENERR; } /* Section 5.2, step 3 */ if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_LIB_TSM_USE_PREFIX)) { /* Section 5.2, step 3a */ const char *prefix; prefix = "ssh:"; /* XXX */ if (prefix == NULL) { /* XXX: snmpTsmUnknownPrefixes++ */ return SNMPERR_GENERR; } if (strlen(prefix) < 1 || strlen(prefix) > 4) { /* XXX: snmpTsmInvalidPrefixes++ */ return SNMPERR_GENERR; } snprintf(parms->secName, *parms->secNameLen, "%s:%s", prefix, tmStateRef->securityName); } else { strncpy(parms->secName, tmStateRef->securityName, *parms->secNameLen); } *parms->secNameLen = strlen(parms->secName); DEBUGMSGTL(("tsm", "user: %s/%d\n", parms->secName, *parms->secNameLen)); /* Section 5.2 Step 4 */ if (parms->secLevel > tmStateRef->transportSecurityLevel) { /* XXX: snmpTsmInadequateSecurityLevels++ */ return SNMPERR_UNSUPPORTED_SEC_LEVEL; } /* Section 5.2 Step 5 */ if (NULL == *parms->secStateRef) { tsmSecRef = SNMP_MALLOC_TYPEDEF(netsnmp_tsmSecurityReference); } else { tsmSecRef = *parms->secStateRef; } if (!tsmSecRef) return SNMPERR_GENERR; *parms->secStateRef = tsmSecRef; tsmSecRef->tmStateRef = tmStateRef; /* If this did not come through a tunneled connection, this security model is inappropriate (and would be a HUGE security hole to assume otherwise). This is functionally a double check since the pdu wouldn't have transport data otherwise. But this is safer though is functionally an extra step beyond the TSM RFC. */ DEBUGMSGTL(("tsm","checking how we got here\n")); if (!(parms->pdu->flags & UCD_MSG_FLAG_TUNNELED)) { DEBUGMSGTL(("tsm"," pdu not tunneled\n")); if (!(parms->sess->flags & NETSNMP_TRANSPORT_FLAG_TUNNELED)) { DEBUGMSGTL(("tsm"," session not tunneled\n")); return SNMPERR_USM_AUTHENTICATIONFAILURE; } DEBUGMSGTL(("tsm"," but session is tunneled\n")); } else { DEBUGMSGTL(("tsm"," tunneled\n")); } /* Section 5.2, Step 6 */ /* * Eat the first octet header. */ remaining = parms->wholeMsgLen - (parms->secParams - parms->wholeMsg); if ((data_ptr = asn_parse_sequence(parms->secParams, &remaining, &type_value, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "usm first octet")) == NULL) { /* * RETURN parse error */ return SNMPERR_GENERR; } *parms->scopedPdu = data_ptr; *parms->scopedPduLen = parms->wholeMsgLen - (data_ptr - parms->wholeMsg); /* Section 5.2, Step 7 */ *parms->maxSizeResponse = parms->maxMsgSize; /* XXX */ tsmSecRef->securityLevel = parms->secLevel; return SNMPERR_SUCCESS; }
int tsm_process_in_msg(struct snmp_secmod_incoming_params *parms) { u_char type_value; size_t remaining; u_char *data_ptr; netsnmp_tmStateReference *tmStateRef; netsnmp_tsmSecurityReference *tsmSecRef; u_char ourEngineID[SNMP_MAX_ENG_SIZE]; static size_t ourEngineID_len = sizeof(ourEngineID); /* Section 5.2, step 1: Set the securityEngineID to the local snmpEngineID. */ ourEngineID_len = snmpv3_get_engineID((u_char*) ourEngineID, ourEngineID_len); netsnmp_assert_or_return(ourEngineID_len != 0 && ourEngineID_len <= *parms->secEngineIDLen, SNMPERR_GENERR); memcpy(parms->secEngineID, ourEngineID, *parms->secEngineIDLen); /* Section 5.2, step 2: If tmStateReference does not refer to a cache containing values for tmTransportDomain, tmTransportAddress, tmSecurityName, and tmTransportSecurityLevel, then the snmpTsmInvalidCaches counter is incremented, an error indication is returned to the calling module, and Security Model processing stops for this message. */ if (!parms->pdu->transport_data || sizeof(netsnmp_tmStateReference) != parms->pdu->transport_data_length) { /* if we're not coming in over a proper transport; bail! */ DEBUGMSGTL(("tsm","improper transport data\n")); return -1; } tmStateRef = (netsnmp_tmStateReference *) parms->pdu->transport_data; parms->pdu->transport_data = NULL; if (tmStateRef == NULL || /* not needed: tmStateRef->transportDomain == NULL || */ /* not needed: tmStateRef->transportAddress == NULL || */ tmStateRef->securityName[0] == '\0' ) { snmp_increment_statistic(STAT_TSM_SNMPTSMINVALIDCACHES); return SNMPERR_GENERR; } /* Section 5.2, step 3: Copy the tmSecurityName to securityName. */ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_TSM_USE_PREFIX)) { /* Section 5.2, step 3: If the snmpTsmConfigurationUsePrefix object is set to true, then use the tmTransportDomain to look up the corresponding prefix. */ const char *prefix = NULL; /* possibilities: |--------------------+-------| | snmpTLSTCPDomain | tls: | | snmpDTLSUDPDomain | dtls: | | snmpSSHDomain | ssh: | |--------------------+-------| */ if (tmStateRef->transportDomain == NULL) { /* XXX: snmpTsmInvalidCaches++ ??? */ return SNMPERR_GENERR; } /* XXX: cache in session! */ #ifdef NETSNMP_TRANSPORT_SSH_DOMAIN if (netsnmp_oid_equals(netsnmp_snmpSSHDomain, netsnmp_snmpSSHDomain_len, tmStateRef->transportDomain, tmStateRef->transportDomainLen) == 0) { prefix = "ssh"; } #endif /* NETSNMP_TRANSPORT_SSH_DOMAIN */ #ifdef NETSNMP_TRANSPORT_DTLSUDP_DOMAIN if (netsnmp_oid_equals(netsnmpDTLSUDPDomain, netsnmpDTLSUDPDomain_len, tmStateRef->transportDomain, tmStateRef->transportDomainLen) == 0) { prefix = "dtls"; } #endif /* NETSNMP_TRANSPORT_DTLSUDP_DOMAIN */ #ifdef NETSNMP_TRANSPORT_TLSTCP_DOMAIN if (netsnmp_oid_equals(netsnmpTLSTCPDomain, netsnmpTLSTCPDomain_len, tmStateRef->transportDomain, tmStateRef->transportDomainLen) == 0) { prefix = "tls"; } #endif /* NETSNMP_TRANSPORT_TLSTCP_DOMAIN */ /* Section 5.2, step 3: If the prefix lookup fails for any reason, then the snmpTsmUnknownPrefixes counter is incremented, an error indication is returned to the calling module, and message processing stops. */ if (prefix == NULL) { snmp_increment_statistic(STAT_TSM_SNMPTSMUNKNOWNPREFIXES); return SNMPERR_GENERR; } /* Section 5.2, step 3: If the lookup succeeds but the prefix length is less than 1 or greater than 4 octets, then the snmpTsmInvalidPrefixes counter is incremented, an error indication is returned to the calling module, and message processing stops. */ #ifdef NOT_USING_HARDCODED_PREFIXES /* the above code actually ensures this will never happen as we don't support a dynamic prefix database where this might happen. */ if (strlen(prefix) < 1 || strlen(prefix) > 4) { /* XXX: snmpTsmInvalidPrefixes++ */ return SNMPERR_GENERR; } #endif /* Section 5.2, step 3: Set the securityName to be the concatenation of the prefix, a ':' character (US-ASCII 0x3a), and the tmSecurityName. */ snprintf(parms->secName, *parms->secNameLen, "%s:%s", prefix, tmStateRef->securityName); } else { /* if the use prefix flag wasn't set, do a straight copy */ strncpy(parms->secName, tmStateRef->securityName, *parms->secNameLen); } /* set the length of the security name */ *parms->secNameLen = strlen(parms->secName); DEBUGMSGTL(("tsm", "user: %s/%d\n", parms->secName, (int)*parms->secNameLen)); /* Section 5.2 Step 4: Compare the value of tmTransportSecurityLevel in the tmStateReference cache to the value of the securityLevel parameter passed in the processIncomingMsg ASI. If securityLevel specifies privacy (Priv) and tmTransportSecurityLevel specifies no privacy (noPriv), or if securityLevel specifies authentication (auth) and tmTransportSecurityLevel specifies no authentication (noAuth) was provided by the Transport Model, then the snmpTsmInadequateSecurityLevels counter is incremented, an error indication (unsupportedSecurityLevel) together with the OID and value of the incremented counter is returned to the calling module, and Transport Security Model processing stops for this message.*/ if (parms->secLevel > tmStateRef->transportSecurityLevel) { snmp_increment_statistic(STAT_TSM_SNMPTSMINADEQUATESECURITYLEVELS); DEBUGMSGTL(("tsm", "inadequate security level: %d\n", parms->secLevel)); /* net-snmp returns error codes not OIDs, which are dealt with later */ return SNMPERR_UNSUPPORTED_SEC_LEVEL; } /* Section 5.2 Step 5 The tmStateReference is cached as cachedSecurityData so that a possible response to this message will use the same security parameters. Then securityStateReference is set for subsequent references to this cached data. */ if (NULL == *parms->secStateRef) { tsmSecRef = SNMP_MALLOC_TYPEDEF(netsnmp_tsmSecurityReference); } else { tsmSecRef = *parms->secStateRef; } netsnmp_assert_or_return(NULL != tsmSecRef, SNMPERR_GENERR); *parms->secStateRef = tsmSecRef; tsmSecRef->tmStateRef = tmStateRef; /* If this did not come through a tunneled connection, this security model is inappropriate (and would be a HUGE security hole to assume otherwise). This is functionally a double check since the pdu wouldn't have transport data otherwise. But this is safer though is functionally an extra step beyond the TSM RFC. */ DEBUGMSGTL(("tsm","checking how we got here\n")); if (!(parms->pdu->flags & UCD_MSG_FLAG_TUNNELED)) { DEBUGMSGTL(("tsm"," pdu not tunneled\n")); if (!(parms->sess->flags & NETSNMP_TRANSPORT_FLAG_TUNNELED)) { DEBUGMSGTL(("tsm"," session not tunneled\n")); return SNMPERR_USM_AUTHENTICATIONFAILURE; } DEBUGMSGTL(("tsm"," but session is tunneled\n")); } else { DEBUGMSGTL(("tsm"," tunneled\n")); } /* Section 5.2, Step 6: The scopedPDU component is extracted from the wholeMsg. */ /* * Eat the first octet header. */ remaining = parms->wholeMsgLen - (parms->secParams - parms->wholeMsg); if ((data_ptr = asn_parse_sequence(parms->secParams, &remaining, &type_value, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "tsm first octet")) == NULL) { /* * RETURN parse error */ return SNMPERR_ASN_PARSE_ERR; } *parms->scopedPdu = data_ptr; *parms->scopedPduLen = parms->wholeMsgLen - (data_ptr - parms->wholeMsg); /* Section 5.2, Step 7: The maxSizeResponseScopedPDU is calculated. This is the maximum size allowed for a scopedPDU for a possible Response message. */ *parms->maxSizeResponse = parms->maxMsgSize; /* XXX */ /* Section 5.2, Step 8: The statusInformation is set to success and a return is made to the calling module passing back the OUT parameters as specified in the processIncomingMsg ASI. */ return SNMPERR_SUCCESS; }
int ksm_process_in_msg(struct snmp_secmod_incoming_params *parms) { long temp; krb5_cksumtype cksumtype; krb5_auth_context auth_context = NULL; krb5_error_code retcode; krb5_checksum checksum; krb5_data ap_req, ivector; krb5_flags flags; krb5_keyblock *subkey = NULL; #ifdef MIT_NEW_CRYPTO krb5_data input, output; krb5_boolean valid; krb5_enc_data in_crypt; #else /* MIT_NEW_CRYPTO */ krb5_encrypt_block eblock; #endif /* MIT_NEW_CRYPTO */ krb5_ticket *ticket = NULL; int retval = SNMPERR_SUCCESS, response = 0; size_t length = parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg); u_char *current = parms->secParams, type; size_t cksumlength, blocksize; long hint; char *cname; struct ksm_secStateRef *ksm_state; struct ksm_cache_entry *entry; DEBUGMSGTL(("ksm", "Processing has begun\n")); checksum.contents = NULL; ap_req.data = NULL; ivector.length = 0; ivector.data = NULL; /* * First, parse the security parameters (because we need the subkey inside * of the ticket to do anything */ if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm first octet")) == NULL) { DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_sequence(current, &length, &type, (ASN_SEQUENCE | ASN_CONSTRUCTOR), "ksm sequence")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter sequence parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_int(current, &length, &type, &temp, sizeof(temp))) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum type parsing" "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } cksumtype = temp; #ifdef MIT_NEW_CRYPTO if (!krb5_c_valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!krb5_c_is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!krb5_c_is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #else /* ! MIT_NEW_CRYPTO */ if (!valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #endif /* MIT_NEW_CRYPTO */ checksum.checksum_type = cksumtype; cksumlength = length; if ((current = asn_parse_sequence(current, &cksumlength, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm checksum")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } checksum.contents = malloc(cksumlength); if (!checksum.contents) { DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n", cksumlength)); retval = SNMPERR_MALLOC; goto error; } memcpy(checksum.contents, current, cksumlength); checksum.length = cksumlength; checksum.checksum_type = cksumtype; /* * Zero out the checksum so the validation works correctly */ memset(current, 0, cksumlength); current += cksumlength; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm ap_req")) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing " "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } ap_req.length = length; ap_req.data = malloc(length); if (!ap_req.data) { DEBUGMSGTL(("ksm", "KSM unable to malloc %d bytes for AP_REQ/REP.\n", length)); retval = SNMPERR_MALLOC; goto error; } memcpy(ap_req.data, current, length); current += length; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_int(current, &length, &type, &hint, sizeof(hint))) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter hint parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * Okay! We've got it all! Now try decoding the damn ticket. * * But of course there's a WRINKLE! We need to figure out if we're * processing a AP_REQ or an AP_REP. How do we do that? We're going * to cheat, and look at the first couple of bytes (which is what * the Kerberos library routines do anyway). * * If there are ever new Kerberos message formats, we'll need to fix * this here. * * If it's a _response_, then we need to get the auth_context * from our cache. */ if (ap_req.length && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) { /* * We need to initalize the authorization context, and set the * replay cache in it (and initialize the replay cache if we * haven't already */ retcode = krb5_auth_con_init(kcontext, &auth_context); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } if (!rcache) { krb5_data server; server.data = "host"; server.length = strlen(server.data); retcode = krb5_get_server_rcache(kcontext, &server, &rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL, keytab, &flags, &ticket); krb5_auth_con_setrcache(kcontext, auth_context, NULL); if (retcode) { DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode == 0) { DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n", cname)); free(cname); } /* * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set */ if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) { DEBUGMSGTL(("ksm", "KSM MUTUAL_REQUIRED not set in request!\n")); retval = SNMPERR_KRB5; snmp_set_detail("MUTUAL_REQUIRED not set in message"); goto error; } retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } else if (ap_req.length && (ap_req.data[0] == 0x6f || ap_req.data[0] == 0x4f)) { /* * Looks like a response; let's see if we've got that auth_context * in our cache. */ krb5_ap_rep_enc_part *repl = NULL; response = 1; entry = ksm_get_cache(parms->pdu->msgid); if (!entry) { DEBUGMSGTL(("ksm", "KSM: Unable to find auth_context for PDU with " "message ID of %ld\n", parms->pdu->msgid)); retval = SNMPERR_KRB5; goto error; } auth_context = entry->auth_context; /* * In that case, let's call the rd_rep function */ retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl); if (repl) krb5_free_ap_rep_enc_part(kcontext, repl); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n")); retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail("Unable to retrieve local subkey"); goto error; } } else { DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n", ap_req.data[0])); retval = SNMPERR_KRB5; snmp_set_detail("Unknown Kerberos message type"); goto error; } #ifdef MIT_NEW_CRYPTO input.data = (char *) parms->wholeMsg; input.length = parms->wholeMsgLen; retcode = krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM, &input, &checksum, &valid); #else /* MIT_NEW_CRYPTO */ retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum, parms->wholeMsg, parms->wholeMsgLen, (krb5_pointer) subkey->contents, subkey->length); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } /* * Don't ask me why they didn't simply return an error, but we have * to check to see if "valid" is false. */ #ifdef MIT_NEW_CRYPTO if (!valid) { DEBUGMSGTL(("ksm", "Computed checksum did not match supplied " "checksum!\n")); retval = SNMPERR_KRB5; snmp_set_detail ("Computed checksum did not match supplied checksum"); goto error; } #endif /* MIT_NEW_CRYPTO */ /* * Handle an encrypted PDU. Note that it's an OCTET_STRING of the * output of whatever Kerberos cryptosystem you're using (defined by * the encryption type). Note that this is NOT the EncryptedData * sequence - it's what goes in the "cipher" field of EncryptedData. */ if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm pdu")) == NULL) { DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * The PDU is now pointed at by "current", and the length is in * "length". */ DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n")); /* * We need to set up a blank initialization vector for the decryption. * Use a block of all zero's (which is dependent on the block size * of the encryption method). */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize); if (retcode) { DEBUGMSGTL(("ksm", "Unable to determine crypto block size: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #else /* MIT_NEW_CRYPTO */ blocksize = krb5_enctype_array[subkey->enctype]->system->block_length; #endif /* MIT_NEW_CRYPTO */ ivector.data = malloc(blocksize); if (!ivector.data) { DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n", blocksize)); retval = SNMPERR_MALLOC; goto error; } ivector.length = blocksize; memset(ivector.data, 0, blocksize); #ifndef MIT_NEW_CRYPTO krb5_use_enctype(kcontext, &eblock, subkey->enctype); retcode = krb5_process_key(kcontext, &eblock, subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #endif /* !MIT_NEW_CRYPTO */ if (length > *parms->scopedPduLen) { DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to " "decrypt but only %d bytes available\n", length, *parms->scopedPduLen)); retval = SNMPERR_TOO_LONG; #ifndef MIT_NEW_CRYPTO krb5_finish_key(kcontext, &eblock); #endif /* ! MIT_NEW_CRYPTO */ goto error; } #ifdef MIT_NEW_CRYPTO in_crypt.ciphertext.data = (char *) current; in_crypt.ciphertext.length = length; in_crypt.enctype = subkey->enctype; output.data = (char *) *parms->scopedPdu; output.length = *parms->scopedPduLen; retcode = krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, &ivector, &in_crypt, &output); #else /* MIT_NEW_CRYPTO */ retcode = krb5_decrypt(kcontext, (krb5_pointer) current, *parms->scopedPdu, length, &eblock, ivector.data); krb5_finish_key(kcontext, &eblock); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "Decryption failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } *parms->scopedPduLen = length; } else { /* * Clear PDU */ *parms->scopedPdu = current; *parms->scopedPduLen = parms->wholeMsgLen - (current - parms->wholeMsg); } /* * A HUGE GROSS HACK */ *parms->maxSizeResponse = parms->maxMsgSize - 200; DEBUGMSGTL(("ksm", "KSM processing complete\n")); /* * Set the secName to the right value (a hack for now). But that's * only used for when we're processing a request, not a response. */ if (!response) { retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode) { DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } if (strlen(cname) > *parms->secNameLen + 1) { DEBUGMSGTL(("ksm", "KSM: Principal length (%d) is too long (%d)\n", strlen(cname), parms->secNameLen)); retval = SNMPERR_TOO_LONG; free(cname); goto error; } strcpy(parms->secName, cname); *parms->secNameLen = strlen(cname); free(cname); /* * Also, if we're not a response, keep around our auth_context so we * can encode the reply message correctly */ ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef); if (!ksm_state) { DEBUGMSGTL(("ksm", "KSM unable to malloc memory for " "ksm_secStateRef\n")); retval = SNMPERR_MALLOC; goto error; } ksm_state->auth_context = auth_context; auth_context = NULL; ksm_state->cksumtype = cksumtype; *parms->secStateRef = ksm_state; } else { /* * We _still_ have to set the secName in process_in_msg(). Do * that now with what we were passed in before (we cached it, * remember?) */ memcpy(parms->secName, entry->secName, entry->secNameLen); *parms->secNameLen = entry->secNameLen; } /* * Just in case */ parms->secEngineID = (u_char *) ""; *parms->secEngineIDLen = 0; auth_context = NULL; /* So we don't try to free it on success */ error: if (retval == SNMPERR_ASN_PARSE_ERR && snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0) DEBUGMSGTL(("ksm", "Failed to increment statistics.\n")); if (subkey) krb5_free_keyblock(kcontext, subkey); if (checksum.contents) free(checksum.contents); if (ivector.data) free(ivector.data); if (ticket) krb5_free_ticket(kcontext, ticket); if (!response && auth_context) krb5_auth_con_free(kcontext, auth_context); if (ap_req.data) free(ap_req.data); return retval; }