/* * Copyright (C) 1996-2017 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ /* * SNMP PDU Encoding * * Complies with: * * RFC 1902: Structure of Management Information for SNMPv2 * */ /********************************************************************** * * Copyright 1997 by Carnegie Mellon University * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of CMU not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Author: Ryan Troll * **********************************************************************/ #include "squid.h" #if HAVE_UNISTD_H #include #endif #if HAVE_STDLIB_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_CTYPE_H #include #endif #if HAVE_GNUMALLOC_H #include #elif HAVE_MALLOC_H #include #endif #if HAVE_MEMORY_H #include #endif #if HAVE_STRING_H #include #endif #if HAVE_STRINGS_H #include #endif #if HAVE_BSTRING_H #include #endif #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #if HAVE_SYS_TIME_H #include #endif #if HAVE_NETDB_H #include #endif #include "asn1.h" #include "snmp.h" #include "snmp_api_error.h" #include "snmp_error.h" #include "snmp_msg.h" #include "snmp_pdu.h" #include "snmp_vars.h" #include "util.h" /* #define DEBUG_PDU 1 */ /* #define DEBUG_PDU_DECODE 1 */ /* #define DEBUG_PDU_ENCODE 1 */ #define ASN_PARSE_ERROR(x) { return(x); } /**********************************************************************/ /* Create a PDU. */ struct snmp_pdu * snmp_pdu_create(int command) { struct snmp_pdu *pdu; #if DEBUG_PDU snmplib_debug(8, "PDU: Creating\n"); #endif pdu = (struct snmp_pdu *) xmalloc(sizeof(struct snmp_pdu)); if (pdu == NULL) { snmp_set_api_error(SNMPERR_OS_ERR); return (NULL); } memset((char *) pdu, '\0', sizeof(struct snmp_pdu)); pdu->command = command; pdu->errstat = SNMP_DEFAULT_ERRSTAT; pdu->errindex = SNMP_DEFAULT_ERRINDEX; pdu->address.sin_addr.s_addr = SNMP_DEFAULT_ADDRESS; pdu->enterprise = NULL; pdu->enterprise_length = 0; pdu->variables = NULL; #if DEBUG_PDU snmplib_debug(8, "PDU: Created %x\n", (unsigned int) pdu); #endif return (pdu); } /**********************************************************************/ /* Clone an existing PDU. */ struct snmp_pdu * snmp_pdu_clone(struct snmp_pdu *Src) { struct snmp_pdu *Dest; #if DEBUG_PDU snmplib_debug(8, "PDU %x: Cloning\n", (unsigned int) Src); #endif Dest = (struct snmp_pdu *) xmalloc(sizeof(struct snmp_pdu)); if (Dest == NULL) { snmp_set_api_error(SNMPERR_OS_ERR); return (NULL); } memcpy((char *) Dest, (char *) Src, sizeof(struct snmp_pdu)); #if DEBUG_PDU snmplib_debug(8, "PDU %x: Created %x\n", (unsigned int) Src, (unsigned int) Dest); #endif return (Dest); } /**********************************************************************/ /* * If there was an error in the input pdu, creates a clone of the pdu * that includes all the variables except the one marked by the errindex. * The command is set to the input command and the reqid, errstat, and * errindex are set to default values. * If the error status didn't indicate an error, the error index didn't * indicate a variable, the pdu wasn't a get response message, or there * would be no remaining variables, this function will return NULL. * If everything was successful, a pointer to the fixed cloned pdu will * be returned. */ struct snmp_pdu * snmp_pdu_fix(struct snmp_pdu *pdu, int command) { return (snmp_fix_pdu(pdu, command)); } struct snmp_pdu * snmp_fix_pdu(struct snmp_pdu *pdu, int command) { struct variable_list *var, *newvar; struct snmp_pdu *newpdu; int i; int copied = 0; #if DEBUG_PDU snmplib_debug(8, "PDU %x: Fixing. Err index is %d\n", (unsigned int) pdu, (unsigned int) pdu->errindex); #endif if (pdu->command != SNMP_PDU_RESPONSE || pdu->errstat == SNMP_ERR_NOERROR || pdu->errindex <= 0) { snmp_set_api_error(SNMPERR_UNABLE_TO_FIX); return (NULL); } /* clone the pdu */ newpdu = snmp_pdu_clone(pdu); if (newpdu == NULL) return (NULL); newpdu->variables = 0; newpdu->command = command; newpdu->reqid = SNMP_DEFAULT_REQID; newpdu->errstat = SNMP_DEFAULT_ERRSTAT; newpdu->errindex = SNMP_DEFAULT_ERRINDEX; /* Loop through the variables, removing whatever isn't necessary */ var = pdu->variables; i = 1; /* skip first variable if necessary */ if (pdu->errindex == i) { var = var->next_variable; i++; } if (var != NULL) { /* VAR is the first uncopied variable */ /* Clone this variable */ newpdu->variables = snmp_var_clone(var); if (newpdu->variables == NULL) { snmp_pdu_free(newpdu); return (NULL); } copied++; newvar = newpdu->variables; /* VAR has been copied to NEWVAR. */ while (var->next_variable) { /* Skip the item that was bad */ if (++i == pdu->errindex) { var = var->next_variable; continue; } /* Copy this var */ newvar->next_variable = snmp_var_clone(var->next_variable); if (newvar->next_variable == NULL) { snmp_pdu_free(newpdu); return (NULL); } /* Move to the next one */ newvar = newvar->next_variable; var = var->next_variable; copied++; } newvar->next_variable = NULL; } /* If we didn't copy anything, free the new pdu. */ if (i < pdu->errindex || copied == 0) { snmp_free_pdu(newpdu); snmp_set_api_error(SNMPERR_UNABLE_TO_FIX); return (NULL); } #if DEBUG_PDU snmplib_debug(8, "PDU %x: Fixed PDU is %x\n", (unsigned int) pdu, (unsigned int) newpdu); #endif return (newpdu); } /**********************************************************************/ void snmp_pdu_free(struct snmp_pdu *pdu) { snmp_free_pdu(pdu); } /* * Frees the pdu and any xmalloc'd data associated with it. */ void snmp_free_pdu(struct snmp_pdu *pdu) { struct variable_list *vp, *ovp; vp = pdu->variables; while (vp) { ovp = vp; vp = vp->next_variable; snmp_var_free(ovp); } if (pdu->enterprise) xfree((char *) pdu->enterprise); xfree((char *) pdu); } /**********************************************************************/ /* Encode this PDU into DestBuf. * * Returns a pointer to the next byte in the buffer (where the Variable * Bindings belong.) */ /* * RFC 1902: Structure of Management Information for SNMPv2 * * PDU ::= * SEQUENCE { * request-id INTEGER32 * error-status INTEGER * error-index INTEGER * Variable Bindings * } * * BulkPDU ::= * SEQUENCE { * request-id INTEGER32 * non-repeaters INTEGER * max-repetitions INTEGER * Variable Bindings * } */ /* * RFC 1157: A Simple Network Management Protocol (SNMP) * * PDU ::= * SEQUENCE { * request-id INTEGER * error-status INTEGER * error-index INTEGER * Variable Bindings * } * * TrapPDU ::= * SEQUENCE { * enterprise NetworkAddress * generic-trap INTEGER * specific-trap INTEGER * time-stamp TIMETICKS * Variable Bindings * } */ u_char * snmp_pdu_encode(u_char * DestBuf, int *DestBufLen, struct snmp_pdu *PDU) { u_char *bufp; #if DEBUG_PDU_ENCODE snmplib_debug(8, "PDU: Encoding %d\n", PDU->command); #endif /* ASN.1 Header */ switch (PDU->command) { /**********************************************************************/ #if TRP_REQ_MSG case TRP_REQ_MSG: /* SNMPv1 Trap */ /* enterprise */ bufp = asn_build_objid(DestBuf, DestBufLen, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID), (oid *) PDU->enterprise, PDU->enterprise_length); if (bufp == NULL) return (NULL); /* agent-addr */ bufp = asn_build_string(bufp, DestBufLen, (u_char) (SMI_IPADDRESS | ASN_PRIMITIVE), (u_char *) & PDU->agent_addr.sin_addr.s_addr, sizeof(PDU->agent_addr.sin_addr.s_addr)); if (bufp == NULL) return (NULL); /* generic trap */ bufp = asn_build_int(bufp, DestBufLen, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), (int *) &PDU->trap_type, sizeof(PDU->trap_type)); if (bufp == NULL) return (NULL); /* specific trap */ bufp = asn_build_int(bufp, DestBufLen, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), (int *) &PDU->specific_type, sizeof(PDU->specific_type)); if (bufp == NULL) return (NULL); /* timestamp */ bufp = asn_build_unsigned_int(bufp, DestBufLen, (u_char) (SMI_TIMETICKS | ASN_PRIMITIVE), &PDU->time, sizeof(PDU->time)); if (bufp == NULL) return (NULL); break; #endif /**********************************************************************/ case SNMP_PDU_GETBULK: /* SNMPv2 Bulk Request */ /* request id */ bufp = asn_build_int(DestBuf, DestBufLen, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), &PDU->reqid, sizeof(PDU->reqid)); if (bufp == NULL) return (NULL); /* non-repeaters */ bufp = asn_build_int(bufp, DestBufLen, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), &PDU->non_repeaters, sizeof(PDU->non_repeaters)); if (bufp == NULL) return (NULL); /* max-repetitions */ bufp = asn_build_int(bufp, DestBufLen, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), &PDU->max_repetitions, sizeof(PDU->max_repetitions)); if (bufp == NULL) return (NULL); break; /**********************************************************************/ default: /* Normal PDU Encoding */ /* request id */ #if DEBUG_PDU_ENCODE snmplib_debug(8, "PDU: Request ID %d (0x%x)\n", PDU->reqid, DestBuf); #endif bufp = asn_build_int(DestBuf, DestBufLen, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), &PDU->reqid, sizeof(PDU->reqid)); if (bufp == NULL) return (NULL); /* error status */ #if DEBUG_PDU_ENCODE snmplib_debug(8, "PDU: Error Status %d (0x%x)\n", PDU->errstat, bufp); #endif bufp = asn_build_int(bufp, DestBufLen, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), &PDU->errstat, sizeof(PDU->errstat)); if (bufp == NULL) return (NULL); /* error index */ #if DEBUG_PDU_ENCODE snmplib_debug(8, "PDU: Error index %d (0x%x)\n", PDU->errindex, bufp); #endif bufp = asn_build_int(bufp, DestBufLen, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), &PDU->errindex, sizeof(PDU->errindex)); if (bufp == NULL) return (NULL); break; } /* End of encoding */ return (bufp); } /**********************************************************************/ /* Decodes PDU from Packet into PDU. * * Returns a pointer to the next byte of the packet, which is where the * Variable Bindings start. */ u_char * snmp_pdu_decode(u_char * Packet, /* data */ int *Length, /* &length */ struct snmp_pdu * PDU) { /* pdu */ u_char *bufp; u_char PDUType; u_char ASNType; #if UNUSED_CODE int four; oid objid[MAX_NAME_LEN]; #endif bufp = asn_parse_header(Packet, Length, &PDUType); if (bufp == NULL) ASN_PARSE_ERROR(NULL); #if DEBUG_PDU_DECODE snmplib_debug(8, "PDU Type: %d\n", PDUType); #endif PDU->command = PDUType; switch (PDUType) { #if TRP_REQ_MSG case TRP_REQ_MSG: /* SNMPv1 Trap Message */ /* enterprise */ PDU->enterprise_length = MAX_NAME_LEN; bufp = asn_parse_objid(bufp, Length, &ASNType, objid, &PDU->enterprise_length); if (bufp == NULL) ASN_PARSE_ERROR(NULL); PDU->enterprise = (oid *) xmalloc(PDU->enterprise_length * sizeof(oid)); if (PDU->enterprise == NULL) { snmp_set_api_error(SNMPERR_OS_ERR); return (NULL); } memcpy((char *) PDU->enterprise, (char *) objid, PDU->enterprise_length * sizeof(oid)); /* Agent-addr */ four = 4; bufp = asn_parse_string(bufp, Length, &ASNType, (u_char *) & PDU->agent_addr.sin_addr.s_addr, &four); if (bufp == NULL) ASN_PARSE_ERROR(NULL); /* Generic trap */ bufp = asn_parse_int(bufp, Length, &ASNType, (int *) &PDU->trap_type, sizeof(PDU->trap_type)); if (bufp == NULL) ASN_PARSE_ERROR(NULL); /* Specific Trap */ bufp = asn_parse_int(bufp, Length, &ASNType, (int *) &PDU->specific_type, sizeof(PDU->specific_type)); if (bufp == NULL) ASN_PARSE_ERROR(NULL); /* Timestamp */ bufp = asn_parse_unsigned_int(bufp, Length, &ASNType, &PDU->time, sizeof(PDU->time)); if (bufp == NULL) ASN_PARSE_ERROR(NULL); break; #endif /**********************************************************************/ case SNMP_PDU_GETBULK: /* SNMPv2 Bulk Request */ /* request id */ bufp = asn_parse_int(bufp, Length, &ASNType, &PDU->reqid, sizeof(PDU->reqid)); if (bufp == NULL) ASN_PARSE_ERROR(NULL); /* non-repeaters */ bufp = asn_parse_int(bufp, Length, &ASNType, &PDU->non_repeaters, sizeof(PDU->non_repeaters)); if (bufp == NULL) ASN_PARSE_ERROR(NULL); /* max-repetitions */ bufp = asn_parse_int(bufp, Length, &ASNType, &PDU->max_repetitions, sizeof(PDU->max_repetitions)); if (bufp == NULL) ASN_PARSE_ERROR(NULL); break; /**********************************************************************/ default: /* Normal PDU Encoding */ /* request id */ bufp = asn_parse_int(bufp, Length, &ASNType, &PDU->reqid, sizeof(PDU->reqid)); if (bufp == NULL) ASN_PARSE_ERROR(NULL); #if DEBUG_PDU_DECODE snmplib_debug(8, "PDU Request ID: %d\n", PDU->reqid); #endif /* error status */ bufp = asn_parse_int(bufp, Length, &ASNType, &PDU->errstat, sizeof(PDU->errstat)); if (bufp == NULL) ASN_PARSE_ERROR(NULL); #if DEBUG_PDU_DECODE snmplib_debug(8, "PDU Error Status: %d\n", PDU->errstat); #endif /* error index */ bufp = asn_parse_int(bufp, Length, &ASNType, &PDU->errindex, sizeof(PDU->errindex)); if (bufp == NULL) ASN_PARSE_ERROR(NULL); #if DEBUG_PDU_DECODE snmplib_debug(8, "PDU Error Index: %d\n", PDU->errindex); #endif break; } return (bufp); } /* * Add a null variable with the requested name to the end of the list of * variables for this pdu. */ void snmp_add_null_var(struct snmp_pdu *pdu, oid * name, int name_length) { struct variable_list *vars; struct variable_list *ptr; vars = snmp_var_new(name, name_length); if (vars == NULL) { perror("snmp_add_null_var:xmalloc"); return; } if (pdu->variables == NULL) { pdu->variables = vars; } else { /* Insert at the end */ for (ptr = pdu->variables; ptr->next_variable; ptr = ptr->next_variable) /*EXIT */ ; ptr->next_variable = vars; } return; }