blob: 191debf09a3dcf9ef0bd7b2543f14ef534a8d9b1 [file] [log] [blame]
/* 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 <net-snmp/net-snmp-features.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
# include <sys/time.h>
# 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_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>
#include <net-snmp/library/large_fd_set.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>
netsnmp_feature_child_of(statistics, libnetsnmp)
netsnmp_feature_child_of(snmp_api, libnetsnmp)
netsnmp_feature_child_of(oid_is_subtree, snmp_api)
netsnmp_feature_child_of(snmpv3_probe_contextEngineID_rfc5343, snmp_api)
#if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL)
extern void netsnmp_certs_init(void);
extern void netsnmp_certs_shutdown(void);
#endif
static void _init_snmp(void);
static int _snmp_store_needed = 0;
#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 ONE_SEC
#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;
};
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 */
"Context probe", /* SNMPERR_JUST_A_CONTEXT_PROBE */
"Configuration data found but the transport can't be configured", /* SNMPERR_TRANSPORT_NO_CONFIG */
"Transport configuration failed", /* SNMPERR_TRANSPORT_CONFIG_ERROR */
};
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(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);
NETSNMP_IMPORT
void snmp_synch_reset(netsnmp_session * notused);
NETSNMP_IMPORT
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_GETBULK:
return "GETBULK";
#ifndef NETSNMP_NO_WRITE_SUPPORT
case SNMP_MSG_SET:
return "SET";
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
case SNMP_MSG_RESPONSE:
return "RESPONSE";
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
*/
#ifdef SVR4
srand48(tv.tv_sec ^ tv.tv_usec);
tmpReqid = lrand48();
tmpMsgid = lrand48();
#else
srandom((unsigned)(tv.tv_sec ^ tv.tv_usec));
tmpReqid = random();
tmpMsgid = random();
#endif
/*
* don't allow zero value to repeat init
*/
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", "dtlsudp", ":10161");
netsnmp_register_default_target("snmp", "tlstcp", ":10161");
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", "dtlsudp", ":10162");
netsnmp_register_default_target("snmptrap", "tlstcp", ":10162");
netsnmp_register_default_target("snmptrap", "ipx", "/36880");
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_HEX_OUTPUT_LENGTH, 16);
netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_RETRIES,
DEFAULT_RETRIES);
#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_ds_register_config(ASN_BOOLEAN, "snmp",
"noContextEngineIDDiscovery",
NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_NO_DISCOVERY);
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "timeout",
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_TIMEOUT);
netsnmp_ds_register_config(ASN_INTEGER, "snmp", "retries",
NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_RETRIES);
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_snmp_transport();
init_snmpv3(type);
init_snmp_alarm();
init_snmp_enum(type);
init_vacm();
#if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL)
netsnmp_certs_init();
#endif
#ifdef DNSSEC_LOCAL_VALIDATION
netsnmp_ds_register_config(ASN_BOOLEAN, "snmp", "dnssecWarnOnly",
NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_DNSSEC_WARN_ONLY);
#endif
read_premib_configs();
#ifndef NETSNMP_DISABLE_MIB_LOADING
netsnmp_init_mib();
#endif /* NETSNMP_DISABLE_MIB_LOADING */
read_configs();
} /* end init_snmp() */
/**
* set a flag indicating that the persistent store needs to be saved.
*/
void
snmp_store_needed(const char *type)
{
DEBUGMSGTL(("snmp_store", "setting needed flag...\n"));
_snmp_store_needed = 1;
}
void
snmp_store_if_needed(void)
{
if (0 == _snmp_store_needed)
return;
DEBUGMSGTL(("snmp_store", "store needed...\n"));
snmp_store(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_APPTYPE));
_snmp_store_needed = 0;
}
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 */
#if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL)
netsnmp_certs_shutdown();
#endif
unregister_all_config_handlers();
netsnmp_container_free_list();
clear_sec_mod();
clear_snmp_enum();
netsnmp_clear_tdomain_list();
clear_callback();
netsnmp_ds_shutdown();
netsnmp_clear_default_target();
netsnmp_clear_default_domain();
shutdown_secmod();
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_feature_child_of(snmp_open_ex, netsnmp_unused)
#ifndef NETSNMP_FEATURE_REMOVE_SNMP_OPEN_EX
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);
}
#endif /* NETSNMP_FEATURE_REMOVE_SNMP_OPEN_EX */
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;
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 (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 (session->retries == SNMP_DEFAULT_RETRIES) {
int retry = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_RETRIES);
if (retry < 0)
session->retries = DEFAULT_RETRIES;
else
session->retries = retry;
}
if (session->timeout == SNMP_DEFAULT_TIMEOUT) {
int timeout = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_TIMEOUT);
if (timeout <= 0)
session->timeout = DEFAULT_TIMEOUT;
else
session->timeout = timeout * ONE_SEC;
}
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) {
/*
* security module specific copying
*/
if (sptr->session_setup) {
int ret = (*sptr->session_setup) (in_session, session);
if (ret != SNMPERR_SUCCESS) {
snmp_sess_close(slp);
return NULL;
}
}
/*
* security module specific opening
*/
if (sptr->session_open) {
int ret = (*sptr->session_open) (session);
if (ret != SNMPERR_SUCCESS) {
snmp_sess_close(slp);
return NULL;
}
}
}
/* Anything below this point should only be done if the transport
had no say in the matter */
if (session->securityLevel == 0)
session->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
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;
}
#ifndef NETSNMP_FEATURE_REMOVE_SNMPV3_PROBE_CONTEXTENGINEID_RFC5343
/**
* probe for engineID using RFC 5343 probing mechanisms
*
* Designed to be a callback for within a security model's probe_engineid hook.
* Since it's likely multiple security models won't have engineIDs to
* probe for then this function is a callback likely to be used by
* multiple future security models. E.G. both SSH and DTLS.
*/
int
snmpv3_probe_contextEngineID_rfc5343(void *slp, netsnmp_session *session) {
netsnmp_pdu *pdu = NULL, *response = NULL;
static oid snmpEngineIDoid[] = { 1,3,6,1,6,3,10,2,1,1,0};
static size_t snmpEngineIDoid_len = 11;
static char probeEngineID[] = { (char)0x80, 0, 0, 0, 6 };
static size_t probeEngineID_len = sizeof(probeEngineID);
int status;
pdu = snmp_pdu_create(SNMP_MSG_GET);
if (!pdu)
return SNMP_ERR_GENERR;
pdu->version = SNMP_VERSION_3;
/* don't require a securityName */
if (session->securityName) {
pdu->securityName = strdup(session->securityName);
pdu->securityNameLen = strlen(pdu->securityName);
}
pdu->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
pdu->securityModel = session->securityModel;
pdu->contextEngineID = netsnmp_memdup(probeEngineID, probeEngineID_len);
if (!pdu->contextEngineID) {
snmp_log(LOG_ERR, "failed to clone memory for rfc5343 probe\n");
snmp_free_pdu(pdu);
return SNMP_ERR_GENERR;
}
pdu->contextEngineIDLen = probeEngineID_len;
snmp_add_null_var(pdu, snmpEngineIDoid, snmpEngineIDoid_len);
DEBUGMSGTL(("snmp_api", "probing for engineID using rfc5343 methods...\n"));
session->flags |= SNMP_FLAGS_DONT_PROBE; /* prevent recursion */
status = snmp_sess_synch_response(slp, pdu, &response);
if ((response == NULL) || (status != STAT_SUCCESS)) {
snmp_log(LOG_ERR, "failed rfc5343 contextEngineID probing\n");
return SNMP_ERR_GENERR;
}
/* check that the response makes sense */
if (NULL != response->variables &&
NULL != response->variables->name &&
snmp_oid_compare(response->variables->name,
response->variables->name_length,
snmpEngineIDoid, snmpEngineIDoid_len) == 0 &&
ASN_OCTET_STR == response->variables->type &&
NULL != response->variables->val.string &&
response->variables->val_len > 0) {
session->contextEngineID =
netsnmp_memdup(response->variables->val.string,
response->variables->val_len);
if (!session->contextEngineID) {
snmp_log(LOG_ERR, "failed rfc5343 contextEngineID probing: memory allocation failed\n");
return SNMP_ERR_GENERR;
}
/* technically there likely isn't a securityEngineID but just
in case anyone goes looking we might as well have one */
session->securityEngineID =
netsnmp_memdup(response->variables->val.string,
response->variables->val_len);
if (!session->securityEngineID) {
snmp_log(LOG_ERR, "failed rfc5343 securityEngineID probing: memory allocation failed\n");
return SNMP_ERR_GENERR;
}
session->securityEngineIDLen = session->contextEngineIDLen =
response->variables->val_len;
if (snmp_get_do_debugging()) {
size_t i;
DEBUGMSGTL(("snmp_sess_open",
" probe found engineID: "));
for (i = 0; i < session->securityEngineIDLen; i++)
DEBUGMSG(("snmp_sess_open", "%02x",
session->securityEngineID[i]));
DEBUGMSG(("snmp_sess_open", "\n"));
}
}
return SNMPERR_SUCCESS;
}
#endif /* NETSNMP_FEATURE_REMOVE_SNMPV3_PROBE_CONTEXTENGINEID_RFC5343 */
/**
* 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_session *session;
int status;
struct snmp_secmod_def *sptr = NULL;
if (slp == NULL || slp->session == NULL) {
return 0;
}
session = slp->session;
netsnmp_assert_or_return(session != NULL, 0);
sptr = find_sec_mod(session->securityModel);
/*
* 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->version == SNMP_VERSION_3 &&
(0 == (session->flags & SNMP_FLAGS_DONT_PROBE))) {
if (NULL != sptr && NULL != sptr->probe_engineid) {
DEBUGMSGTL(("snmp_api", "probing for engineID using security model callback...\n"));
/* security model specific mechanism of determining engineID */
status = (*sptr->probe_engineid) (slp, in_session);
if (status != SNMPERR_SUCCESS)
return 0;
} else {
/* XXX: default to the default RFC5343 contextEngineID Probe? */
return 0;
}
}
/*
* see if there is a hook to call now that we're done probing for an
* engineID
*/
if (sptr && sptr->post_probe_engineid) {
status = (*sptr->post_probe_engineid)(slp, in_session);
if (status != SNMPERR_SUCCESS)
return 0;
}
return 1;
}
/*******************************************************************-o-******
* netsnmp_sess_config_transport
*
* Parameters:
* *in_session
* *in_transport
*
* Returns:
* SNMPERR_SUCCESS - Yay
* SNMPERR_GENERR - Generic Error
* SNMPERR_TRANSPORT_CONFIG_ERROR - Transport rejected config
* SNMPERR_TRANSPORT_NO_CONFIG - Transport can't config
*/
int
netsnmp_sess_config_transport(netsnmp_container *transport_configuration,
netsnmp_transport *transport)
{
/* Optional supplimental transport configuration information and
final call to actually open the transport */
if (transport_configuration) {
DEBUGMSGTL(("snmp_sess", "configuring transport\n"));
if (transport->f_config) {
netsnmp_iterator *iter;
netsnmp_transport_config *config_data;
int ret;
iter = CONTAINER_ITERATOR(transport_configuration);
if (NULL == iter) {
return SNMPERR_GENERR;
}
for(config_data = (netsnmp_transport_config*)ITERATOR_FIRST(iter); config_data;
config_data = (netsnmp_transport_config*)ITERATOR_NEXT(iter)) {
ret = transport->f_config(transport, config_data->key,
config_data->value);
if (ret) {
return SNMPERR_TRANSPORT_CONFIG_ERROR;
}
}
} else {
return SNMPERR_TRANSPORT_NO_CONFIG;
}
}
return SNMPERR_SUCCESS;
}
/**
* Copies configuration from the session and calls f_open
* This function copies any configuration stored in the session
* pointer to the transport if it has a f_config pointer and then
* calls the transport's f_open function to actually open the
* connection.
*
* @param in_session A pointer to the session that config information is in.
* @param transport A pointer to the transport to config/open.
*
* @return SNMPERR_SUCCESS : on success
*/
/*******************************************************************-o-******
* netsnmp_sess_config_transport
*
* Parameters:
* *in_session
* *in_transport
*
* Returns:
* SNMPERR_SUCCESS - Yay
* SNMPERR_GENERR - Generic Error
* SNMPERR_TRANSPORT_CONFIG_ERROR - Transport rejected config
* SNMPERR_TRANSPORT_NO_CONFIG - Transport can't config
*/
int
netsnmp_sess_config_and_open_transport(netsnmp_session *in_session,
netsnmp_transport *transport)
{
int rc;
DEBUGMSGTL(("snmp_sess", "opening transport: %x\n", transport->flags & NETSNMP_TRANSPORT_FLAG_OPENED));
/* don't double open */
if (transport->flags & NETSNMP_TRANSPORT_FLAG_OPENED)
return SNMPERR_SUCCESS;
if ((rc = netsnmp_sess_config_transport(in_session->transport_configuration,
transport)) != SNMPERR_SUCCESS) {
in_session->s_snmp_errno = rc;
in_session->s_errno = 0;
return rc;
}
if (transport->f_open)
transport = transport->f_open(transport);
if (transport == NULL) {
DEBUGMSGTL(("snmp_sess", "couldn't interpret peername\n"));
in_session->s_snmp_errno = SNMPERR_BAD_ADDRESS;
in_session->s_errno = errno;
snmp_set_detail(in_session->peername);
return SNMPERR_BAD_ADDRESS;
}
transport->flags |= NETSNMP_TRANSPORT_FLAG_OPENED;
DEBUGMSGTL(("snmp_sess", "done opening transport: %x\n", transport->flags & NETSNMP_TRANSPORT_FLAG_OPENED));
return SNMPERR_SUCCESS;
}
/*******************************************************************-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)
{
netsnmp_transport *transport = NULL;
int rc;
in_session->s_snmp_errno = 0;
in_session->s_errno = 0;
_init_snmp();
{
char *clientaddr_save = NULL;
if (NULL != in_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,
in_session->localname);
}
if (in_session->flags & SNMP_FLAGS_STREAM_SOCKET) {
transport =
netsnmp_tdomain_transport_full("snmp", in_session->peername,
in_session->local_port, "tcp,tcp6",
NULL);
} else {
transport =
netsnmp_tdomain_transport_full("snmp", in_session->peername,
in_session->local_port, "udp,udp6",
NULL);
}
if (NULL != clientaddr_save)
netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_CLIENT_ADDR, clientaddr_save);
}
if (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(in_session->peername);
return NULL;
}
/* Optional supplimental transport configuration information and
final call to actually open the transport */
if ((rc = netsnmp_sess_config_and_open_transport(in_session, transport))
!= SNMPERR_SUCCESS) {
transport = NULL;
return NULL;
}
#if defined(SO_BROADCAST) && defined(SOL_SOCKET)
if ( in_session->flags & SNMP_FLAGS_UDP_BROADCAST) {
int b = 1;
int rc;
rc = setsockopt(transport->sock, SOL_SOCKET, SO_BROADCAST,
(char *)&b, sizeof(b));
if ( rc != 0 ) {
in_session->s_snmp_errno = SNMPERR_BAD_ADDRESS; /* good as any? */
in_session->s_errno = errno;
DEBUGMSGTL(("_sess_open", "couldn't enable UDP_BROADCAST\n"));
return NULL;
}
}
#endif
return snmp_sess_add(in_session, transport, NULL, NULL);
}
/*
* 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;
int rc;
_init_snmp();
if (transport == NULL)
return NULL;
if (in_session == NULL) {
transport->f_close(transport);
netsnmp_transport_free(transport);
return NULL;
}
/* if the transport hasn't been fully opened yet, open it now */
if ((rc = netsnmp_sess_config_and_open_transport(in_session, transport))
!= SNMPERR_SUCCESS) {
return NULL;
}
if (transport->f_setup_session) {
if (SNMPERR_SUCCESS !=
transport->f_setup_session(transport, in_session)) {
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 -- maybe engineID probe now\n"));
if (!snmpv3_engineID_probe(slp, slp->session)) {
DEBUGMSGTL(("snmp_sess_add", "engine ID probe failed\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;
}
int
create_user_from_session(netsnmp_session * session) {
#ifdef NETSNMP_SECMOD_USM
return usm_create_user_from_session(session);
#else
snmp_log(LOG_ERR, "create_user_from_session called when USM wasn't compiled in");
netsnmp_assert(0 == 1);
return SNMP_ERR_GENERR;
#endif
}
/*
* 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 = NULL;
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 = NULL;
if (transport) {
transport->f_close(transport);
netsnmp_transport_free(transport);
}
sesp = slp->session;
slp->session = NULL;
/*
* 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 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;
/* tunneled transports don't have a securityEngineID... that's
USM specific (and maybe other future ones) */
if (pdu->securityModel == SNMP_SEC_MODEL_USM &&
(rpdu->securityEngineIDLen != pdu->securityEngineIDLen ||
memcmp(rpdu->securityEngineID, pdu->securityEngineID,
pdu->securityEngineIDLen)))
return 0;
/* the securityName must match though regardless of secmodel */
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
*/
#ifndef NETSNMP_NOTIFY_ONLY
case SNMP_MSG_GET:
case SNMP_MSG_GETNEXT:
#endif /* ! NETSNMP_NOTIFY_ONLY */
#ifndef NETSNMP_NO_WRITE_SUPPORT
case SNMP_MSG_SET:
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
case SNMP_MSG_INFORM:
if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
pdu->errstat = 0;
if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
pdu->errindex = 0;
break;
#ifndef NETSNMP_NOTIFY_ONLY
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;
#endif /* ! NETSNMP_NOTIFY_ONLY */
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 = se_find_value_in_slist("snmp_secmods", netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECMODEL));
if (pdu->securityModel <= 0) {
pdu->securityModel = SNMP_SEC_MODEL_USM;
}
}
}
if (pdu->securityNameLen == 0 && pdu->securityName == NULL) {
if (session->securityModel != SNMP_SEC_MODEL_TSM &&
session->securityNameLen == 0) {
session->s_snmp_errno = SNMPERR_BAD_SEC_NAME;
return -1;
}
if (session->securityName) {
pdu->securityName = strdup(session->securityName);
if (pdu->securityName == NULL) {
session->s_snmp_errno = SNMPERR_GENERR;
return -1;
}
pdu->securityNameLen = session->securityNameLen;
} else {
pdu->securityName = strdup("");
session->securityName = strdup("");
}
}
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 = NULL;
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
*/
#ifndef NETSNMP_NOTIFY_ONLY
case SNMP_MSG_GET:
case SNMP_MSG_GETNEXT:
#endif /* ! NETSNMP_NOTIFY_ONLY */
#ifndef NETSNMP_NO_WRITE_SUPPORT
case SNMP_MSG_SET:
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
/*
* 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;
#ifndef NETSNMP_NOTIFY_ONLY
case SNMP_MSG_GETBULK:
/*
* not supported in SNMPv1 and SNMPsec
*/
#ifndef NETSNMP_DISABLE_SNMPV1
if (pdu->version == SNMP_