| /* Portions of this file are subject to the following copyright(s). See |
| * the Net-SNMP's COPYING file for more details and other copyrights |
| * that may apply: |
| */ |
| /****************************************************************** |
| Copyright 1989, 1991, 1992 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. |
| ******************************************************************/ |
| /* |
| * Portions of this file are copyrighted by: |
| * Copyright Copyright 2003 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms specified in the COPYING file |
| * distributed with the Net-SNMP package. |
| */ |
| |
| /** @defgroup library The Net-SNMP library |
| * @{ |
| */ |
| /* |
| * snmp_api.c - API for access to snmp. |
| */ |
| #include <net-snmp/net-snmp-config.h> |
| |
| #include <stdio.h> |
| #include <ctype.h> |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <sys/types.h> |
| #if HAVE_SYS_PARAM_H |
| #include <sys/param.h> |
| #endif |
| #if TIME_WITH_SYS_TIME |
| # ifdef WIN32 |
| # include <sys/timeb.h> |
| # else |
| # include <sys/time.h> |
| # endif |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #if HAVE_ARPA_INET_H |
| #include <arpa/inet.h> |
| #endif |
| #if HAVE_SYS_SELECT_H |
| #include <sys/select.h> |
| #endif |
| #if HAVE_IO_H |
| #include <io.h> |
| #endif |
| #if HAVE_WINSOCK_H |
| #include <winsock.h> |
| #endif |
| #if HAVE_SYS_SOCKET_H |
| #include <sys/socket.h> |
| #endif |
| #if HAVE_SYS_UN_H |
| #include <sys/un.h> |
| #endif |
| #if HAVE_NETDB_H |
| #include <netdb.h> |
| #endif |
| #if HAVE_NET_IF_DL_H |
| #ifndef dynix |
| #include <net/if_dl.h> |
| #else |
| #include <sys/net/if_dl.h> |
| #endif |
| #endif |
| #include <errno.h> |
| |
| #if HAVE_LOCALE_H |
| #include <locale.h> |
| #endif |
| |
| #if HAVE_DMALLOC_H |
| #include <dmalloc.h> |
| #endif |
| |
| #define SNMP_NEED_REQUEST_LIST |
| #include <net-snmp/types.h> |
| #include <net-snmp/output_api.h> |
| #include <net-snmp/config_api.h> |
| #include <net-snmp/utilities.h> |
| |
| #include <net-snmp/library/asn1.h> |
| #include <net-snmp/library/snmp.h> /* for xdump & {build,parse}_var_op */ |
| #include <net-snmp/library/snmp_api.h> |
| #include <net-snmp/library/snmp_client.h> |
| #include <net-snmp/library/parse.h> |
| #include <net-snmp/library/mib.h> |
| #include <net-snmp/library/int64.h> |
| #include <net-snmp/library/snmpv3.h> |
| #include <net-snmp/library/callback.h> |
| #include <net-snmp/library/container.h> |
| #include <net-snmp/library/snmp_secmod.h> |
| #ifdef NETSNMP_SECMOD_USM |
| #include <net-snmp/library/snmpusm.h> |
| #endif |
| #ifdef NETSNMP_SECMOD_KSM |
| #include <net-snmp/library/snmpksm.h> |
| #endif |
| #include <net-snmp/library/keytools.h> |
| #include <net-snmp/library/lcd_time.h> |
| #include <net-snmp/library/snmp_alarm.h> |
| #include <net-snmp/library/snmp_transport.h> |
| #include <net-snmp/library/snmp_service.h> |
| #include <net-snmp/library/vacm.h> |
| |
| static void _init_snmp(void); |
| |
| #include "../agent/mibgroup/agentx/protocol.h" |
| #include <net-snmp/library/transform_oids.h> |
| #ifndef timercmp |
| #define timercmp(tvp, uvp, cmp) \ |
| /* CSTYLED */ \ |
| ((tvp)->tv_sec cmp (uvp)->tv_sec || \ |
| ((tvp)->tv_sec == (uvp)->tv_sec && \ |
| /* CSTYLED */ \ |
| (tvp)->tv_usec cmp (uvp)->tv_usec)) |
| #endif |
| #ifndef timerclear |
| #define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 |
| #endif |
| |
| /* |
| * Globals. |
| */ |
| #define MAX_PACKET_LENGTH (0x7fffffff) |
| #ifndef NETSNMP_STREAM_QUEUE_LEN |
| #define NETSNMP_STREAM_QUEUE_LEN 5 |
| #endif |
| |
| #ifndef BSD4_3 |
| #define BSD4_2 |
| #endif |
| |
| #ifndef FD_SET |
| |
| typedef long fd_mask; |
| #define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ |
| |
| #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) |
| #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) |
| #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) |
| #define FD_ZERO(p) memset((p), 0, sizeof(*(p))) |
| #endif |
| |
| static oid default_enterprise[] = { 1, 3, 6, 1, 4, 1, 3, 1, 1 }; |
| /* |
| * enterprises.cmu.systems.cmuSNMP |
| */ |
| |
| #define DEFAULT_COMMUNITY "public" |
| #define DEFAULT_RETRIES 5 |
| #define DEFAULT_TIMEOUT 1000000L |
| #define DEFAULT_REMPORT SNMP_PORT |
| #define DEFAULT_ENTERPRISE default_enterprise |
| #define DEFAULT_TIME 0 |
| |
| /* |
| * don't set higher than 0x7fffffff, and I doubt it should be that high |
| * * = 4 gig snmp messages max |
| */ |
| #define MAXIMUM_PACKET_SIZE 0x7fffffff |
| |
| /* |
| * Internal information about the state of the snmp session. |
| */ |
| struct snmp_internal_session { |
| netsnmp_request_list *requests; /* Info about outstanding requests */ |
| netsnmp_request_list *requestsEnd; /* ptr to end of list */ |
| int (*hook_pre) (netsnmp_session *, netsnmp_transport *, |
| void *, int); |
| int (*hook_parse) (netsnmp_session *, netsnmp_pdu *, |
| u_char *, size_t); |
| int (*hook_post) (netsnmp_session *, netsnmp_pdu *, int); |
| int (*hook_build) (netsnmp_session *, netsnmp_pdu *, |
| u_char *, size_t *); |
| int (*hook_realloc_build) (netsnmp_session *, |
| netsnmp_pdu *, u_char **, |
| size_t *, size_t *); |
| int (*check_packet) (u_char *, size_t); |
| netsnmp_pdu *(*hook_create_pdu) (netsnmp_transport *, |
| void *, size_t); |
| |
| u_char *packet; |
| size_t packet_len, packet_size; |
| }; |
| |
| /* |
| * The list of active/open sessions. |
| */ |
| struct session_list { |
| struct session_list *next; |
| netsnmp_session *session; |
| netsnmp_transport *transport; |
| struct snmp_internal_session *internal; |
| }; |
| |
| |
| |
| static const char *api_errors[-SNMPERR_MAX + 1] = { |
| "No error", /* SNMPERR_SUCCESS */ |
| "Generic error", /* SNMPERR_GENERR */ |
| "Invalid local port", /* SNMPERR_BAD_LOCPORT */ |
| "Unknown host", /* SNMPERR_BAD_ADDRESS */ |
| "Unknown session", /* SNMPERR_BAD_SESSION */ |
| "Too long", /* SNMPERR_TOO_LONG */ |
| "No socket", /* SNMPERR_NO_SOCKET */ |
| "Cannot send V2 PDU on V1 session", /* SNMPERR_V2_IN_V1 */ |
| "Cannot send V1 PDU on V2 session", /* SNMPERR_V1_IN_V2 */ |
| "Bad value for non-repeaters", /* SNMPERR_BAD_REPEATERS */ |
| "Bad value for max-repetitions", /* SNMPERR_BAD_REPETITIONS */ |
| "Error building ASN.1 representation", /* SNMPERR_BAD_ASN1_BUILD */ |
| "Failure in sendto", /* SNMPERR_BAD_SENDTO */ |
| "Bad parse of ASN.1 type", /* SNMPERR_BAD_PARSE */ |
| "Bad version specified", /* SNMPERR_BAD_VERSION */ |
| "Bad source party specified", /* SNMPERR_BAD_SRC_PARTY */ |
| "Bad destination party specified", /* SNMPERR_BAD_DST_PARTY */ |
| "Bad context specified", /* SNMPERR_BAD_CONTEXT */ |
| "Bad community specified", /* SNMPERR_BAD_COMMUNITY */ |
| "Cannot send noAuth/Priv", /* SNMPERR_NOAUTH_DESPRIV */ |
| "Bad ACL definition", /* SNMPERR_BAD_ACL */ |
| "Bad Party definition", /* SNMPERR_BAD_PARTY */ |
| "Session abort failure", /* SNMPERR_ABORT */ |
| "Unknown PDU type", /* SNMPERR_UNKNOWN_PDU */ |
| "Timeout", /* SNMPERR_TIMEOUT */ |
| "Failure in recvfrom", /* SNMPERR_BAD_RECVFROM */ |
| "Unable to determine contextEngineID", /* SNMPERR_BAD_ENG_ID */ |
| "No securityName specified", /* SNMPERR_BAD_SEC_NAME */ |
| "Unable to determine securityLevel", /* SNMPERR_BAD_SEC_LEVEL */ |
| "ASN.1 parse error in message", /* SNMPERR_ASN_PARSE_ERR */ |
| "Unknown security model in message", /* SNMPERR_UNKNOWN_SEC_MODEL */ |
| "Invalid message (e.g. msgFlags)", /* SNMPERR_INVALID_MSG */ |
| "Unknown engine ID", /* SNMPERR_UNKNOWN_ENG_ID */ |
| "Unknown user name", /* SNMPERR_UNKNOWN_USER_NAME */ |
| "Unsupported security level", /* SNMPERR_UNSUPPORTED_SEC_LEVEL */ |
| "Authentication failure (incorrect password, community or key)", /* SNMPERR_AUTHENTICATION_FAILURE */ |
| "Not in time window", /* SNMPERR_NOT_IN_TIME_WINDOW */ |
| "Decryption error", /* SNMPERR_DECRYPTION_ERR */ |
| "SCAPI general failure", /* SNMPERR_SC_GENERAL_FAILURE */ |
| "SCAPI sub-system not configured", /* SNMPERR_SC_NOT_CONFIGURED */ |
| "Key tools not available", /* SNMPERR_KT_NOT_AVAILABLE */ |
| "Unknown Report message", /* SNMPERR_UNKNOWN_REPORT */ |
| "USM generic error", /* SNMPERR_USM_GENERICERROR */ |
| "USM unknown security name (no such user exists)", /* SNMPERR_USM_UNKNOWNSECURITYNAME */ |
| "USM unsupported security level (this user has not been configured for that level of security)", /* SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL */ |
| "USM encryption error", /* SNMPERR_USM_ENCRYPTIONERROR */ |
| "USM authentication failure (incorrect password or key)", /* SNMPERR_USM_AUTHENTICATIONFAILURE */ |
| "USM parse error", /* SNMPERR_USM_PARSEERROR */ |
| "USM unknown engineID", /* SNMPERR_USM_UNKNOWNENGINEID */ |
| "USM not in time window", /* SNMPERR_USM_NOTINTIMEWINDOW */ |
| "USM decryption error", /* SNMPERR_USM_DECRYPTIONERROR */ |
| "MIB not initialized", /* SNMPERR_NOMIB */ |
| "Value out of range", /* SNMPERR_RANGE */ |
| "Sub-id out of range", /* SNMPERR_MAX_SUBID */ |
| "Bad sub-id in object identifier", /* SNMPERR_BAD_SUBID */ |
| "Object identifier too long", /* SNMPERR_LONG_OID */ |
| "Bad value name", /* SNMPERR_BAD_NAME */ |
| "Bad value notation", /* SNMPERR_VALUE */ |
| "Unknown Object Identifier", /* SNMPERR_UNKNOWN_OBJID */ |
| "No PDU in snmp_send", /* SNMPERR_NULL_PDU */ |
| "Missing variables in PDU", /* SNMPERR_NO_VARS */ |
| "Bad variable type", /* SNMPERR_VAR_TYPE */ |
| "Out of memory (malloc failure)", /* SNMPERR_MALLOC */ |
| "Kerberos related error", /* SNMPERR_KRB5 */ |
| "Protocol error", /* SNMPERR_PROTOCOL */ |
| "OID not increasing", /* SNMPERR_OID_NONINCREASING */ |
| }; |
| |
| static const char *secLevelName[] = { |
| "BAD_SEC_LEVEL", |
| "noAuthNoPriv", |
| "authNoPriv", |
| "authPriv" |
| }; |
| |
| /* |
| * Multiple threads may changes these variables. |
| * Suggest using the Single API, which does not use Sessions. |
| * |
| * Reqid may need to be protected. Time will tell... |
| * |
| */ |
| /* |
| * MTCRITICAL_RESOURCE |
| */ |
| /* |
| * use token in comments to individually protect these resources |
| */ |
| struct session_list *Sessions = NULL; /* MT_LIB_SESSION */ |
| static long Reqid = 0; /* MT_LIB_REQUESTID */ |
| static long Msgid = 0; /* MT_LIB_MESSAGEID */ |
| static long Sessid = 0; /* MT_LIB_SESSIONID */ |
| static long Transid = 0; /* MT_LIB_TRANSID */ |
| int snmp_errno = 0; |
| /* |
| * END MTCRITICAL_RESOURCE |
| */ |
| |
| /* |
| * global error detail storage |
| */ |
| static char snmp_detail[192]; |
| static int snmp_detail_f = 0; |
| |
| /* |
| * Prototypes. |
| */ |
| int snmp_build(u_char ** pkt, size_t * pkt_len, |
| size_t * offset, netsnmp_session * pss, |
| netsnmp_pdu *pdu); |
| static int snmp_parse(void *, netsnmp_session *, netsnmp_pdu *, |
| u_char *, size_t); |
| |
| static void snmpv3_calc_msg_flags(int, int, u_char *); |
| static int snmpv3_verify_msg(netsnmp_request_list *, netsnmp_pdu *); |
| static int snmpv3_build_probe_pdu(netsnmp_pdu **); |
| static int snmpv3_build(u_char ** pkt, size_t * pkt_len, |
| size_t * offset, netsnmp_session * session, |
| netsnmp_pdu *pdu); |
| static int snmp_parse_version(u_char *, size_t); |
| static int snmp_resend_request(struct session_list *slp, |
| netsnmp_request_list *rp, |
| int incr_retries); |
| static void register_default_handlers(void); |
| static struct session_list *snmp_sess_copy(netsnmp_session * pss); |
| int snmp_get_errno(void); |
| void snmp_synch_reset(netsnmp_session * notused); |
| void snmp_synch_setup(netsnmp_session * notused); |
| |
| #ifndef HAVE_STRERROR |
| const char * |
| strerror(int err) |
| { |
| extern const char *sys_errlist[]; |
| extern int sys_nerr; |
| |
| if (err < 0 || err >= sys_nerr) |
| return "Unknown error"; |
| return sys_errlist[err]; |
| } |
| #endif |
| |
| const char * |
| snmp_pdu_type(int type) |
| { |
| static char unknown[20]; |
| switch(type) { |
| case SNMP_MSG_GET: |
| return "GET"; |
| case SNMP_MSG_GETNEXT: |
| return "GETNEXT"; |
| case SNMP_MSG_RESPONSE: |
| return "RESPONSE"; |
| case SNMP_MSG_SET: |
| return "SET"; |
| case SNMP_MSG_GETBULK: |
| return "GETBULK"; |
| case SNMP_MSG_INFORM: |
| return "INFORM"; |
| case SNMP_MSG_TRAP2: |
| return "TRAP2"; |
| case SNMP_MSG_REPORT: |
| return "REPORT"; |
| default: |
| snprintf(unknown, sizeof(unknown), "?0x%2X?", type); |
| return unknown; |
| } |
| } |
| |
| #define DEBUGPRINTPDUTYPE(token, type) \ |
| DEBUGDUMPSECTION(token, snmp_pdu_type(type)) |
| |
| long |
| snmp_get_next_reqid(void) |
| { |
| long retVal; |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_REQUESTID); |
| retVal = 1 + Reqid; /*MTCRITICAL_RESOURCE */ |
| if (!retVal) |
| retVal = 2; |
| Reqid = retVal; |
| if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS)) |
| retVal &= 0x7fff; /* mask to 15 bits */ |
| else |
| retVal &= 0x7fffffff; /* mask to 31 bits */ |
| |
| if (!retVal) { |
| Reqid = retVal = 2; |
| } |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_REQUESTID); |
| return retVal; |
| } |
| |
| long |
| snmp_get_next_msgid(void) |
| { |
| long retVal; |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_MESSAGEID); |
| retVal = 1 + Msgid; /*MTCRITICAL_RESOURCE */ |
| if (!retVal) |
| retVal = 2; |
| Msgid = retVal; |
| if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS)) |
| retVal &= 0x7fff; /* mask to 15 bits */ |
| else |
| retVal &= 0x7fffffff; /* mask to 31 bits */ |
| |
| if (!retVal) { |
| Msgid = retVal = 2; |
| } |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_MESSAGEID); |
| return retVal; |
| } |
| |
| long |
| snmp_get_next_sessid(void) |
| { |
| long retVal; |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSIONID); |
| retVal = 1 + Sessid; /*MTCRITICAL_RESOURCE */ |
| if (!retVal) |
| retVal = 2; |
| Sessid = retVal; |
| if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS)) |
| retVal &= 0x7fff; /* mask to 15 bits */ |
| else |
| retVal &= 0x7fffffff; /* mask to 31 bits */ |
| |
| if (!retVal) { |
| Sessid = retVal = 2; |
| } |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSIONID); |
| return retVal; |
| } |
| |
| long |
| snmp_get_next_transid(void) |
| { |
| long retVal; |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_TRANSID); |
| retVal = 1 + Transid; /*MTCRITICAL_RESOURCE */ |
| if (!retVal) |
| retVal = 2; |
| Transid = retVal; |
| if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS)) |
| retVal &= 0x7fff; /* mask to 15 bits */ |
| else |
| retVal &= 0x7fffffff; /* mask to 31 bits */ |
| |
| if (!retVal) { |
| Transid = retVal = 2; |
| } |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_TRANSID); |
| return retVal; |
| } |
| |
| void |
| snmp_perror(const char *prog_string) |
| { |
| const char *str; |
| int xerr; |
| xerr = snmp_errno; /*MTCRITICAL_RESOURCE */ |
| str = snmp_api_errstring(xerr); |
| snmp_log(LOG_ERR, "%s: %s\n", prog_string, str); |
| } |
| |
| void |
| snmp_set_detail(const char *detail_string) |
| { |
| if (detail_string != NULL) { |
| strlcpy((char *) snmp_detail, detail_string, sizeof(snmp_detail)); |
| snmp_detail_f = 1; |
| } |
| } |
| |
| /* |
| * returns pointer to static data |
| */ |
| /* |
| * results not guaranteed in multi-threaded use |
| */ |
| const char * |
| snmp_api_errstring(int snmp_errnumber) |
| { |
| const char *msg = ""; |
| static char msg_buf[SPRINT_MAX_LEN]; |
| |
| if (snmp_errnumber >= SNMPERR_MAX && snmp_errnumber <= SNMPERR_GENERR) { |
| msg = api_errors[-snmp_errnumber]; |
| } else if (snmp_errnumber != SNMPERR_SUCCESS) { |
| msg = NULL; |
| } |
| if (!msg) { |
| snprintf(msg_buf, sizeof(msg_buf), "Unknown error: %d", snmp_errnumber); |
| msg_buf[sizeof(msg_buf)-1] = '\0'; |
| } else if (snmp_detail_f) { |
| snprintf(msg_buf, sizeof(msg_buf), "%s (%s)", msg, snmp_detail); |
| msg_buf[sizeof(msg_buf)-1] = '\0'; |
| snmp_detail_f = 0; |
| } else { |
| strlcpy(msg_buf, msg, sizeof(msg_buf)); |
| } |
| |
| return (msg_buf); |
| } |
| |
| /* |
| * snmp_error - return error data |
| * Inputs : address of errno, address of snmp_errno, address of string |
| * Caller must free the string returned after use. |
| */ |
| void |
| snmp_error(netsnmp_session * psess, |
| int *p_errno, int *p_snmp_errno, char **p_str) |
| { |
| char buf[SPRINT_MAX_LEN]; |
| int snmp_errnumber; |
| |
| if (p_errno) |
| *p_errno = psess->s_errno; |
| if (p_snmp_errno) |
| *p_snmp_errno = psess->s_snmp_errno; |
| if (p_str == NULL) |
| return; |
| |
| strcpy(buf, ""); |
| snmp_errnumber = psess->s_snmp_errno; |
| if (snmp_errnumber >= SNMPERR_MAX && snmp_errnumber <= SNMPERR_GENERR) { |
| if (snmp_detail_f) { |
| snprintf(buf, sizeof(buf), "%s (%s)", api_errors[-snmp_errnumber], |
| snmp_detail); |
| buf[sizeof(buf)-1] = '\0'; |
| snmp_detail_f = 0; |
| } |
| else |
| strlcpy(buf, api_errors[-snmp_errnumber], sizeof(buf)); |
| } else { |
| if (snmp_errnumber) { |
| snprintf(buf, sizeof(buf), "Unknown Error %d", snmp_errnumber); |
| buf[sizeof(buf)-1] = '\0'; |
| } |
| } |
| |
| /* |
| * append a useful system errno interpretation. |
| */ |
| if (psess->s_errno) { |
| const char* error = strerror(psess->s_errno); |
| if(error == NULL) |
| error = "Unknown Error"; |
| snprintf (&buf[strlen(buf)], sizeof(buf)-strlen(buf), |
| " (%s)", error); |
| } |
| buf[sizeof(buf)-1] = '\0'; |
| *p_str = strdup(buf); |
| } |
| |
| /* |
| * snmp_sess_error - same as snmp_error for single session API use. |
| */ |
| void |
| snmp_sess_error(void *sessp, int *p_errno, int *p_snmp_errno, char **p_str) |
| { |
| struct session_list *slp = (struct session_list *) sessp; |
| |
| if ((slp) && (slp->session)) |
| snmp_error(slp->session, p_errno, p_snmp_errno, p_str); |
| } |
| |
| /* |
| * netsnmp_sess_log_error(): print a error stored in a session pointer |
| */ |
| void |
| netsnmp_sess_log_error(int priority, |
| const char *prog_string, netsnmp_session * ss) |
| { |
| char *err; |
| snmp_error(ss, NULL, NULL, &err); |
| snmp_log(priority, "%s: %s\n", prog_string, err); |
| SNMP_FREE(err); |
| } |
| |
| /* |
| * snmp_sess_perror(): print a error stored in a session pointer |
| */ |
| void |
| snmp_sess_perror(const char *prog_string, netsnmp_session * ss) |
| { |
| netsnmp_sess_log_error(LOG_ERR, prog_string, ss); |
| } |
| |
| |
| |
| /* |
| * Primordial SNMP library initialization. |
| * Initializes mutex locks. |
| * Invokes minimum required initialization for displaying MIB objects. |
| * Gets initial request ID for all transactions, |
| * and finds which port SNMP over UDP uses. |
| * SNMP over AppleTalk is not currently supported. |
| * |
| * Warning: no debug messages here. |
| */ |
| static char _init_snmp_init_done = 0; |
| static void |
| _init_snmp(void) |
| { |
| |
| struct timeval tv; |
| long tmpReqid, tmpMsgid; |
| |
| if (_init_snmp_init_done) |
| return; |
| _init_snmp_init_done = 1; |
| Reqid = 1; |
| |
| snmp_res_init(); /* initialize the mt locking structures */ |
| #ifndef NETSNMP_DISABLE_MIB_LOADING |
| netsnmp_init_mib_internals(); |
| #endif /* NETSNMP_DISABLE_MIB_LOADING */ |
| netsnmp_tdomain_init(); |
| |
| gettimeofday(&tv, (struct timezone *) 0); |
| /* |
| * Now = tv; |
| */ |
| |
| /* |
| * get pseudo-random values for request ID and message ID |
| */ |
| /* |
| * don't allow zero value to repeat init |
| */ |
| #ifdef SVR4 |
| srand48(tv.tv_sec ^ tv.tv_usec); |
| tmpReqid = lrand48(); |
| tmpMsgid = lrand48(); |
| #else |
| srandom(tv.tv_sec ^ tv.tv_usec); |
| tmpReqid = random(); |
| tmpMsgid = random(); |
| #endif |
| |
| if (tmpReqid == 0) |
| tmpReqid = 1; |
| if (tmpMsgid == 0) |
| tmpMsgid = 1; |
| Reqid = tmpReqid; |
| Msgid = tmpMsgid; |
| |
| netsnmp_register_default_domain("snmp", "udp udp6"); |
| netsnmp_register_default_domain("snmptrap", "udp udp6"); |
| |
| netsnmp_register_default_target("snmp", "udp", ":161"); |
| netsnmp_register_default_target("snmp", "tcp", ":161"); |
| netsnmp_register_default_target("snmp", "udp6", ":161"); |
| netsnmp_register_default_target("snmp", "tcp6", ":161"); |
| netsnmp_register_default_target("snmp", "ipx", "/36879"); |
| netsnmp_register_default_target("snmptrap", "udp", ":162"); |
| netsnmp_register_default_target("snmptrap", "tcp", ":162"); |
| netsnmp_register_default_target("snmptrap", "udp6", ":162"); |
| netsnmp_register_default_target("snmptrap", "tcp6", ":162"); |
| netsnmp_register_default_target("snmptrap", "ipx", "/36880"); |
| |
| netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_HEX_OUTPUT_LENGTH, 16); |
| |
| #ifdef NETSNMP_USE_REVERSE_ASNENCODING |
| netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_REVERSE_ENCODE, |
| NETSNMP_DEFAULT_ASNENCODING_DIRECTION); |
| #endif |
| } |
| |
| /* |
| * Initializes the session structure. |
| * May perform one time minimal library initialization. |
| * No MIB file processing is done via this call. |
| */ |
| void |
| snmp_sess_init(netsnmp_session * session) |
| { |
| _init_snmp(); |
| |
| /* |
| * initialize session to default values |
| */ |
| |
| memset(session, 0, sizeof(netsnmp_session)); |
| session->remote_port = SNMP_DEFAULT_REMPORT; |
| session->timeout = SNMP_DEFAULT_TIMEOUT; |
| session->retries = SNMP_DEFAULT_RETRIES; |
| session->version = SNMP_DEFAULT_VERSION; |
| session->securityModel = SNMP_DEFAULT_SECMODEL; |
| session->rcvMsgMaxSize = SNMP_MAX_MSG_SIZE; |
| session->flags |= SNMP_FLAGS_DONT_PROBE; |
| } |
| |
| |
| static void |
| register_default_handlers(void) |
| { |
| netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "dumpPacket", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DUMP_PACKET); |
| netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "reverseEncodeBER", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_ENCODE); |
| netsnmp_ds_register_config(ASN_INTEGER, "snmp", "defaultPort", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DEFAULT_PORT); |
| #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
| netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defCommunity", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_COMMUNITY); |
| #endif |
| netsnmp_ds_register_premib(ASN_BOOLEAN, "snmp", "noTokenWarnings", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NO_TOKEN_WARNINGS); |
| netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noRangeCheck", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_CHECK_RANGE); |
| netsnmp_ds_register_premib(ASN_OCTET_STR, "snmp", "persistentDir", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PERSISTENT_DIR); |
| netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "tempFilePattern", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_TEMP_FILE_PATTERN); |
| netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noDisplayHint", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NO_DISPLAY_HINT); |
| netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "16bitIDs", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_16BIT_IDS); |
| netsnmp_ds_register_premib(ASN_OCTET_STR, "snmp", "clientaddr", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENT_ADDR); |
| netsnmp_ds_register_config(ASN_INTEGER, "snmp", "serverSendBuf", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SERVERSENDBUF); |
| netsnmp_ds_register_config(ASN_INTEGER, "snmp", "serverRecvBuf", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SERVERRECVBUF); |
| netsnmp_ds_register_config(ASN_INTEGER, "snmp", "clientSendBuf", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENTSENDBUF); |
| netsnmp_ds_register_config(ASN_INTEGER, "snmp", "clientRecvBuf", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_CLIENTRECVBUF); |
| netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noPersistentLoad", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD); |
| netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "noPersistentSave", |
| NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE); |
| netsnmp_register_service_handlers(); |
| } |
| |
| static int init_snmp_init_done = 0; /* To prevent double init's. */ |
| /** |
| * Calls the functions to do config file loading and mib module parsing |
| * in the correct order. |
| * |
| * @param type label for the config file "type" |
| * |
| * @return void |
| * |
| * @see init_agent |
| */ |
| void |
| init_snmp(const char *type) |
| { |
| if (init_snmp_init_done) { |
| return; |
| } |
| |
| init_snmp_init_done = 1; |
| |
| /* |
| * make the type available everywhere else |
| */ |
| if (type && !netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_APPTYPE)) { |
| netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_APPTYPE, type); |
| } |
| |
| _init_snmp(); |
| |
| /* |
| * set our current locale properly to initialize isprint() type functions |
| */ |
| #ifdef HAVE_SETLOCALE |
| setlocale(LC_CTYPE, ""); |
| #endif |
| |
| snmp_debug_init(); /* should be done first, to turn on debugging ASAP */ |
| netsnmp_container_init_list(); |
| init_callbacks(); |
| init_snmp_logging(); |
| snmp_init_statistics(); |
| register_mib_handlers(); |
| register_default_handlers(); |
| init_snmpv3(type); |
| init_snmp_alarm(); |
| init_snmp_enum(type); |
| init_vacm(); |
| |
| read_premib_configs(); |
| #ifndef NETSNMP_DISABLE_MIB_LOADING |
| netsnmp_init_mib(); |
| #endif /* NETSNMP_DISABLE_MIB_LOADING */ |
| |
| read_configs(); |
| |
| } /* end init_snmp() */ |
| |
| void |
| snmp_store(const char *type) |
| { |
| DEBUGMSGTL(("snmp_store", "storing stuff...\n")); |
| snmp_save_persistent(type); |
| snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA, NULL); |
| snmp_clean_persistent(type); |
| } |
| |
| |
| /** |
| * Shuts down the application, saving any needed persistent storage, |
| * and appropriate clean up. |
| * |
| * @param type Label for the config file "type" used |
| * |
| * @return void |
| */ |
| void |
| snmp_shutdown(const char *type) |
| { |
| snmp_store(type); |
| snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN, NULL); |
| shutdown_snmp_logging(); |
| snmp_alarm_unregister_all(); |
| snmp_close_sessions(); |
| #ifndef NETSNMP_DISABLE_MIB_LOADING |
| shutdown_mib(); |
| #endif /* NETSNMP_DISABLE_MIB_LOADING */ |
| unregister_all_config_handlers(); |
| netsnmp_container_free_list(); |
| clear_sec_mod(); |
| clear_snmp_enum(); |
| netsnmp_clear_tdomain_list(); |
| clear_callback(); |
| netsnmp_ds_shutdown(); |
| clear_user_list(); |
| netsnmp_clear_default_target(); |
| netsnmp_clear_default_domain(); |
| free_etimelist(); |
| |
| init_snmp_init_done = 0; |
| _init_snmp_init_done = 0; |
| } |
| |
| |
| /* |
| * Sets up the session with the snmp_session information provided by the user. |
| * Then opens and binds the necessary low-level transport. A handle to the |
| * created session is returned (this is NOT the same as the pointer passed to |
| * snmp_open()). On any error, NULL is returned and snmp_errno is set to the |
| * appropriate error code. |
| */ |
| netsnmp_session * |
| snmp_open(netsnmp_session *session) |
| { |
| struct session_list *slp; |
| slp = (struct session_list *) snmp_sess_open(session); |
| if (!slp) { |
| return NULL; |
| } |
| |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| slp->next = Sessions; |
| Sessions = slp; |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| |
| return (slp->session); |
| } |
| |
| /* |
| * extended open |
| */ |
| netsnmp_session * |
| snmp_open_ex(netsnmp_session *session, |
| int (*fpre_parse) (netsnmp_session *, netsnmp_transport *, |
| void *, int), |
| int (*fparse) (netsnmp_session *, netsnmp_pdu *, u_char *, |
| size_t), |
| int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int), |
| |
| int (*fbuild) (netsnmp_session *, netsnmp_pdu *, u_char *, |
| size_t *), |
| int (*frbuild) (netsnmp_session *, netsnmp_pdu *, |
| u_char **, size_t *, size_t *), |
| int (*fcheck) (u_char *, size_t) |
| ) |
| { |
| struct session_list *slp; |
| slp = (struct session_list *) snmp_sess_open(session); |
| if (!slp) { |
| return NULL; |
| } |
| slp->internal->hook_pre = fpre_parse; |
| slp->internal->hook_parse = fparse; |
| slp->internal->hook_post = fpost_parse; |
| slp->internal->hook_build = fbuild; |
| slp->internal->hook_realloc_build = frbuild; |
| slp->internal->check_packet = fcheck; |
| |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| slp->next = Sessions; |
| Sessions = slp; |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| |
| return (slp->session); |
| } |
| |
| static struct session_list * |
| _sess_copy(netsnmp_session * in_session) |
| { |
| struct session_list *slp; |
| struct snmp_internal_session *isp; |
| netsnmp_session *session; |
| struct snmp_secmod_def *sptr; |
| char *cp; |
| u_char *ucp; |
| size_t i; |
| |
| in_session->s_snmp_errno = 0; |
| in_session->s_errno = 0; |
| |
| /* |
| * Copy session structure and link into list |
| */ |
| slp = (struct session_list *) calloc(1, sizeof(struct session_list)); |
| if (slp == NULL) { |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| |
| slp->transport = NULL; |
| |
| isp = (struct snmp_internal_session *)calloc(1, sizeof(struct snmp_internal_session)); |
| |
| if (isp == NULL) { |
| snmp_sess_close(slp); |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| |
| slp->internal = isp; |
| slp->session = (netsnmp_session *)malloc(sizeof(netsnmp_session)); |
| if (slp->session == NULL) { |
| snmp_sess_close(slp); |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| memmove(slp->session, in_session, sizeof(netsnmp_session)); |
| session = slp->session; |
| |
| /* |
| * zero out pointers so if we have to free the session we wont free mem |
| * owned by in_session |
| */ |
| session->localname = NULL; |
| session->peername = NULL; |
| session->community = NULL; |
| session->contextEngineID = NULL; |
| session->contextName = NULL; |
| session->securityEngineID = NULL; |
| session->securityName = NULL; |
| session->securityAuthProto = NULL; |
| session->securityPrivProto = NULL; |
| /* |
| * session now points to the new structure that still contains pointers to |
| * data allocated elsewhere. Some of this data is copied to space malloc'd |
| * here, and the pointer replaced with the new one. |
| */ |
| |
| if (in_session->peername != NULL) { |
| session->peername = (char *)malloc(strlen(in_session->peername) + 1); |
| if (session->peername == NULL) { |
| snmp_sess_close(slp); |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| strcpy(session->peername, in_session->peername); |
| } |
| |
| /* |
| * Fill in defaults if necessary |
| */ |
| #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
| if (in_session->community_len != SNMP_DEFAULT_COMMUNITY_LEN) { |
| ucp = (u_char *) malloc(in_session->community_len); |
| if (ucp != NULL) |
| memmove(ucp, in_session->community, in_session->community_len); |
| } else { |
| if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_COMMUNITY)) != NULL) { |
| session->community_len = strlen(cp); |
| ucp = (u_char *) malloc(session->community_len); |
| if (ucp) |
| memmove(ucp, cp, session->community_len); |
| } else { |
| #ifdef NETSNMP_NO_ZEROLENGTH_COMMUNITY |
| session->community_len = strlen(DEFAULT_COMMUNITY); |
| ucp = (u_char *) malloc(session->community_len); |
| if (ucp) |
| memmove(ucp, DEFAULT_COMMUNITY, session->community_len); |
| #else |
| ucp = (u_char *) strdup(""); |
| #endif |
| } |
| } |
| |
| if (ucp == NULL) { |
| snmp_sess_close(slp); |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| session->community = ucp; /* replace pointer with pointer to new data */ |
| #endif |
| |
| if (session->securityLevel <= 0) { |
| session->securityLevel = |
| netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECLEVEL); |
| } |
| |
| if (session->securityLevel == 0) |
| session->securityLevel = SNMP_SEC_LEVEL_NOAUTH; |
| |
| if (in_session->securityAuthProtoLen > 0) { |
| session->securityAuthProto = |
| snmp_duplicate_objid(in_session->securityAuthProto, |
| in_session->securityAuthProtoLen); |
| if (session->securityAuthProto == NULL) { |
| snmp_sess_close(slp); |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| } else if (get_default_authtype(&i) != NULL) { |
| session->securityAuthProto = |
| snmp_duplicate_objid(get_default_authtype(NULL), i); |
| session->securityAuthProtoLen = i; |
| } |
| |
| if (in_session->securityPrivProtoLen > 0) { |
| session->securityPrivProto = |
| snmp_duplicate_objid(in_session->securityPrivProto, |
| in_session->securityPrivProtoLen); |
| if (session->securityPrivProto == NULL) { |
| snmp_sess_close(slp); |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| } else if (get_default_privtype(&i) != NULL) { |
| session->securityPrivProto = |
| snmp_duplicate_objid(get_default_privtype(NULL), i); |
| session->securityPrivProtoLen = i; |
| } |
| |
| if (in_session->securityEngineIDLen > 0) { |
| ucp = (u_char *) malloc(in_session->securityEngineIDLen); |
| if (ucp == NULL) { |
| snmp_sess_close(slp); |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| memmove(ucp, in_session->securityEngineID, |
| in_session->securityEngineIDLen); |
| session->securityEngineID = ucp; |
| |
| } |
| |
| if (in_session->contextEngineIDLen > 0) { |
| ucp = (u_char *) malloc(in_session->contextEngineIDLen); |
| if (ucp == NULL) { |
| snmp_sess_close(slp); |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| memmove(ucp, in_session->contextEngineID, |
| in_session->contextEngineIDLen); |
| session->contextEngineID = ucp; |
| } else if (in_session->securityEngineIDLen > 0) { |
| /* |
| * default contextEngineID to securityEngineIDLen if defined |
| */ |
| ucp = (u_char *) malloc(in_session->securityEngineIDLen); |
| if (ucp == NULL) { |
| snmp_sess_close(slp); |
| in_session->s_snmp_errno = SNMPERR_MALLOC; |
| return (NULL); |
| } |
| memmove(ucp, in_session->securityEngineID, |
| in_session->securityEngineIDLen); |
| session->contextEngineID = ucp; |
| session->contextEngineIDLen = in_session->securityEngineIDLen; |
| } |
| |
| if (in_session->contextName) { |
| session->contextName = strdup(in_session->contextName); |
| if (session->contextName == NULL) { |
| snmp_sess_close(slp); |
| return (NULL); |
| } |
| session->contextNameLen = in_session->contextNameLen; |
| } else { |
| if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_CONTEXT)) != NULL) |
| cp = strdup(cp); |
| else |
| cp = strdup(SNMP_DEFAULT_CONTEXT); |
| if (cp == NULL) { |
| snmp_sess_close(slp); |
| return (NULL); |
| } |
| session->contextName = cp; |
| session->contextNameLen = strlen(cp); |
| } |
| |
| if (in_session->securityName) { |
| session->securityName = strdup(in_session->securityName); |
| if (session->securityName == NULL) { |
| snmp_sess_close(slp); |
| return (NULL); |
| } |
| } else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_SECNAME)) != NULL) { |
| cp = strdup(cp); |
| if (cp == NULL) { |
| snmp_sess_close(slp); |
| return (NULL); |
| } |
| session->securityName = cp; |
| session->securityNameLen = strlen(cp); |
| } |
| |
| if ((in_session->securityAuthKeyLen <= 0) && |
| ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_AUTHMASTERKEY)))) { |
| size_t buflen = sizeof(session->securityAuthKey); |
| u_char *tmpp = session->securityAuthKey; |
| session->securityAuthKeyLen = 0; |
| /* it will be a hex string */ |
| if (!snmp_hex_to_binary(&tmpp, &buflen, |
| &session->securityAuthKeyLen, 0, cp)) { |
| snmp_set_detail("error parsing authentication master key"); |
| snmp_sess_close(slp); |
| return NULL; |
| } |
| } else if ((in_session->securityAuthKeyLen <= 0) && |
| ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_AUTHPASSPHRASE)) || |
| (cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_PASSPHRASE)))) { |
| session->securityAuthKeyLen = USM_AUTH_KU_LEN; |
| if (generate_Ku(session->securityAuthProto, |
| session->securityAuthProtoLen, |
| (u_char *) cp, strlen(cp), |
| session->securityAuthKey, |
| &session->securityAuthKeyLen) != SNMPERR_SUCCESS) { |
| snmp_set_detail |
| ("Error generating a key (Ku) from the supplied authentication pass phrase."); |
| snmp_sess_close(slp); |
| return NULL; |
| } |
| } |
| |
| |
| if ((in_session->securityPrivKeyLen <= 0) && |
| ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_PRIVMASTERKEY)))) { |
| size_t buflen = sizeof(session->securityPrivKey); |
| u_char *tmpp = session->securityPrivKey; |
| session->securityPrivKeyLen = 0; |
| /* it will be a hex string */ |
| if (!snmp_hex_to_binary(&tmpp, &buflen, |
| &session->securityPrivKeyLen, 0, cp)) { |
| snmp_set_detail("error parsing encryption master key"); |
| snmp_sess_close(slp); |
| return NULL; |
| } |
| } else if ((in_session->securityPrivKeyLen <= 0) && |
| ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_PRIVPASSPHRASE)) || |
| (cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_PASSPHRASE)))) { |
| session->securityPrivKeyLen = USM_PRIV_KU_LEN; |
| if (generate_Ku(session->securityAuthProto, |
| session->securityAuthProtoLen, |
| (u_char *) cp, strlen(cp), |
| session->securityPrivKey, |
| &session->securityPrivKeyLen) != SNMPERR_SUCCESS) { |
| snmp_set_detail |
| ("Error generating a key (Ku) from the supplied privacy pass phrase."); |
| snmp_sess_close(slp); |
| return NULL; |
| } |
| } |
| |
| if (session->retries == SNMP_DEFAULT_RETRIES) |
| session->retries = DEFAULT_RETRIES; |
| if (session->timeout == SNMP_DEFAULT_TIMEOUT) |
| session->timeout = DEFAULT_TIMEOUT; |
| session->sessid = snmp_get_next_sessid(); |
| |
| snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SESSION_INIT, |
| session); |
| |
| if ((sptr = find_sec_mod(session->securityModel)) != NULL && |
| sptr->session_open != NULL) { |
| /* |
| * security module specific inialization |
| */ |
| (*sptr->session_open) (session); |
| } |
| |
| return (slp); |
| } |
| |
| static struct session_list * |
| snmp_sess_copy(netsnmp_session * pss) |
| { |
| struct session_list *psl; |
| psl = _sess_copy(pss); |
| if (!psl) { |
| if (!pss->s_snmp_errno) { |
| pss->s_snmp_errno = SNMPERR_GENERR; |
| } |
| SET_SNMP_ERROR(pss->s_snmp_errno); |
| } |
| return psl; |
| } |
| |
| /** |
| * probe for peer engineID |
| * |
| * @param slp session list pointer. |
| * @param in_session session for errors |
| * |
| * @note |
| * - called by _sess_open(), snmp_sess_add_ex() |
| * - in_session is the user supplied session provided to those functions. |
| * - the first session in slp should the internal allocated copy of in_session |
| * |
| * @return 0 : error |
| * @return 1 : ok |
| * |
| */ |
| int |
| snmpv3_engineID_probe(struct session_list *slp, |
| netsnmp_session * in_session) |
| { |
| netsnmp_pdu *pdu = NULL, *response = NULL; |
| netsnmp_session *session; |
| unsigned int i; |
| int status; |
| |
| if (slp == NULL || slp->session == NULL) { |
| return 0; |
| } |
| |
| session = slp->session; |
| |
| /* |
| * If we are opening a V3 session and we don't know engineID we must probe |
| * it -- this must be done after the session is created and inserted in the |
| * list so that the response can handled correctly. |
| */ |
| |
| if ((session->flags & SNMP_FLAGS_DONT_PROBE) == SNMP_FLAGS_DONT_PROBE) |
| return 1; |
| |
| if (session->version == SNMP_VERSION_3) { |
| if (session->securityEngineIDLen == 0) { |
| if (snmpv3_build_probe_pdu(&pdu) != 0) { |
| DEBUGMSGTL(("snmp_api", "unable to create probe PDU\n")); |
| return 0; |
| } |
| DEBUGMSGTL(("snmp_api", "probing for engineID...\n")); |
| session->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */ |
| status = snmp_sess_synch_response(slp, pdu, &response); |
| |
| if ((response == NULL) && (status == STAT_SUCCESS)) { |
| status = STAT_ERROR; |
| } |
| |
| switch (status) { |
| case STAT_SUCCESS: |
| in_session->s_snmp_errno = SNMPERR_INVALID_MSG; /* XX?? */ |
| DEBUGMSGTL(("snmp_sess_open", |
| "error: expected Report as response to probe: %s (%d)\n", |
| snmp_errstring(response->errstat), |
| response->errstat)); |
| break; |
| case STAT_ERROR: /* this is what we expected -> Report == STAT_ERROR */ |
| in_session->s_snmp_errno = SNMPERR_UNKNOWN_ENG_ID; |
| break; |
| case STAT_TIMEOUT: |
| in_session->s_snmp_errno = SNMPERR_TIMEOUT; |
| default: |
| DEBUGMSGTL(("snmp_sess_open", |
| "unable to connect with remote engine: %s (%d)\n", |
| snmp_api_errstring(session->s_snmp_errno), |
| session->s_snmp_errno)); |
| break; |
| } |
| |
| if (slp->session->securityEngineIDLen == 0) { |
| DEBUGMSGTL(("snmp_api", |
| "unable to determine remote engine ID\n")); |
| /* clear the flag so that probe occurs on next inform */ |
| session->flags &= ~SNMP_FLAGS_DONT_PROBE; |
| return 0; |
| } |
| |
| in_session->s_snmp_errno = SNMPERR_SUCCESS; |
| if (snmp_get_do_debugging()) { |
| DEBUGMSGTL(("snmp_sess_open", |
| " probe found engineID: ")); |
| for (i = 0; i < slp->session->securityEngineIDLen; i++) |
| DEBUGMSG(("snmp_sess_open", "%02x", |
| slp->session->securityEngineID[i])); |
| DEBUGMSG(("snmp_sess_open", "\n")); |
| } |
| } |
| |
| /* |
| * if boot/time supplied set it for this engineID |
| */ |
| if (session->engineBoots || session->engineTime) { |
| set_enginetime(session->securityEngineID, |
| session->securityEngineIDLen, |
| session->engineBoots, session->engineTime, |
| TRUE); |
| } |
| |
| if (create_user_from_session(slp->session) != SNMPERR_SUCCESS) { |
| in_session->s_snmp_errno = SNMPERR_UNKNOWN_USER_NAME; /* XX?? */ |
| DEBUGMSGTL(("snmp_api", |
| "snmpv3_engine_probe(): failed(2) to create a new user from session\n")); |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| |
| |
| /*******************************************************************-o-****** |
| * snmp_sess_open |
| * |
| * Parameters: |
| * *in_session |
| * |
| * Returns: |
| * Pointer to a session in the session list -OR- FIX -- right? |
| * NULL on failure. |
| * |
| * The "spin-free" version of snmp_open. |
| */ |
| static void * |
| _sess_open(netsnmp_session * in_session) |
| { |
| struct session_list *slp; |
| netsnmp_session *session; |
| char *clientaddr_save = NULL; |
| |
| in_session->s_snmp_errno = 0; |
| in_session->s_errno = 0; |
| |
| _init_snmp(); |
| |
| if ((slp = snmp_sess_copy(in_session)) == NULL) { |
| return (NULL); |
| } |
| session = slp->session; |
| slp->transport = NULL; |
| |
| if (NULL != session->localname) { |
| clientaddr_save = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_CLIENT_ADDR); |
| netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_CLIENT_ADDR, session->localname); |
| } |
| |
| if (session->flags & SNMP_FLAGS_STREAM_SOCKET) { |
| slp->transport = |
| netsnmp_tdomain_transport_full("snmp", session->peername, |
| session->local_port, "tcp", NULL); |
| } else { |
| slp->transport = |
| netsnmp_tdomain_transport_full("snmp", session->peername, |
| session->local_port, "udp", NULL); |
| } |
| |
| if (NULL != session->localname) |
| netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_CLIENT_ADDR, clientaddr_save); |
| |
| if (slp->transport == NULL) { |
| DEBUGMSGTL(("_sess_open", "couldn't interpret peername\n")); |
| in_session->s_snmp_errno = SNMPERR_BAD_ADDRESS; |
| in_session->s_errno = errno; |
| snmp_set_detail(session->peername); |
| snmp_sess_close(slp); |
| return NULL; |
| } |
| |
| session->rcvMsgMaxSize = slp->transport->msgMaxSize; |
| |
| if (!snmpv3_engineID_probe(slp, in_session)) { |
| snmp_sess_close(slp); |
| return NULL; |
| } |
| if (create_user_from_session(slp->session) != SNMPERR_SUCCESS) { |
| in_session->s_snmp_errno = SNMPERR_UNKNOWN_USER_NAME; /* XX?? */ |
| DEBUGMSGTL(("snmp_api", |
| "_sess_open(): failed(2) to create a new user from session\n")); |
| snmp_sess_close(slp); |
| return NULL; |
| } |
| |
| session->flags &= ~SNMP_FLAGS_DONT_PROBE; |
| |
| |
| return (void *) slp; |
| } /* end snmp_sess_open() */ |
| |
| |
| |
| /* |
| * EXTENDED SESSION API ------------------------------------------ |
| * |
| * snmp_sess_add_ex, snmp_sess_add, snmp_add |
| * |
| * Analogous to snmp_open family of functions, but taking a netsnmp_transport |
| * pointer as an extra argument. Unlike snmp_open et al. it doesn't attempt |
| * to interpret the in_session->peername as a transport endpoint specifier, |
| * but instead uses the supplied transport. JBPN |
| * |
| */ |
| |
| netsnmp_session * |
| snmp_add(netsnmp_session * in_session, |
| netsnmp_transport *transport, |
| int (*fpre_parse) (netsnmp_session *, netsnmp_transport *, void *, |
| int), int (*fpost_parse) (netsnmp_session *, |
| netsnmp_pdu *, int)) |
| { |
| struct session_list *slp; |
| slp = (struct session_list *) snmp_sess_add_ex(in_session, transport, |
| fpre_parse, NULL, |
| fpost_parse, NULL, NULL, |
| NULL, NULL); |
| if (slp == NULL) { |
| return NULL; |
| } |
| |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| slp->next = Sessions; |
| Sessions = slp; |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| |
| return (slp->session); |
| } |
| |
| netsnmp_session * |
| snmp_add_full(netsnmp_session * in_session, |
| netsnmp_transport *transport, |
| int (*fpre_parse) (netsnmp_session *, netsnmp_transport *, |
| void *, int), |
| int (*fparse) (netsnmp_session *, netsnmp_pdu *, u_char *, |
| size_t), |
| int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int), |
| int (*fbuild) (netsnmp_session *, netsnmp_pdu *, u_char *, |
| size_t *), int (*frbuild) (netsnmp_session *, |
| netsnmp_pdu *, |
| u_char **, |
| size_t *, |
| size_t *), |
| int (*fcheck) (u_char *, size_t), |
| netsnmp_pdu *(*fcreate_pdu) (netsnmp_transport *, void *, |
| size_t)) |
| { |
| struct session_list *slp; |
| slp = (struct session_list *) snmp_sess_add_ex(in_session, transport, |
| fpre_parse, fparse, |
| fpost_parse, fbuild, |
| frbuild, fcheck, |
| fcreate_pdu); |
| if (slp == NULL) { |
| return NULL; |
| } |
| |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| slp->next = Sessions; |
| Sessions = slp; |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| |
| return (slp->session); |
| } |
| |
| |
| |
| void * |
| snmp_sess_add_ex(netsnmp_session * in_session, |
| netsnmp_transport *transport, |
| int (*fpre_parse) (netsnmp_session *, netsnmp_transport *, |
| void *, int), |
| int (*fparse) (netsnmp_session *, netsnmp_pdu *, u_char *, |
| size_t), |
| int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, |
| int), |
| int (*fbuild) (netsnmp_session *, netsnmp_pdu *, u_char *, |
| size_t *), |
| int (*frbuild) (netsnmp_session *, netsnmp_pdu *, |
| u_char **, size_t *, size_t *), |
| int (*fcheck) (u_char *, size_t), |
| netsnmp_pdu *(*fcreate_pdu) (netsnmp_transport *, void *, |
| size_t)) |
| { |
| struct session_list *slp; |
| |
| _init_snmp(); |
| |
| if (transport == NULL) |
| return NULL; |
| |
| if (in_session == NULL) { |
| transport->f_close(transport); |
| netsnmp_transport_free(transport); |
| return NULL; |
| } |
| |
| DEBUGMSGTL(("snmp_sess_add", "fd %d\n", transport->sock)); |
| |
| if ((slp = snmp_sess_copy(in_session)) == NULL) { |
| transport->f_close(transport); |
| netsnmp_transport_free(transport); |
| return (NULL); |
| } |
| |
| slp->transport = transport; |
| slp->internal->hook_pre = fpre_parse; |
| slp->internal->hook_parse = fparse; |
| slp->internal->hook_post = fpost_parse; |
| slp->internal->hook_build = fbuild; |
| slp->internal->hook_realloc_build = frbuild; |
| slp->internal->check_packet = fcheck; |
| slp->internal->hook_create_pdu = fcreate_pdu; |
| |
| slp->session->rcvMsgMaxSize = transport->msgMaxSize; |
| |
| if (slp->session->version == SNMP_VERSION_3) { |
| DEBUGMSGTL(("snmp_sess_add", |
| "adding v3 session -- engineID probe now\n")); |
| if (!snmpv3_engineID_probe(slp, in_session)) { |
| DEBUGMSGTL(("snmp_sess_add", "engine ID probe failed\n")); |
| snmp_sess_close(slp); |
| return NULL; |
| } |
| if (create_user_from_session(slp->session) != SNMPERR_SUCCESS) { |
| in_session->s_snmp_errno = SNMPERR_UNKNOWN_USER_NAME; |
| DEBUGMSGTL(("snmp_api", |
| "snmp_sess_add(): failed(2) to create a new user from session\n")); |
| snmp_sess_close(slp); |
| return NULL; |
| } |
| } |
| |
| slp->session->flags &= ~SNMP_FLAGS_DONT_PROBE; |
| |
| return (void *) slp; |
| } /* end snmp_sess_add_ex() */ |
| |
| |
| |
| void * |
| snmp_sess_add(netsnmp_session * in_session, |
| netsnmp_transport *transport, |
| int (*fpre_parse) (netsnmp_session *, netsnmp_transport *, |
| void *, int), |
| int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, int)) |
| { |
| return snmp_sess_add_ex(in_session, transport, fpre_parse, NULL, |
| fpost_parse, NULL, NULL, NULL, NULL); |
| } |
| |
| |
| |
| void * |
| snmp_sess_open(netsnmp_session * pss) |
| { |
| void *pvoid; |
| pvoid = _sess_open(pss); |
| if (!pvoid) { |
| SET_SNMP_ERROR(pss->s_snmp_errno); |
| } |
| return pvoid; |
| } |
| |
| |
| |
| /* |
| * create_user_from_session(netsnmp_session *session): |
| * |
| * creates a user in the usm table from the information in a session. |
| * If the user already exists, it is updated with the current |
| * information from the session |
| * |
| * Parameters: |
| * session -- IN: pointer to the session to use when creating the user. |
| * |
| * Returns: |
| * SNMPERR_SUCCESS |
| * SNMPERR_GENERR |
| */ |
| int |
| create_user_from_session(netsnmp_session * session) |
| { |
| struct usmUser *user; |
| int user_just_created = 0; |
| char *cp; |
| |
| /* |
| * - don't create-another/copy-into user for this session by default |
| * - bail now (no error) if we don't have an engineID |
| */ |
| if (SNMP_FLAGS_USER_CREATED == (session->flags & SNMP_FLAGS_USER_CREATED) || |
| session->securityModel != SNMP_SEC_MODEL_USM || |
| session->version != SNMP_VERSION_3 || |
| session->securityNameLen == 0 || |
| session->securityEngineIDLen == 0) |
| return SNMPERR_SUCCESS; |
| |
| session->flags |= SNMP_FLAGS_USER_CREATED; |
| |
| /* |
| * now that we have the engineID, create an entry in the USM list |
| * for this user using the information in the session |
| */ |
| user = usm_get_user_from_list(session->securityEngineID, |
| session->securityEngineIDLen, |
| session->securityName, |
| usm_get_userList(), 0); |
| if (user == NULL) { |
| DEBUGMSGTL(("snmp_api", "Building user %s...\n", |
| session->securityName)); |
| /* |
| * user doesn't exist so we create and add it |
| */ |
| user = (struct usmUser *) calloc(1, sizeof(struct usmUser)); |
| if (user == NULL) |
| return SNMPERR_GENERR; |
| |
| /* |
| * copy in the securityName |
| */ |
| if (session->securityName) { |
| user->name = strdup(session->securityName); |
| user->secName = strdup(session->securityName); |
| if (user->name == NULL || user->secName == NULL) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| } |
| |
| /* |
| * copy in the engineID |
| */ |
| user->engineID = netsnmp_memdup(session->securityEngineID, |
| session->securityEngineIDLen); |
| if (!user->engineID) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| user->engineIDLen = session->securityEngineIDLen; |
| |
| user_just_created = 1; |
| } |
| |
| /* |
| * copy the auth protocol |
| */ |
| if (user->authProtocol == NULL && session->securityAuthProto != NULL) { |
| SNMP_FREE(user->authProtocol); |
| user->authProtocol = |
| snmp_duplicate_objid(session->securityAuthProto, |
| session->securityAuthProtoLen); |
| if (user->authProtocol == NULL) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| user->authProtocolLen = session->securityAuthProtoLen; |
| } |
| |
| /* |
| * copy the priv protocol |
| */ |
| if (user->privProtocol == NULL && session->securityPrivProto != NULL) { |
| SNMP_FREE(user->privProtocol); |
| user->privProtocol = |
| snmp_duplicate_objid(session->securityPrivProto, |
| session->securityPrivProtoLen); |
| if (user->privProtocol == NULL) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| user->privProtocolLen = session->securityPrivProtoLen; |
| } |
| |
| /* |
| * copy in the authentication Key. If not localized, localize it |
| */ |
| if (user->authKey == NULL) { |
| if (session->securityAuthLocalKey != NULL |
| && session->securityAuthLocalKeyLen != 0) { |
| /* already localized key passed in. use it */ |
| SNMP_FREE(user->authKey); |
| user->authKey = netsnmp_memdup(session->securityAuthLocalKey, |
| session->securityAuthLocalKeyLen); |
| if (!user->authKey) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| user->authKeyLen = session->securityAuthLocalKeyLen; |
| } else if (session->securityAuthKey != NULL |
| && session->securityAuthKeyLen != 0) { |
| SNMP_FREE(user->authKey); |
| user->authKey = (u_char *) calloc(1, USM_LENGTH_KU_HASHBLOCK); |
| if (user->authKey == NULL) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| user->authKeyLen = USM_LENGTH_KU_HASHBLOCK; |
| if (generate_kul(user->authProtocol, user->authProtocolLen, |
| session->securityEngineID, |
| session->securityEngineIDLen, |
| session->securityAuthKey, |
| session->securityAuthKeyLen, user->authKey, |
| &user->authKeyLen) != SNMPERR_SUCCESS) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| } else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_AUTHLOCALIZEDKEY))) { |
| size_t buflen = USM_AUTH_KU_LEN; |
| SNMP_FREE(user->authKey); |
| user->authKey = (u_char *)malloc(buflen); /* max length needed */ |
| user->authKeyLen = 0; |
| /* it will be a hex string */ |
| if (!snmp_hex_to_binary(&user->authKey, &buflen, &user->authKeyLen, |
| 0, cp)) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| } |
| } |
| |
| /* |
| * copy in the privacy Key. If not localized, localize it |
| */ |
| if (user->privKey == NULL) { |
| if (session->securityPrivLocalKey != NULL |
| && session->securityPrivLocalKeyLen != 0) { |
| /* already localized key passed in. use it */ |
| SNMP_FREE(user->privKey); |
| user->privKey = netsnmp_memdup(session->securityPrivLocalKey, |
| session->securityPrivLocalKeyLen); |
| if (!user->privKey) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| user->privKeyLen = session->securityPrivLocalKeyLen; |
| } else if (session->securityPrivKey != NULL |
| && session->securityPrivKeyLen != 0) { |
| SNMP_FREE(user->privKey); |
| user->privKey = (u_char *) calloc(1, USM_LENGTH_KU_HASHBLOCK); |
| if (user->privKey == NULL) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| user->privKeyLen = USM_LENGTH_KU_HASHBLOCK; |
| if (generate_kul(user->authProtocol, user->authProtocolLen, |
| session->securityEngineID, |
| session->securityEngineIDLen, |
| session->securityPrivKey, |
| session->securityPrivKeyLen, user->privKey, |
| &user->privKeyLen) != SNMPERR_SUCCESS) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| } else if ((cp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
| NETSNMP_DS_LIB_PRIVLOCALIZEDKEY))) { |
| size_t buflen = USM_PRIV_KU_LEN; |
| SNMP_FREE(user->privKey); |
| user->privKey = (u_char *)malloc(buflen); /* max length needed */ |
| user->privKeyLen = 0; |
| /* it will be a hex string */ |
| if (!snmp_hex_to_binary(&user->privKey, &buflen, &user->privKeyLen, |
| 0, cp)) { |
| usm_free_user(user); |
| return SNMPERR_GENERR; |
| } |
| } |
| } |
| |
| if (user_just_created) { |
| /* |
| * add the user into the database |
| */ |
| user->userStatus = RS_ACTIVE; |
| user->userStorageType = ST_READONLY; |
| usm_add_user(user); |
| } |
| |
| return SNMPERR_SUCCESS; |
| |
| |
| } /* end create_user_from_session() */ |
| |
| /* |
| * Do a "deep free()" of a netsnmp_session. |
| * |
| * CAUTION: SHOULD ONLY BE USED FROM snmp_sess_close() OR SIMILAR. |
| * (hence it is static) |
| */ |
| |
| static void |
| snmp_free_session(netsnmp_session * s) |
| { |
| if (s) { |
| SNMP_FREE(s->localname); |
| SNMP_FREE(s->peername); |
| SNMP_FREE(s->community); |
| SNMP_FREE(s->contextEngineID); |
| SNMP_FREE(s->contextName); |
| SNMP_FREE(s->securityEngineID); |
| SNMP_FREE(s->securityName); |
| SNMP_FREE(s->securityAuthProto); |
| SNMP_FREE(s->securityPrivProto); |
| SNMP_FREE(s->paramName); |
| |
| /* |
| * clear session from any callbacks |
| */ |
| netsnmp_callback_clear_client_arg(s, 0, 0); |
| |
| free((char *) s); |
| } |
| } |
| |
| /* |
| * Close the input session. Frees all data allocated for the session, |
| * dequeues any pending requests, and closes any sockets allocated for |
| * the session. Returns 0 on error, 1 otherwise. |
| */ |
| int |
| snmp_sess_close(void *sessp) |
| { |
| struct session_list *slp = (struct session_list *) sessp; |
| netsnmp_transport *transport; |
| struct snmp_internal_session *isp; |
| netsnmp_session *sesp = NULL; |
| struct snmp_secmod_def *sptr; |
| |
| if (slp == NULL) { |
| return 0; |
| } |
| |
| if (slp->session != NULL && |
| (sptr = find_sec_mod(slp->session->securityModel)) != NULL && |
| sptr->session_close != NULL) { |
| (*sptr->session_close) (slp->session); |
| } |
| |
| isp = slp->internal; |
| slp->internal = 0; |
| |
| if (isp) { |
| netsnmp_request_list *rp, *orp; |
| |
| SNMP_FREE(isp->packet); |
| |
| /* |
| * Free each element in the input request list. |
| */ |
| rp = isp->requests; |
| while (rp) { |
| orp = rp; |
| rp = rp->next_request; |
| if (orp->callback) { |
| orp->callback(NETSNMP_CALLBACK_OP_TIMED_OUT, |
| slp->session, orp->pdu->reqid, |
| orp->pdu, orp->cb_data); |
| } |
| snmp_free_pdu(orp->pdu); |
| free((char *) orp); |
| } |
| |
| free((char *) isp); |
| } |
| |
| transport = slp->transport; |
| slp->transport = 0; |
| |
| if (transport) { |
| transport->f_close(transport); |
| netsnmp_transport_free(transport); |
| } |
| |
| sesp = slp->session; |
| slp->session = 0; |
| |
| /* |
| * The following is necessary to avoid memory leakage when closing AgentX |
| * sessions that may have multiple subsessions. These hang off the main |
| * session at ->subsession, and chain through ->next. |
| */ |
| |
| if (sesp != NULL && sesp->subsession != NULL) { |
| netsnmp_session *subsession = sesp->subsession, *tmpsub; |
| |
| while (subsession != NULL) { |
| DEBUGMSGTL(("snmp_sess_close", |
| "closing session %p, subsession %p\n", sesp, |
| subsession)); |
| tmpsub = subsession->next; |
| snmp_free_session(subsession); |
| subsession = tmpsub; |
| } |
| } |
| |
| snmp_free_session(sesp); |
| free((char *) slp); |
| return 1; |
| } |
| |
| int |
| snmp_close(netsnmp_session * session) |
| { |
| struct session_list *slp = NULL, *oslp = NULL; |
| |
| { /*MTCRITICAL_RESOURCE */ |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| if (Sessions && Sessions->session == session) { /* If first entry */ |
| slp = Sessions; |
| Sessions = slp->next; |
| } else { |
| for (slp = Sessions; slp; slp = slp->next) { |
| if (slp->session == session) { |
| if (oslp) /* if we found entry that points here */ |
| oslp->next = slp->next; /* link around this entry */ |
| break; |
| } |
| oslp = slp; |
| } |
| } |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| } /*END MTCRITICAL_RESOURCE */ |
| if (slp == NULL) { |
| return 0; |
| } |
| return snmp_sess_close((void *) slp); |
| } |
| |
| int |
| snmp_close_sessions(void) |
| { |
| struct session_list *slp; |
| |
| snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| while (Sessions) { |
| slp = Sessions; |
| Sessions = Sessions->next; |
| snmp_sess_close((void *) slp); |
| } |
| snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION); |
| return 1; |
| } |
| |
| static int |
| snmpv3_build_probe_pdu(netsnmp_pdu **pdu) |
| { |
| struct usmUser *user; |
| |
| /* |
| * create the pdu |
| */ |
| if (!pdu) |
| return -1; |
| *pdu = snmp_pdu_create(SNMP_MSG_GET); |
| if (!(*pdu)) |
| return -1; |
| (*pdu)->version = SNMP_VERSION_3; |
| (*pdu)->securityName = strdup(""); |
| (*pdu)->securityNameLen = strlen((*pdu)->securityName); |
| (*pdu)->securityLevel = SNMP_SEC_LEVEL_NOAUTH; |
| (*pdu)->securityModel = SNMP_SEC_MODEL_USM; |
| |
| /* |
| * create the empty user |
| */ |
| user = usm_get_user(NULL, 0, (*pdu)->securityName); |
| if (user == NULL) { |
| user = (struct usmUser *) calloc(1, sizeof(struct usmUser)); |
| if (user == NULL) { |
| snmp_free_pdu(*pdu); |
| *pdu = (netsnmp_pdu *) NULL; |
| return -1; |
| } |
| user->name = strdup((*pdu)->securityName); |
| user->secName = strdup((*pdu)->securityName); |
| user->authProtocolLen = sizeof(usmNoAuthProtocol) / sizeof(oid); |
| user->authProtocol = |
| snmp_duplicate_objid(usmNoAuthProtocol, user->authProtocolLen); |
| user->privProtocolLen = sizeof(usmNoPrivProtocol) / sizeof(oid); |
| user->privProtocol = |
| snmp_duplicate_objid(usmNoPrivProtocol, user->privProtocolLen); |
| usm_add_user(user); |
| } |
| return 0; |
| } |
| |
| static void |
| snmpv3_calc_msg_flags(int sec_level, int msg_command, u_char * flags) |
| { |
| *flags = 0; |
| if (sec_level == SNMP_SEC_LEVEL_AUTHNOPRIV) |
| *flags = SNMP_MSG_FLAG_AUTH_BIT; |
| else if (sec_level == SNMP_SEC_LEVEL_AUTHPRIV) |
| *flags = SNMP_MSG_FLAG_AUTH_BIT | SNMP_MSG_FLAG_PRIV_BIT; |
| |
| if (SNMP_CMD_CONFIRMED(msg_command)) |
| *flags |= SNMP_MSG_FLAG_RPRT_BIT; |
| |
| return; |
| } |
| |
| static int |
| snmpv3_verify_msg(netsnmp_request_list *rp, netsnmp_pdu *pdu) |
| { |
| netsnmp_pdu *rpdu; |
| |
| if (!rp || !rp->pdu || !pdu) |
| return 0; |
| /* |
| * Reports don't have to match anything according to the spec |
| */ |
| if (pdu->command == SNMP_MSG_REPORT) |
| return 1; |
| rpdu = rp->pdu; |
| if (rp->request_id != pdu->reqid || rpdu->reqid != pdu->reqid) |
| return 0; |
| if (rpdu->version != pdu->version) |
| return 0; |
| if (rpdu->securityModel != pdu->securityModel) |
| return 0; |
| if (rpdu->securityLevel != pdu->securityLevel) |
| return 0; |
| |
| if (rpdu->contextEngineIDLen != pdu->contextEngineIDLen || |
| memcmp(rpdu->contextEngineID, pdu->contextEngineID, |
| pdu->contextEngineIDLen)) |
| return 0; |
| if (rpdu->contextNameLen != pdu->contextNameLen || |
| memcmp(rpdu->contextName, pdu->contextName, pdu->contextNameLen)) |
| return 0; |
| if (rpdu->securityEngineIDLen != pdu->securityEngineIDLen || |
| memcmp(rpdu->securityEngineID, pdu->securityEngineID, |
| pdu->securityEngineIDLen)) |
| return 0; |
| if (rpdu->securityNameLen != pdu->securityNameLen || |
| memcmp(rpdu->securityName, pdu->securityName, |
| pdu->securityNameLen)) |
| return 0; |
| return 1; |
| } |
| |
| |
| /* |
| * SNMPv3 |
| * * Takes a session and a pdu and serializes the ASN PDU into the area |
| * * pointed to by packet. out_length is the size of the data area available. |
| * * Returns the length of the completed packet in out_length. If any errors |
| * * occur, -1 is returned. If all goes well, 0 is returned. |
| */ |
| static int |
| snmpv3_build(u_char ** pkt, size_t * pkt_len, size_t * offset, |
| netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| int ret; |
| |
| session->s_snmp_errno = 0; |
| session->s_errno = 0; |
| |
| /* |
| * do validation for PDU types |
| */ |
| switch (pdu->command) { |
| case SNMP_MSG_RESPONSE: |
| case SNMP_MSG_TRAP2: |
| case SNMP_MSG_REPORT: |
| netsnmp_assert(0 == (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE)); |
| /* |
| * Fallthrough |
| */ |
| case SNMP_MSG_GET: |
| case SNMP_MSG_GETNEXT: |
| case SNMP_MSG_SET: |
| case SNMP_MSG_INFORM: |
| if (pdu->errstat == SNMP_DEFAULT_ERRSTAT) |
| pdu->errstat = 0; |
| if (pdu->errindex == SNMP_DEFAULT_ERRINDEX) |
| pdu->errindex = 0; |
| break; |
| |
| case SNMP_MSG_GETBULK: |
| if (pdu->max_repetitions < 0) { |
| session->s_snmp_errno = SNMPERR_BAD_REPETITIONS; |
| return -1; |
| } |
| if (pdu->non_repeaters < 0) { |
| session->s_snmp_errno = SNMPERR_BAD_REPEATERS; |
| return -1; |
| } |
| break; |
| |
| case SNMP_MSG_TRAP: |
| session->s_snmp_errno = SNMPERR_V1_IN_V2; |
| return -1; |
| |
| default: |
| session->s_snmp_errno = SNMPERR_UNKNOWN_PDU; |
| return -1; |
| } |
| |
| /* Do we need to set the session security engineid? */ |
| if (pdu->securityEngineIDLen == 0) { |
| if (session->securityEngineIDLen) { |
| snmpv3_clone_engineID(&pdu->securityEngineID, |
| &pdu->securityEngineIDLen, |
| session->securityEngineID, |
| session->securityEngineIDLen); |
| } |
| } |
| |
| /* Do we need to set the session context engineid? */ |
| if (pdu->contextEngineIDLen == 0) { |
| if (session->contextEngineIDLen) { |
| snmpv3_clone_engineID(&pdu->contextEngineID, |
| &pdu->contextEngineIDLen, |
| session->contextEngineID, |
| session->contextEngineIDLen); |
| } else if (pdu->securityEngineIDLen) { |
| snmpv3_clone_engineID(&pdu->contextEngineID, |
| &pdu->contextEngineIDLen, |
| pdu->securityEngineID, |
| pdu->securityEngineIDLen); |
| } |
| } |
| |
| if (pdu->contextName == NULL) { |
| if (!session->contextName) { |
| session->s_snmp_errno = SNMPERR_BAD_CONTEXT; |
| return -1; |
| } |
| pdu->contextName = strdup(session->contextName); |
| if (pdu->contextName == NULL) { |
| session->s_snmp_errno = SNMPERR_GENERR; |
| return -1; |
| } |
| pdu->contextNameLen = session->contextNameLen; |
| } |
| if (pdu->securityModel == SNMP_DEFAULT_SECMODEL) { |
| pdu->securityModel = session->securityModel; |
| if (pdu->securityModel == SNMP_DEFAULT_SECMODEL) { |
| pdu->securityModel = SNMP_SEC_MODEL_USM; |
| } |
| } |
| if (pdu->securityNameLen == 0 && pdu->securityName == 0) { |
| if (session->securityNameLen == 0) { |
| session->s_snmp_errno = SNMPERR_BAD_SEC_NAME; |
| return -1; |
| } |
| pdu->securityName = strdup(session->securityName); |
| if (pdu->securityName == NULL) { |
| session->s_snmp_errno = SNMPERR_GENERR; |
| return -1; |
| } |
| pdu->securityNameLen = session->securityNameLen; |
| } |
| if (pdu->securityLevel == 0) { |
| if (session->securityLevel == 0) { |
| session->s_snmp_errno = SNMPERR_BAD_SEC_LEVEL; |
| return -1; |
| } |
| pdu->securityLevel = session->securityLevel; |
| } |
| DEBUGMSGTL(("snmp_build", |
| "Building SNMPv3 message (secName:\"%s\", secLevel:%s)...\n", |
| ((session->securityName) ? (char *) session->securityName : |
| ((pdu->securityName) ? (char *) pdu->securityName : |
| "ERROR: undefined")), secLevelName[pdu->securityLevel])); |
| |
| DEBUGDUMPSECTION("send", "SNMPv3 Message"); |
| #ifdef NETSNMP_USE_REVERSE_ASNENCODING |
| if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_ENCODE)) { |
| ret = snmpv3_packet_realloc_rbuild(pkt, pkt_len, offset, |
| session, pdu, NULL, 0); |
| } else { |
| #endif |
| ret = snmpv3_packet_build(session, pdu, *pkt, pkt_len, NULL, 0); |
| #ifdef NETSNMP_USE_REVERSE_ASNENCODING |
| } |
| #endif |
| DEBUGINDENTLESS(); |
| if (-1 != ret) { |
| session->s_snmp_errno = ret; |
| } |
| |
| return ret; |
| |
| } /* end snmpv3_build() */ |
| |
| |
| |
| |
| static u_char * |
| snmpv3_header_build(netsnmp_session * session, netsnmp_pdu *pdu, |
| u_char * packet, size_t * out_length, |
| size_t length, u_char ** msg_hdr_e) |
| { |
| u_char *global_hdr, *global_hdr_e; |
| u_char *cp; |
| u_char msg_flags; |
| long max_size; |
| long sec_model; |
| u_char *pb, *pb0e; |
| |
| /* |
| * Save current location and build SEQUENCE tag and length placeholder |
| * * for SNMP message sequence (actual length inserted later) |
| */ |
| cp = asn_build_sequence(packet, out_length, |
| (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), |
| length); |
| if (cp == NULL) |
| return NULL; |
| if (msg_hdr_e != NULL) |
| *msg_hdr_e = cp; |
| pb0e = cp; |
| |
| |
| /* |
| * store the version field - msgVersion |
| */ |
| DEBUGDUMPHEADER("send", "SNMP Version Number"); |
| cp = asn_build_int(cp, out_length, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_INTEGER), (long *) &pdu->version, |
| sizeof(pdu->version)); |
| DEBUGINDENTLESS(); |
| if (cp == NULL) |
| return NULL; |
| |
| global_hdr = cp; |
| /* |
| * msgGlobalData HeaderData |
| */ |
| DEBUGDUMPSECTION("send", "msgGlobalData"); |
| cp = asn_build_sequence(cp, out_length, |
| (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), 0); |
| if (cp == NULL) |
| return NULL; |
| global_hdr_e = cp; |
| |
| |
| /* |
| * msgID |
| */ |
| DEBUGDUMPHEADER("send", "msgID"); |
| cp = asn_build_int(cp, out_length, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_INTEGER), &pdu->msgid, |
| sizeof(pdu->msgid)); |
| DEBUGINDENTLESS(); |
| if (cp == NULL) |
| return NULL; |
| |
| /* |
| * msgMaxSize |
| */ |
| max_size = session->rcvMsgMaxSize; |
| DEBUGDUMPHEADER("send", "msgMaxSize"); |
| cp = asn_build_int(cp, out_length, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_INTEGER), &max_size, |
| sizeof(max_size)); |
| DEBUGINDENTLESS(); |
| if (cp == NULL) |
| return NULL; |
| |
| /* |
| * msgFlags |
| */ |
| snmpv3_calc_msg_flags(pdu->securityLevel, pdu->command, &msg_flags); |
| DEBUGDUMPHEADER("send", "msgFlags"); |
| cp = asn_build_string(cp, out_length, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_OCTET_STR), &msg_flags, |
| sizeof(msg_flags)); |
| DEBUGINDENTLESS(); |
| if (cp == NULL) |
| return NULL; |
| |
| /* |
| * msgSecurityModel |
| */ |
| sec_model = pdu->securityModel; |
| DEBUGDUMPHEADER("send", "msgSecurityModel"); |
| cp = asn_build_int(cp, out_length, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_INTEGER), &sec_model, |
| sizeof(sec_model)); |
| DEBUGINDENTADD(-4); /* return from global data indent */ |
| if (cp == NULL) |
| return NULL; |
| |
| |
| /* |
| * insert actual length of globalData |
| */ |
| pb = asn_build_sequence(global_hdr, out_length, |
| (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), |
| cp - global_hdr_e); |
| if (pb == NULL) |
| return NULL; |
| |
| |
| /* |
| * insert the actual length of the entire packet |
| */ |
| pb = asn_build_sequence(packet, out_length, |
| (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), |
| length + (cp - pb0e)); |
| if (pb == NULL) |
| return NULL; |
| |
| return cp; |
| |
| } /* end snmpv3_header_build() */ |
| |
| #ifdef NETSNMP_USE_REVERSE_ASNENCODING |
| |
| int |
| snmpv3_header_realloc_rbuild(u_char ** pkt, size_t * pkt_len, |
| size_t * offset, netsnmp_session * session, |
| netsnmp_pdu *pdu) |
| { |
| size_t start_offset = *offset; |
| u_char msg_flags; |
| long max_size, sec_model; |
| int rc = 0; |
| |
| /* |
| * msgSecurityModel. |
| */ |
| sec_model = pdu->securityModel; |
| DEBUGDUMPHEADER("send", "msgSecurityModel"); |
| rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_INTEGER), &sec_model, |
| sizeof(sec_model)); |
| DEBUGINDENTLESS(); |
| if (rc == 0) { |
| return 0; |
| } |
| |
| /* |
| * msgFlags. |
| */ |
| snmpv3_calc_msg_flags(pdu->securityLevel, pdu->command, &msg_flags); |
| DEBUGDUMPHEADER("send", "msgFlags"); |
| rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
| | ASN_OCTET_STR), &msg_flags, |
| sizeof(msg_flags)); |
| DEBUGINDENTLESS(); |
| if (rc == 0) { |
| return 0; |
| } |
| |
| /* |
| * msgMaxSize. |
| */ |
| max_size = session->rcvMsgMaxSize; |
| DEBUGDUMPHEADER("send", "msgMaxSize"); |
| rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_INTEGER), &max_size, |
| sizeof(max_size)); |
| DEBUGINDENTLESS(); |
| if (rc == 0) { |
| return 0; |
| } |
| |
| /* |
| * msgID. |
| */ |
| DEBUGDUMPHEADER("send", "msgID"); |
| rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_INTEGER), &pdu->msgid, |
| sizeof(pdu->msgid)); |
| DEBUGINDENTLESS(); |
| if (rc == 0) { |
| return 0; |
| } |
| |
| /* |
| * Global data sequence. |
| */ |
| rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_SEQUENCE | |
| ASN_CONSTRUCTOR), |
| *offset - start_offset); |
| if (rc == 0) { |
| return 0; |
| } |
| |
| /* |
| * Store the version field - msgVersion. |
| */ |
| DEBUGDUMPHEADER("send", "SNMP Version Number"); |
| rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_INTEGER), |
| (long *) &pdu->version, |
| sizeof(pdu->version)); |
| DEBUGINDENTLESS(); |
| return rc; |
| } /* end snmpv3_header_realloc_rbuild() */ |
| #endif /* NETSNMP_USE_REVERSE_ASNENCODING */ |
| |
| static u_char * |
| snmpv3_scopedPDU_header_build(netsnmp_pdu *pdu, |
| u_char * packet, size_t * out_length, |
| u_char ** spdu_e) |
| { |
| u_char *scopedPdu, *pb; |
| |
| pb = scopedPdu = packet; |
| pb = asn_build_sequence(pb, out_length, |
| (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), 0); |
| if (pb == NULL) |
| return NULL; |
| if (spdu_e) |
| *spdu_e = pb; |
| |
| DEBUGDUMPHEADER("send", "contextEngineID"); |
| pb = asn_build_string(pb, out_length, |
| (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), |
| pdu->contextEngineID, pdu->contextEngineIDLen); |
| DEBUGINDENTLESS(); |
| if (pb == NULL) |
| return NULL; |
| |
| DEBUGDUMPHEADER("send", "contextName"); |
| pb = asn_build_string(pb, out_length, |
| (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), |
| (u_char *) pdu->contextName, |
| pdu->contextNameLen); |
| DEBUGINDENTLESS(); |
| if (pb == NULL) |
| return NULL; |
| |
| return pb; |
| |
| } /* end snmpv3_scopedPDU_header_build() */ |
| |
| |
| #ifdef NETSNMP_USE_REVERSE_ASNENCODING |
| int |
| snmpv3_scopedPDU_header_realloc_rbuild(u_char ** pkt, size_t * pkt_len, |
| size_t * offset, netsnmp_pdu *pdu, |
| size_t body_len) |
| { |
| size_t start_offset = *offset; |
| int rc = 0; |
| |
| /* |
| * contextName. |
| */ |
| DEBUGDUMPHEADER("send", "contextName"); |
| rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
| | ASN_OCTET_STR), |
| (u_char *) pdu->contextName, |
| pdu->contextNameLen); |
| DEBUGINDENTLESS(); |
| if (rc == 0) { |
| return 0; |
| } |
| |
| /* |
| * contextEngineID. |
| */ |
| DEBUGDUMPHEADER("send", "contextEngineID"); |
| rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
| | ASN_OCTET_STR), |
| pdu->contextEngineID, |
| pdu->contextEngineIDLen); |
| DEBUGINDENTLESS(); |
| if (rc == 0) { |
| return 0; |
| } |
| |
| rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_SEQUENCE | |
| ASN_CONSTRUCTOR), |
| *offset - start_offset + body_len); |
| |
| return rc; |
| } /* end snmpv3_scopedPDU_header_realloc_rbuild() */ |
| #endif /* NETSNMP_USE_REVERSE_ASNENCODING */ |
| |
| #ifdef NETSNMP_USE_REVERSE_ASNENCODING |
| /* |
| * returns 0 if success, -1 if fail, not 0 if SM build failure |
| */ |
| int |
| snmpv3_packet_realloc_rbuild(u_char ** pkt, size_t * pkt_len, |
| size_t * offset, netsnmp_session * session, |
| netsnmp_pdu *pdu, u_char * pdu_data, |
| size_t pdu_data_len) |
| { |
| u_char *scoped_pdu, *hdrbuf = NULL, *hdr = NULL; |
| size_t hdrbuf_len = SNMP_MAX_MSG_V3_HDRS, hdr_offset = |
| 0, spdu_offset = 0; |
| size_t body_end_offset = *offset, body_len = 0; |
| struct snmp_secmod_def *sptr = NULL; |
| int rc = 0; |
| |
| /* |
| * Build a scopedPDU structure into the packet buffer. |
| */ |
| DEBUGPRINTPDUTYPE("send", pdu->command); |
| if (pdu_data) { |
| while ((*pkt_len - *offset) < pdu_data_len) { |
| if (!asn_realloc(pkt, pkt_len)) { |
| return -1; |
| } |
| } |
| |
| *offset += pdu_data_len; |
| memcpy(*pkt + *pkt_len - *offset, pdu_data, pdu_data_len); |
| } else { |
| rc = snmp_pdu_realloc_rbuild(pkt, pkt_len, offset, pdu); |
| if (rc == 0) { |
| return -1; |
| } |
| } |
| body_len = *offset - body_end_offset; |
| |
| DEBUGDUMPSECTION("send", "ScopedPdu"); |
| rc = snmpv3_scopedPDU_header_realloc_rbuild(pkt, pkt_len, offset, |
| pdu, body_len); |
| if (rc == 0) { |
| return -1; |
| } |
| spdu_offset = *offset; |
| DEBUGINDENTADD(-4); /* Return from Scoped PDU. */ |
| |
| if ((hdrbuf = (u_char *) malloc(hdrbuf_len)) == NULL) { |
| return -1; |
| } |
| |
| rc = snmpv3_header_realloc_rbuild(&hdrbuf, &hdrbuf_len, &hdr_offset, |
| session, pdu); |
| if (rc == 0) { |
| SNMP_FREE(hdrbuf); |
| return -1; |
| } |
| hdr = hdrbuf + hdrbuf_len - hdr_offset; |
| scoped_pdu = *pkt + *pkt_len - spdu_offset; |
| |
| /* |
| * Call the security module to possibly encrypt and authenticate the |
| * message---the entire message to transmitted on the wire is returned. |
| */ |
| |
| sptr = find_sec_mod(pdu->securityModel); |
| DEBUGDUMPSECTION("send", "SM msgSecurityParameters"); |
| if (sptr && sptr->encode_reverse) { |
| struct snmp_secmod_outgoing_params parms; |
| |
| parms.msgProcModel = pdu->msgParseModel; |
| parms.globalData = hdr; |
| parms.globalDataLen = hdr_offset; |
| parms.maxMsgSize = SNMP_MAX_MSG_SIZE; |
| parms.secModel = pdu->securityModel; |
| parms.secEngineID = pdu->securityEngineID; |
| parms.secEngineIDLen = pdu->securityEngineIDLen; |
| parms.secName = pdu->securityName; |
| parms.secNameLen = pdu->securityNameLen; |
| parms.secLevel = pdu->securityLevel; |
| parms.scopedPdu = scoped_pdu; |
| parms.scopedPduLen = spdu_offset; |
| parms.secStateRef = pdu->securityStateRef; |
| parms.wholeMsg = pkt; |
| parms.wholeMsgLen = pkt_len; |
| parms.wholeMsgOffset = offset; |
| parms.session = session; |
| parms.pdu = pdu; |
| |
| rc = (*sptr->encode_reverse) (&parms); |
| } else { |
| if (!sptr) { |
| snmp_log(LOG_ERR, |
| "no such security service available: %d\n", |
| pdu->securityModel); |
| } else if (!sptr->encode_reverse) { |
| snmp_log(LOG_ERR, |
| "security service %d doesn't support reverse encoding.\n", |
| pdu->securityModel); |
| } |
| rc = -1; |
| } |
| |
| DEBUGINDENTLESS(); |
| SNMP_FREE(hdrbuf); |
| return rc; |
| } /* end snmpv3_packet_realloc_rbuild() */ |
| #endif /* NETSNMP_USE_REVERSE_ASNENCODING */ |
| |
| /* |
| * returns 0 if success, -1 if fail, not 0 if SM build failure |
| */ |
| int |
| snmpv3_packet_build(netsnmp_session * session, netsnmp_pdu *pdu, |
| u_char * packet, size_t * out_length, |
| u_char * pdu_data, size_t pdu_data_len) |
| { |
| u_char *global_data, *sec_params, *spdu_hdr_e; |
| size_t global_data_len, sec_params_len; |
| u_char spdu_buf[SNMP_MAX_MSG_SIZE]; |
| size_t spdu_buf_len, spdu_len; |
| u_char *cp; |
| int result; |
| struct snmp_secmod_def *sptr; |
| |
| global_data = packet; |
| |
| /* |
| * build the headers for the packet, returned addr = start of secParams |
| */ |
| sec_params = snmpv3_header_build(session, pdu, global_data, |
| out_length, 0, NULL); |
| if (sec_params == NULL) |
| return -1; |
| global_data_len = sec_params - global_data; |
| sec_params_len = *out_length; /* length left in packet buf for sec_params */ |
| |
| |
| /* |
| * build a scopedPDU structure into spdu_buf |
| */ |
| spdu_buf_len = SNMP_MAX_MSG_SIZE; |
| DEBUGDUMPSECTION("send", "ScopedPdu"); |
| cp = snmpv3_scopedPDU_header_build(pdu, spdu_buf, &spdu_buf_len, |
| &spdu_hdr_e); |
| if (cp == NULL) |
| return -1; |
| |
| /* |
| * build the PDU structure onto the end of spdu_buf |
| */ |
| DEBUGPRINTPDUTYPE("send", ((pdu_data) ? *pdu_data : 0x00)); |
| if (pdu_data) { |
| memcpy(cp, pdu_data, pdu_data_len); |
| cp += pdu_data_len; |
| } else { |
| cp = snmp_pdu_build(pdu, cp, &spdu_buf_len); |
| if (cp == NULL) |
| return -1; |
| } |
| DEBUGINDENTADD(-4); /* return from Scoped PDU */ |
| |
| /* |
| * re-encode the actual ASN.1 length of the scopedPdu |
| */ |
| spdu_len = cp - spdu_hdr_e; /* length of scopedPdu minus ASN.1 headers */ |
| spdu_buf_len = SNMP_MAX_MSG_SIZE; |
| if (asn_build_sequence(spdu_buf, &spdu_buf_len, |
| (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), |
| spdu_len) == NULL) |
| return -1; |
| spdu_len = cp - spdu_buf; /* the length of the entire scopedPdu */ |
| |
| |
| /* |
| * call the security module to possibly encrypt and authenticate the |
| * message - the entire message to transmitted on the wire is returned |
| */ |
| cp = NULL; |
| *out_length = SNMP_MAX_MSG_SIZE; |
| DEBUGDUMPSECTION("send", "SM msgSecurityParameters"); |
| sptr = find_sec_mod(pdu->securityModel); |
| if (sptr && sptr->encode_forward) { |
| struct snmp_secmod_outgoing_params parms; |
| parms.msgProcModel = pdu->msgParseModel; |
| parms.globalData = global_data; |
| parms.globalDataLen = global_data_len; |
| parms.maxMsgSize = SNMP_MAX_MSG_SIZE; |
| parms.secModel = pdu->securityModel; |
| parms.secEngineID = pdu->securityEngineID; |
| parms.secEngineIDLen = pdu->securityEngineIDLen; |
| parms.secName = pdu->securityName; |
| parms.secNameLen = pdu->securityNameLen; |
| parms.secLevel = pdu->securityLevel; |
| parms.scopedPdu = spdu_buf; |
| parms.scopedPduLen = spdu_len; |
| parms.secStateRef = pdu->securityStateRef; |
| parms.secParams = sec_params; |
| parms.secParamsLen = &sec_params_len; |
| parms.wholeMsg = &cp; |
| parms.wholeMsgLen = out_length; |
| parms.session = session; |
| parms.pdu = pdu; |
| result = (*sptr->encode_forward) (&parms); |
| } else { |
| if (!sptr) { |
| snmp_log(LOG_ERR, "no such security service available: %d\n", |
| pdu->securityModel); |
| } else if (!sptr->encode_forward) { |
| snmp_log(LOG_ERR, |
| "security service %d doesn't support forward out encoding.\n", |
| pdu->securityModel); |
| } |
| result = -1; |
| } |
| DEBUGINDENTLESS(); |
| return result; |
| |
| } /* end snmpv3_packet_build() */ |
| |
| |
| /* |
| * Takes a session and a pdu and serializes the ASN PDU into the area |
| * pointed to by *pkt. *pkt_len is the size of the data area available. |
| * Returns the length of the completed packet in *offset. If any errors |
| * occur, -1 is returned. If all goes well, 0 is returned. |
| */ |
| |
| static int |
| _snmp_build(u_char ** pkt, size_t * pkt_len, size_t * offset, |
| netsnmp_session * session, netsnmp_pdu *pdu) |
| { |
| #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
| u_char *h0e = 0; |
| size_t start_offset = *offset; |
| long version; |
| int rc = 0; |
| #endif /* support for community based SNMP */ |
| |
| u_char *cp; |
| size_t length; |
| |
| session->s_snmp_errno = 0; |
| session->s_errno = 0; |
| |
| if (pdu->version == SNMP_VERSION_3) { |
| return snmpv3_build(pkt, pkt_len, offset, session, pdu); |
| } |
| |
| switch (pdu->command) { |
| case SNMP_MSG_RESPONSE: |
| netsnmp_assert(0 == (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE)); |
| /* |
| * Fallthrough |
| */ |
| case SNMP_MSG_GET: |
| case SNMP_MSG_GETNEXT: |
| case SNMP_MSG_SET: |
| /* |
| * all versions support these PDU types |
| */ |
| /* |
| * initialize defaulted PDU fields |
| */ |
| |
| if (pdu->errstat == SNMP_DEFAULT_ERRSTAT) |
| pdu->errstat = 0; |
| if (pdu->errindex == SNMP_DEFAULT_ERRINDEX) |
| pdu->errindex = 0; |
| break; |
| |
| case SNMP_MSG_TRAP2: |
| netsnmp_assert(0 == (pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE)); |
| /* |
| * Fallthrough |
| */ |
| case SNMP_MSG_INFORM: |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| /* |
| * not supported in SNMPv1 and SNMPsec |
| */ |
| if (pdu->version == SNMP_VERSION_1) { |
| session->s_snmp_errno = SNMPERR_V2_IN_V1; |
| return -1; |
| } |
| #endif |
| if (pdu->errstat == SNMP_DEFAULT_ERRSTAT) |
| pdu->errstat = 0; |
| if (pdu->errindex == SNMP_DEFAULT_ERRINDEX) |
| pdu->errindex = 0; |
| break; |
| |
| case SNMP_MSG_GETBULK: |
| /* |
| * not supported in SNMPv1 and SNMPsec |
| */ |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| if (pdu->version == SNMP_VERSION_1) { |
| session->s_snmp_errno = SNMPERR_V2_IN_V1; |
| return -1; |
| } |
| #endif |
| if (pdu->max_repetitions < 0) { |
| session->s_snmp_errno = SNMPERR_BAD_REPETITIONS; |
| return -1; |
| } |
| if (pdu->non_repeaters < 0) { |
| session->s_snmp_errno = SNMPERR_BAD_REPEATERS; |
| return -1; |
| } |
| break; |
| |
| case SNMP_MSG_TRAP: |
| /* |
| * *only* supported in SNMPv1 and SNMPsec |
| */ |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| if (pdu->version != SNMP_VERSION_1) { |
| session->s_snmp_errno = SNMPERR_V1_IN_V2; |
| return -1; |
| } |
| #endif |
| /* |
| * initialize defaulted Trap PDU fields |
| */ |
| pdu->reqid = 1; /* give a bogus non-error reqid for traps */ |
| if (pdu->enterprise_length == SNMP_DEFAULT_ENTERPRISE_LENGTH) { |
| pdu->enterprise = (oid *) malloc(sizeof(DEFAULT_ENTERPRISE)); |
| if (pdu->enterprise == NULL) { |
| session->s_snmp_errno = SNMPERR_MALLOC; |
| return -1; |
| } |
| memmove(pdu->enterprise, DEFAULT_ENTERPRISE, |
| sizeof(DEFAULT_ENTERPRISE)); |
| pdu->enterprise_length = |
| sizeof(DEFAULT_ENTERPRISE) / sizeof(oid); |
| } |
| if (pdu->time == SNMP_DEFAULT_TIME) |
| pdu->time = DEFAULT_TIME; |
| /* |
| * don't expect a response |
| */ |
| pdu->flags &= (~UCD_MSG_FLAG_EXPECT_RESPONSE); |
| break; |
| |
| case SNMP_MSG_REPORT: /* SNMPv3 only */ |
| default: |
| session->s_snmp_errno = SNMPERR_UNKNOWN_PDU; |
| return -1; |
| } |
| |
| /* |
| * save length |
| */ |
| length = *pkt_len; |
| |
| /* |
| * setup administrative fields based on version |
| */ |
| /* |
| * build the message wrapper and all the administrative fields |
| * upto the PDU sequence |
| * (note that actual length of message will be inserted later) |
| */ |
| switch (pdu->version) { |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| case SNMP_VERSION_1: |
| #endif |
| #ifndef NETSNMP_DISABLE_SNMPV2C |
| case SNMP_VERSION_2c: |
| #endif |
| #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) |
| #ifdef NETSNMP_NO_ZEROLENGTH_COMMUNITY |
| if (pdu->community_len == 0) { |
| if (session->community_len == 0) { |
| session->s_snmp_errno = SNMPERR_BAD_COMMUNITY; |
| return -1; |
| } |
| pdu->community = (u_char *) malloc(session->community_len); |
| if (pdu->community == NULL) { |
| session->s_snmp_errno = SNMPERR_MALLOC; |
| return -1; |
| } |
| memmove(pdu->community, |
| session->community, session->community_len); |
| pdu->community_len = session->community_len; |
| } |
| #else /* !NETSNMP_NO_ZEROLENGTH_COMMUNITY */ |
| if (pdu->community_len == 0 && pdu->command != SNMP_MSG_RESPONSE) { |
| /* |
| * copy session community exactly to pdu community |
| */ |
| if (0 == session->community_len) { |
| SNMP_FREE(pdu->community); |
| pdu->community = NULL; |
| } else if (pdu->community_len == session->community_len) { |
| memmove(pdu->community, |
| session->community, session->community_len); |
| } else { |
| SNMP_FREE(pdu->community); |
| pdu->community = (u_char *) malloc(session->community_len); |
| if (pdu->community == NULL) { |
| session->s_snmp_errno = SNMPERR_MALLOC; |
| return -1; |
| } |
| memmove(pdu->community, |
| session->community, session->community_len); |
| } |
| pdu->community_len = session->community_len; |
| } |
| #endif /* !NETSNMP_NO_ZEROLENGTH_COMMUNITY */ |
| |
| DEBUGMSGTL(("snmp_send", "Building SNMPv%d message...\n", |
| (1 + pdu->version))); |
| #ifdef NETSNMP_USE_REVERSE_ASNENCODING |
| if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_REVERSE_ENCODE)) { |
| DEBUGPRINTPDUTYPE("send", pdu->command); |
| rc = snmp_pdu_realloc_rbuild(pkt, pkt_len, offset, pdu); |
| if (rc == 0) { |
| return -1; |
| } |
| |
| DEBUGDUMPHEADER("send", "Community String"); |
| rc = asn_realloc_rbuild_string(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_UNIVERSAL | |
| ASN_PRIMITIVE | |
| ASN_OCTET_STR), |
| pdu->community, |
| pdu->community_len); |
| DEBUGINDENTLESS(); |
| if (rc == 0) { |
| return -1; |
| } |
| |
| |
| /* |
| * Store the version field. |
| */ |
| DEBUGDUMPHEADER("send", "SNMP Version Number"); |
| |
| version = pdu->version; |
| rc = asn_realloc_rbuild_int(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_UNIVERSAL | |
| ASN_PRIMITIVE | |
| ASN_INTEGER), |
| (long *) &version, |
| sizeof(version)); |
| DEBUGINDENTLESS(); |
| if (rc == 0) { |
| return -1; |
| } |
| |
| /* |
| * Build the final sequence. |
| */ |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| if (pdu->version == SNMP_VERSION_1) { |
| DEBUGDUMPSECTION("send", "SNMPv1 Message"); |
| } else { |
| #endif |
| DEBUGDUMPSECTION("send", "SNMPv2c Message"); |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| } |
| #endif |
| rc = asn_realloc_rbuild_sequence(pkt, pkt_len, offset, 1, |
| (u_char) (ASN_SEQUENCE | |
| ASN_CONSTRUCTOR), |
| *offset - start_offset); |
| DEBUGINDENTLESS(); |
| |
| if (rc == 0) { |
| return -1; |
| } |
| return 0; |
| } else { |
| |
| #endif /* NETSNMP_USE_REVERSE_ASNENCODING */ |
| /* |
| * Save current location and build SEQUENCE tag and length |
| * placeholder for SNMP message sequence |
| * (actual length will be inserted later) |
| */ |
| cp = asn_build_sequence(*pkt, pkt_len, |
| (u_char) (ASN_SEQUENCE | |
| ASN_CONSTRUCTOR), 0); |
| if (cp == NULL) { |
| return -1; |
| } |
| h0e = cp; |
| |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| if (pdu->version == SNMP_VERSION_1) { |
| DEBUGDUMPSECTION("send", "SNMPv1 Message"); |
| } else { |
| #endif |
| DEBUGDUMPSECTION("send", "SNMPv2c Message"); |
| #ifndef NETSNMP_DISABLE_SNMPV1 |
| } |
| #endif |
| |
| /* |
| * store the version field |
| */ |
| DEBUGDUMPHEADER("send", "SNMP Version Number"); |
| |
| version = pdu->version; |
| cp = asn_build_int(cp, pkt_len, |
| (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | |
| ASN_INTEGER), (long *) &version, |
| sizeof |