In bsnmpd(1) add support for SNMPv3 message processing model, including message authentication, packet encryption & view-based access control (RFC 3412, 3414, 3415).

Sponsored by:	The FreeBSD Foundation
Reviewed by:	philip@ (mostly)
Approved by:	philip@
This commit is contained in:
Shteryana Shopova 2010-12-08 13:51:38 +00:00
parent 055731ce60
commit 135f7de5dd
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=216294
35 changed files with 4600 additions and 279 deletions

View File

@ -196,7 +196,7 @@ asn_put_temp_header(struct asn_buf *b, u_char type, u_char **ptr)
return (ret);
}
enum asn_err
asn_commit_header(struct asn_buf *b, u_char *ptr)
asn_commit_header(struct asn_buf *b, u_char *ptr, size_t *moved)
{
asn_len_t len;
u_int lenlen, shift;
@ -215,6 +215,8 @@ asn_commit_header(struct asn_buf *b, u_char *ptr)
memmove(ptr + 1 + lenlen, ptr + TEMP_LEN, len);
b->asn_ptr -= shift;
b->asn_len += shift;
if (moved != NULL)
*moved = shift;
}
return (ASN_ERR_OK);
}
@ -912,6 +914,20 @@ asn_skip(struct asn_buf *b, asn_len_t len)
return (ASN_ERR_OK);
}
/*
* Add a padding
*/
enum asn_err
asn_pad(struct asn_buf *b, asn_len_t len)
{
if (b->asn_len < len)
return (ASN_ERR_EOBUF);
b->asn_ptr += len;
b->asn_len -= len;
return (ASN_ERR_OK);
}
/*
* Compare two OIDs.
*

View File

@ -93,7 +93,7 @@ enum asn_err asn_get_header(struct asn_buf *, u_char *, asn_len_t *);
enum asn_err asn_put_header(struct asn_buf *, u_char, asn_len_t);
enum asn_err asn_put_temp_header(struct asn_buf *, u_char, u_char **);
enum asn_err asn_commit_header(struct asn_buf *, u_char *);
enum asn_err asn_commit_header(struct asn_buf *, u_char *, size_t *);
enum asn_err asn_get_integer_raw(struct asn_buf *, asn_len_t, int32_t *);
enum asn_err asn_get_integer(struct asn_buf *, int32_t *);
@ -129,6 +129,7 @@ enum asn_err asn_get_timeticks(struct asn_buf *, uint32_t *);
enum asn_err asn_put_timeticks(struct asn_buf *, uint32_t);
enum asn_err asn_skip(struct asn_buf *, asn_len_t);
enum asn_err asn_pad(struct asn_buf *, asn_len_t);
/*
* Utility functions for OIDs

View File

@ -31,7 +31,7 @@
.\"
.\" $Begemot: bsnmp/lib/bsnmpclient.3,v 1.12 2005/10/04 08:46:50 brandt_h Exp $
.\"
.Dd October 4, 2005
.Dd September 9, 2010
.Dt BSNMPCLIENT 3
.Os
.Sh NAME
@ -52,7 +52,8 @@
.Nm snmp_table_cb_f ,
.Nm snmp_table_fetch ,
.Nm snmp_table_fetch_async ,
.Nm snmp_dialog
.Nm snmp_dialog ,
.Nm snmp_discover_engine
.Nd "SNMP client library"
.Sh LIBRARY
Begemot SNMP library
@ -102,44 +103,56 @@ Begemot SNMP library
.Fn snmp_table_fetch_async "const struct snmp_table *descr" "void *list" "snmp_table_cb_f callback" "void *uarg"
.Ft int
.Fn snmp_dialog "struct snmp_pdu *req" "struct snmp_pdu *resp"
.Ft int
.Fn snmp_discover_engine "void"
.Sh DESCRIPTION
The SNMP library contains routines to easily build SNMP client applications
that use SNMP versions 1 or 2.
that use SNMP versions 1, 2 or 3.
Most of the routines use a
.Vt struct snmp_client :
.Bd -literal -offset indent
struct snmp_client {
enum snmp_version version;
int trans; /* transport type to use */
enum snmp_version version;
int trans; /* which transport to use */
/* these two are read-only for the application */
char *cport; /* port number as string */
char *chost; /* host name or IP address as string */
char *cport; /* port number as string */
char *chost; /* host name or IP address as string */
char read_community[SNMP_COMMUNITY_MAXLEN + 1];
char write_community[SNMP_COMMUNITY_MAXLEN + 1];
char read_community[SNMP_COMMUNITY_MAXLEN + 1];
char write_community[SNMP_COMMUNITY_MAXLEN + 1];
struct timeval timeout;
u_int retries;
/* SNMPv3 specific fields */
int32_t identifier;
int32_t security_model;
struct snmp_engine engine;
struct snmp_user user;
int dump_pdus;
/* SNMPv3 Access control - VACM*/
uint32_t clen;
uint8_t cengine[SNMP_ENGINE_ID_SIZ];
char cname[SNMP_CONTEXT_NAME_SIZ];
size_t txbuflen;
size_t rxbuflen;
struct timeval timeout;
u_int retries;
int fd;
int dump_pdus;
int32_t next_reqid;
int32_t max_reqid;
int32_t min_reqid;
size_t txbuflen;
size_t rxbuflen;
char error[SNMP_STRERROR_LEN];
int fd;
snmp_timeout_start_f timeout_start;
snmp_timeout_stop_f timeout_stop;
int32_t next_reqid;
int32_t max_reqid;
int32_t min_reqid;
/* private */
char local_path[sizeof(SNMP_LOCAL_PATH)];
char error[SNMP_STRERROR_LEN];
snmp_timeout_start_f timeout_start;
snmp_timeout_stop_f timeout_stop;
char local_path[sizeof(SNMP_LOCAL_PATH)];
};
.Ed
.Pp
@ -194,6 +207,23 @@ The default is
The community name to be used for SET requests.
The default is
.Sq private .
.It Va identifier
The message indentifier value to be used with SNMPv3 PDUs. Incremented with
each transmitted PDU.
.It Va security_model
The security model to be used with SNMPv3 PDUs. Currently only User-Based
Security model specified by RFC 3414 (value 3) is supported.
.It Va engine
The authorative SNMP engine parameters to be used with SNMPv3 PDUs.
.It Va user
The USM SNMP user credentials to be used with SNMPv3 PDUs.
.It Va clen
The length of the context engine id to be used with SNMPv3 PDUs.
.It Va cengine
The context engine id to be used with SNMPv3 PDUs. Default is empty.
.It Va cname
The context name to be used with SNMPv3 PDUs. Default is
.Sq "" .
.It Va timeout
The maximum time to wait for responses to requests.
If the time elapses, the request is resent up to
@ -617,6 +647,21 @@ returns -1.
If a response was received 0 is returned.
.Pp
The function
.Fn snmp_discover_engine
is used to discover the authorative snmpEngineId of a remote SNMPv3 agent.
A request PDU with empty USM user name is sent and the client's engine
parameters are set according to the snmpEngine parameters received in the
response PDU.
If the client is configured to use authentication and/or privacy and the
snmpEngineBoots and/or snmpEngineTime in the response had zero values, an
additional request (possibly encrypted) with the appropriate user credentials
is sent to fetch the missing values.
Note, that the function blocks until the discovery proccess is completed.
If no response could be received after all timeouts and retries, or the
response contained errors the function returns -1.
If the discovery proccess was completed 0 is returned.
.Pp
The function
.Fn snmp_parse_server
is used to parse an SNMP server specification string and fill in the
fields of a

View File

@ -1,4 +1,10 @@
.\"
.\" Copyright (c) 2010 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" Portions of this documentation were written by Shteryana Sotirova Shopova
.\" under sponsorship from the FreeBSD Foundation.
.\"
.\" Copyright (c) 2004-2005
.\" Hartmut Brandt.
.\" All rights reserved.
@ -31,7 +37,7 @@
.\"
.\" $Begemot: bsnmp/lib/bsnmplib.3,v 1.9 2005/10/04 08:46:51 brandt_h Exp $
.\"
.Dd October 4, 2005
.Dd September 9, 2010
.Dt BSNMPLIB 3
.Os
.Sh NAME
@ -39,9 +45,15 @@
.Nm snmp_value_parse ,
.Nm snmp_value_copy ,
.Nm snmp_pdu_free ,
.Nm snmp_code snmp_pdu_decode ,
.Nm snmp_code snmp_pdu_encode ,
.Nm snmp_pdu_decode ,
.Nm snmp_pdu_encode ,
.Nm snmp_pdu_decode_header ,
.Nm snmp_pdu_decode_scoped ,
.Nm snmp_pdu_decode_secmode ,
.Nm snmp_pdu_dump ,
.Nm snmp_passwd_to_keys ,
.Nm snmp_get_local_keys ,
.Nm snmp_calc_keychange ,
.Nm TRUTH_MK ,
.Nm TRUTH_GET ,
.Nm TRUTH_OK
@ -64,8 +76,20 @@ Begemot SNMP library
.Fn snmp_pdu_decode "struct asn_buf *buf" "struct snmp_pdu *pdu" "int32_t *ip"
.Ft enum snmp_code
.Fn snmp_pdu_encode "struct snmp_pdu *pdu" "struct asn_buf *buf"
.Ft enum snmp_code
.Fn snmp_pdu_decode_header "struct snmp_pdu *pdu" "struct asn_buf *buf"
.Ft enum snmp_code
.Fn snmp_pdu_decode_scoped "struct asn_buf *buf" "struct snmp_pdu *pdu" "int32_t *ip"
.Ft enum snmp_code
.Fn snmp_pdu_decode_secmode "struct asn_buf *buf" "struct snmp_pdu *pdu"
.Ft void
.Fn snmp_pdu_dump "const struct snmp_pdu *pdu"
.Ft enum snmp_code
.Fn snmp_passwd_to_keys "struct snmp_user *user" "char *passwd"
.Ft enum snmp_code
.Fn snmp_get_local_keys "struct snmp_user *user" "uint8_t *eid" "uint32_t elen"
.Ft enum snmp_code
.Fn snmp_calc_keychange "struct snmp_user *user" "uint8_t *keychange"
.Ft int
.Fn TRUTH_MK "F"
.Ft int
@ -73,8 +97,8 @@ Begemot SNMP library
.Ft int
.Fn TRUTH_OK "T"
.Sh DESCRIPTION
The SNMP library contains routines to handle SNMP version 1 and 2 PDUs.
There are two basic structures used throughout the library:
The SNMP library contains routines to handle SNMP version 1, 2 and 3 PDUs.
There are several basic structures used throughout the library:
.Bd -literal -offset indent
struct snmp_value {
struct asn_oid var;
@ -134,34 +158,126 @@ is not zero,
.Fa v.octetstring.octets
points to a string allocated by
.Xr malloc 3 .
.Pp
.Bd -literal -offset indent
#define SNMP_COMMUNITY_MAXLEN 128
#define SNMP_MAX_BINDINGS 100
#define SNMP_ENGINE_ID_SIZ 32
struct snmp_engine {
uint8_t engine_id[SNMP_ENGINE_ID_SIZ];
uint32_t engine_len;
int32_t engine_boots;
int32_t engine_time;
int32_t max_msg_size;
};
.Ed
.Pp
This structure represents an SNMP engine as specified by the SNMP Management
Architecture described in RFC 3411.
.Pp
.Bd -literal -offset indent
#define SNMP_USM_NAME_SIZ (32 + 1)
#define SNMP_AUTH_KEY_SIZ 40
#define SNMP_PRIV_KEY_SIZ 32
struct snmp_user {
char sec_name[SNMP_USM_NAME_SIZ];
enum snmp_authentication auth_proto;
enum snmp_privacy priv_proto;
uint8_t auth_key[SNMP_AUTH_KEY_SIZ];
uint8_t priv_key[SNMP_PRIV_KEY_SIZ];
};
.Ed
.Pp
This structure represents an SNMPv3 user as specified by the User-based
Security Model (USM) described in RFC 3414. The field
.Fa sec_name
is a human readable string containing the security user name.
.Fa auth_proto
contains the id of the authentication protocol in use by the user and may be one
of:
.Bd -literal -offset indent
enum snmp_authentication {
SNMP_AUTH_NOAUTH = 0,
SNMP_AUTH_HMAC_MD5,
SNMP_AUTH_HMAC_SHA
};
.Ed
.Fa priv_proto
contains the id of the privacy protocol in use by the user and may be one
of:
.Bd -literal -offset indent
enum snmp_privacy {
SNMP_PRIV_NOPRIV = 0,
SNMP_PRIV_DES = 1,
SNMP_PRIV_AES
};
.Ed
.Fa auth_key
and
.Fa priv_key
contain the authentication and privacy keys for the user.
.Pp
.Bd -literal -offset indent
#define SNMP_COMMUNITY_MAXLEN 128
#define SNMP_MAX_BINDINGS 100
#define SNMP_CONTEXT_NAME_SIZ (32 + 1)
#define SNMP_TIME_WINDOW 150
#define SNMP_USM_AUTH_SIZE 12
#define SNMP_USM_PRIV_SIZE 8
#define SNMP_MSG_AUTH_FLAG 0x1
#define SNMP_MSG_PRIV_FLAG 0x2
#define SNMP_MSG_REPORT_FLAG 0x4
#define SNMP_SECMODEL_USM 3
struct snmp_pdu {
char community[SNMP_COMMUNITY_MAXLEN + 1];
enum snmp_version version;
u_int type;
char community[SNMP_COMMUNITY_MAXLEN + 1];
enum snmp_version version;
u_int type;
/* SNMPv3 PDU header fields */
int32_t identifier;
uint8_t flags;
int32_t security_model;
struct snmp_engine engine;
/* Associated USM user parameters */
struct snmp_user user;
uint8_t msg_digest[SNMP_USM_AUTH_SIZE];
uint8_t msg_salt[SNMP_USM_PRIV_SIZE];
/* View-based Access Model */
uint32_t context_engine_len;
uint8_t context_engine[SNMP_ENGINE_ID_SIZ];
char context_name[SNMP_CONTEXT_NAME_SIZ];
/* trap only */
struct asn_oid enterprise;
u_char agent_addr[4];
int32_t generic_trap;
int32_t specific_trap;
u_int32_t time_stamp;
struct asn_oid enterprise;
u_char agent_addr[4];
int32_t generic_trap;
int32_t specific_trap;
uint32_t time_stamp;
/* others */
int32_t request_id;
int32_t error_status;
int32_t error_index;
int32_t request_id;
int32_t error_status;
int32_t error_index;
/* fixes for encoding */
u_char *outer_ptr;
u_char *pdu_ptr;
u_char *vars_ptr;
size_t outer_len;
size_t scoped_len;
u_char *outer_ptr;
u_char *digest_ptr;
u_char *encrypted_ptr;
u_char *scoped_ptr;
u_char *pdu_ptr;
u_char *vars_ptr;
struct snmp_value bindings[SNMP_MAX_BINDINGS];
u_int nbindings;
struct snmp_value bindings[SNMP_MAX_BINDINGS];
u_int nbindings;
};
.Ed
This structure contains a decoded SNMP PDU.
@ -172,11 +288,15 @@ enum snmp_version {
SNMP_Verr = 0,
SNMP_V1 = 1,
SNMP_V2c,
SNMP_V3
};
.Ed
and
.Fa type
is the type of the PDU.
.Fa security_model
is the security model used for SNMPv3 PDUs. The only supported
value currently is 3 (User-based Security Model).
.Pp
The function
.Fn snmp_value_free
@ -223,15 +343,60 @@ The function
.Fn snmp_pdu_encode
encodes the PDU
.Fa pdu
into the an octetstring in buffer
into the an octetstring in buffer, and if authentication and privacy are used,
calculates a message digest and encrypts the PDU data in the buffer
.Fa buf .
.Pp
The function
.Fn snmp_pdu_decode_header
decodes the header of the PDU pointed to by
.Fa buf .
The uncoded PDU contents remain in the buffer.
.Pp
The function
.Fn snmp_pdu_decode_scoped
decodes the scoped PDU pointed to by
.Fa buf .
.Pp
The function
.Fn snmp_pdu_decode_secmode
verifies the authentication parameter contained in the PDU (if present) and
if the PDU is encrypted, decrypts the PDU contents pointed to by
.Fa buf .
If successfull, a plain text scoped PDU is stored in the buffer.
.Pp
The function
.Fn snmp_pdu_dump
dumps the PDU in a human readable form by calling
.Fn snmp_printf .
.Pp
The function
.Fn snmp_passwd_to_keys
calculates a binary private authentication key corresponding to a plain text human
readable password string. The calculated key is placed in the
.Fa auth_key
field of the
.Fa user .
.Pp
The function
.Fn snmp_get_local_keys
calculates a localazied authentication and privacy keys for a specified SNMPv3
engine. The calculateds keys are placed in the
.Fa auth_key
and
.Fa priv_key
fields of the
.Fa user .
.Pp
The function
.Fn snmp_calc_keychange
calculates a binary key change octet string based on the contents of an old and
a new binary localized key. The rezult is placed in the buffer pointer to by
.Fa keychange
and may be used by an SNMPv3 user who wishes to change his/her password
or localized key.
.Pp
The function
.Fn TRUTH_MK
takes a C truth value (zero or non-zero) and makes an SNMP truth value (2 or 1).
The function
@ -281,6 +446,13 @@ A variable binding value was out of the allowed range.
The PDU is of an unsupported version.
.It Bq Er SNMP_CODE_BADENQ
There was an ASN.1 value with an unsupported tag.
.It Bq Er SNMP_CODE_BADSECLEVEL
The requested securityLevel contained in the PDU is not supported.
.It Bq Er SNMP_CODE_BADDIGEST
The PDU authentication parameter received in the PDU did not match the
calculated message digest.
.It Bq Er SNMP_CODE_EDECRYPT
Error occured while trying to decrypt the PDU.
.El
.Pp
.Fn snmp_pdu_encode
@ -297,8 +469,21 @@ Encoding failed.
.Xr bsnmpagent 3 ,
.Xr bsnmpclient 3 ,
.Xr bsnmplib 3
.Sh CAVEAT
The SNMPv3 message digests, encryption and decryption, and key routines use
the cryptographic functions from
.Xr crypto 3 .
The library may optionally be built without references to the
.Xr crypto 3
library. In such case only plain text SNMPv3 PDUs without message digests
may be proccessed correctly.
.Sh STANDARDS
This implementation conforms to the applicable IETF RFCs and ITU-T
recommendations.
.Sh AUTHORS
The Begemot SNMP library was originally written by
.An Hartmut Brandt Aq harti@FreeBSD.org
.Pp
.An Shteryana Shopova Aq syrinx@FreeBSD.org
added support for the SNMPv3 message proccessing and User-Based
Security model message authentication and privacy.

View File

@ -5,6 +5,12 @@
*
* Author: Harti Brandt <harti@freebsd.org>
*
* Copyright (c) 2010 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Shteryana Sotirova Shopova
* under sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -271,112 +277,144 @@ parse_pdus(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
return (err);
}
/*
* Parse the outer SEQUENCE value. ASN_ERR_TAG means 'bad version'.
*/
enum asn_err
snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp)
static enum asn_err
parse_secparams(struct asn_buf *b, struct snmp_pdu *pdu)
{
int32_t version;
u_char type;
u_int comm_len;
asn_len_t octs_len;
u_char buf[256]; /* XXX: calc max possible size here */
struct asn_buf tb;
if (asn_get_integer(b, &version) != ASN_ERR_OK) {
snmp_error("cannot decode version");
memset(buf, 0, 256);
tb.asn_ptr = buf;
tb.asn_len = 256;
if (asn_get_octetstring(b, buf, &tb.asn_len) != ASN_ERR_OK) {
snmp_error("cannot parse usm header");
return (ASN_ERR_FAILED);
}
if (version == 0) {
pdu->version = SNMP_V1;
} else if (version == 1) {
pdu->version = SNMP_V2c;
} else {
pdu->version = SNMP_Verr;
snmp_error("unsupported SNMP version");
return (ASN_ERR_TAG);
}
comm_len = SNMP_COMMUNITY_MAXLEN;
if (asn_get_octetstring(b, (u_char *)pdu->community,
&comm_len) != ASN_ERR_OK) {
snmp_error("cannot decode community");
return (ASN_ERR_FAILED);
}
pdu->community[comm_len] = '\0';
if (asn_get_header(b, &type, lenp) != ASN_ERR_OK) {
snmp_error("cannot get pdu header");
return (ASN_ERR_FAILED);
}
if ((type & ~ASN_TYPE_MASK) !=
(ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) {
snmp_error("bad pdu header tag");
return (ASN_ERR_FAILED);
}
pdu->type = type & ASN_TYPE_MASK;
switch (pdu->type) {
case SNMP_PDU_GET:
case SNMP_PDU_GETNEXT:
case SNMP_PDU_RESPONSE:
case SNMP_PDU_SET:
break;
case SNMP_PDU_TRAP:
if (pdu->version != SNMP_V1) {
snmp_error("bad pdu type %u", pdu->type);
return (ASN_ERR_FAILED);
}
break;
case SNMP_PDU_GETBULK:
case SNMP_PDU_INFORM:
case SNMP_PDU_TRAP2:
case SNMP_PDU_REPORT:
if (pdu->version == SNMP_V1) {
snmp_error("bad pdu type %u", pdu->type);
return (ASN_ERR_FAILED);
}
break;
default:
snmp_error("bad pdu type %u", pdu->type);
if (asn_get_sequence(&tb, &octs_len) != ASN_ERR_OK) {
snmp_error("cannot decode usm header");
return (ASN_ERR_FAILED);
}
if (*lenp > b->asn_len) {
snmp_error("pdu length too long");
octs_len = SNMP_ENGINE_ID_SIZ;
if (asn_get_octetstring(&tb, (u_char *)&pdu->engine.engine_id,
&octs_len) != ASN_ERR_OK) {
snmp_error("cannot decode msg engine id");
return (ASN_ERR_FAILED);
}
pdu->engine.engine_len = octs_len;
if (asn_get_integer(&tb, &pdu->engine.engine_boots) != ASN_ERR_OK) {
snmp_error("cannot decode msg engine boots");
return (ASN_ERR_FAILED);
}
if (asn_get_integer(&tb, &pdu->engine.engine_time) != ASN_ERR_OK) {
snmp_error("cannot decode msg engine time");
return (ASN_ERR_FAILED);
}
octs_len = SNMP_ADM_STR32_SIZ - 1;
if (asn_get_octetstring(&tb, (u_char *)&pdu->user.sec_name, &octs_len)
!= ASN_ERR_OK) {
snmp_error("cannot decode msg user name");
return (ASN_ERR_FAILED);
}
pdu->user.sec_name[octs_len] = '\0';
octs_len = sizeof(pdu->msg_digest);
if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_digest, &octs_len) !=
ASN_ERR_OK || ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0 &&
octs_len != sizeof(pdu->msg_digest))) {
snmp_error("cannot decode msg authentication param");
return (ASN_ERR_FAILED);
}
octs_len = sizeof(pdu->msg_salt);
if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_salt, &octs_len) !=
ASN_ERR_OK ||((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 &&
octs_len != sizeof(pdu->msg_salt))) {
snmp_error("cannot decode msg authentication param");
return (ASN_ERR_FAILED);
}
if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) {
pdu->digest_ptr = b->asn_ptr - SNMP_USM_AUTH_SIZE;
pdu->digest_ptr -= octs_len + ASN_MAXLENLEN;
}
return (ASN_ERR_OK);
}
static enum asn_err
parse_message(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
static enum snmp_code
pdu_encode_secparams(struct asn_buf *b, struct snmp_pdu *pdu)
{
enum asn_err err;
asn_len_t len, trailer;
u_char buf[256], *sptr;
struct asn_buf tb;
size_t auth_off, moved = 0;
err = snmp_parse_message_hdr(b, pdu, &len);
if (ASN_ERR_STOPPED(err))
return (err);
auth_off = 0;
memset(buf, 0, 256);
tb.asn_ptr = buf;
tb.asn_len = 256;
trailer = b->asn_len - len;
b->asn_len = len;
if (asn_put_temp_header(&tb, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
&sptr) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
err = parse_pdus(b, pdu, ip);
if (ASN_ERR_STOPPED(err))
return (ASN_ERR_FAILED);
if (asn_put_octetstring(&tb, (u_char *)pdu->engine.engine_id,
pdu->engine.engine_len) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (b->asn_len != 0)
snmp_error("ignoring trailing junk after pdu");
if (asn_put_integer(&tb, pdu->engine.engine_boots) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
b->asn_len = trailer;
if (asn_put_integer(&tb, pdu->engine.engine_time) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
return (err);
if (asn_put_octetstring(&tb, (u_char *)pdu->user.sec_name,
strlen(pdu->user.sec_name)) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) {
auth_off = sizeof(buf) - tb.asn_len + ASN_MAXLENLEN;
if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest,
sizeof(pdu->msg_digest)) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
} else {
if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest, 0)
!= ASN_ERR_OK)
return (SNMP_CODE_FAILED);
}
if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0) {
if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt,
sizeof(pdu->msg_salt)) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
} else {
if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt, 0)
!= ASN_ERR_OK)
return (SNMP_CODE_FAILED);
}
if (asn_commit_header(&tb, sptr, &moved) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0)
pdu->digest_ptr = b->asn_ptr + auth_off - moved;
if (asn_put_octetstring(b, buf, sizeof(buf) - tb.asn_len) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
pdu->digest_ptr += ASN_MAXLENLEN;
if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && asn_put_temp_header(b,
ASN_TYPE_OCTETSTRING, &pdu->encrypted_ptr) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
return (SNMP_CODE_OK);
}
/*
@ -388,9 +426,45 @@ parse_message(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
enum snmp_code
snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
{
enum snmp_code code;
if ((code = snmp_pdu_decode_header(b, pdu)) != SNMP_CODE_OK)
return (code);
if (pdu->version == SNMP_V3) {
if (pdu->security_model != SNMP_SECMODEL_USM)
return (SNMP_CODE_FAILED);
if ((code = snmp_pdu_decode_secmode(b, pdu)) != SNMP_CODE_OK)
return (code);
}
code = snmp_pdu_decode_scoped(b, pdu, ip);
switch (code) {
case SNMP_CODE_FAILED:
snmp_pdu_free(pdu);
break;
case SNMP_CODE_BADENC:
if (pdu->version == SNMP_Verr)
return (SNMP_CODE_BADVERS);
default:
break;
}
return (code);
}
enum snmp_code
snmp_pdu_decode_header(struct asn_buf *b, struct snmp_pdu *pdu)
{
int32_t version;
u_int octs_len;
asn_len_t len;
memset(pdu, 0, sizeof(*pdu));
pdu->outer_ptr = b->asn_ptr;
pdu->outer_len = b->asn_len;
if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
snmp_error("cannot decode pdu header");
@ -405,32 +479,192 @@ snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
b->asn_len = len;
}
switch (parse_message(b, pdu, ip)) {
case ASN_ERR_OK:
return (SNMP_CODE_OK);
case ASN_ERR_FAILED:
case ASN_ERR_EOBUF:
snmp_pdu_free(pdu);
if (asn_get_integer(b, &version) != ASN_ERR_OK) {
snmp_error("cannot decode version");
return (SNMP_CODE_FAILED);
}
case ASN_ERR_BADLEN:
return (SNMP_CODE_BADLEN);
if (version == 0)
pdu->version = SNMP_V1;
else if (version == 1)
pdu->version = SNMP_V2c;
else if (version == 3)
pdu->version = SNMP_V3;
else {
pdu->version = SNMP_Verr;
snmp_error("unsupported SNMP version");
return (SNMP_CODE_BADENC);
}
case ASN_ERR_RANGE:
return (SNMP_CODE_OORANGE);
if (pdu->version == SNMP_V3) {
if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
snmp_error("cannot decode pdu global data header");
return (SNMP_CODE_FAILED);
}
case ASN_ERR_TAG:
if (pdu->version == SNMP_Verr)
return (SNMP_CODE_BADVERS);
else
return (SNMP_CODE_BADENC);
if (asn_get_integer(b, &pdu->identifier) != ASN_ERR_OK) {
snmp_error("cannot decode msg indetifier");
return (SNMP_CODE_FAILED);
}
if (asn_get_integer(b, &pdu->engine.max_msg_size)
!= ASN_ERR_OK) {
snmp_error("cannot decode msg size");
return (SNMP_CODE_FAILED);
}
octs_len = 1;
if (asn_get_octetstring(b, (u_char *)&pdu->flags,
&octs_len) != ASN_ERR_OK) {
snmp_error("cannot decode msg flags");
return (SNMP_CODE_FAILED);
}
if (asn_get_integer(b, &pdu->security_model) != ASN_ERR_OK) {
snmp_error("cannot decode msg size");
return (SNMP_CODE_FAILED);
}
if (pdu->security_model != SNMP_SECMODEL_USM)
return (SNMP_CODE_FAILED);
if (parse_secparams(b, pdu) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
} else {
octs_len = SNMP_COMMUNITY_MAXLEN;
if (asn_get_octetstring(b, (u_char *)pdu->community,
&octs_len) != ASN_ERR_OK) {
snmp_error("cannot decode community");
return (SNMP_CODE_FAILED);
}
pdu->community[octs_len] = '\0';
}
return (SNMP_CODE_OK);
}
enum snmp_code
snmp_pdu_decode_scoped(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
{
u_char type;
asn_len_t len, trailer;
enum asn_err err;
if (pdu->version == SNMP_V3) {
if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
snmp_error("cannot decode scoped pdu header");
return (SNMP_CODE_FAILED);
}
len = SNMP_ENGINE_ID_SIZ;
if (asn_get_octetstring(b, (u_char *)&pdu->context_engine,
&len) != ASN_ERR_OK) {
snmp_error("cannot decode msg context engine");
return (SNMP_CODE_FAILED);
}
pdu->context_engine_len = len;
len = SNMP_CONTEXT_NAME_SIZ;
if (asn_get_octetstring(b, (u_char *)&pdu->context_name,
&len) != ASN_ERR_OK) {
snmp_error("cannot decode msg context name");
return (SNMP_CODE_FAILED);
}
pdu->context_name[len] = '\0';
}
if (asn_get_header(b, &type, &len) != ASN_ERR_OK) {
snmp_error("cannot get pdu header");
return (SNMP_CODE_FAILED);
}
if ((type & ~ASN_TYPE_MASK) !=
(ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) {
snmp_error("bad pdu header tag");
return (SNMP_CODE_FAILED);
}
pdu->type = type & ASN_TYPE_MASK;
switch (pdu->type) {
case SNMP_PDU_GET:
case SNMP_PDU_GETNEXT:
case SNMP_PDU_RESPONSE:
case SNMP_PDU_SET:
break;
case SNMP_PDU_TRAP:
if (pdu->version != SNMP_V1) {
snmp_error("bad pdu type %u", pdu->type);
return (SNMP_CODE_FAILED);
}
break;
case SNMP_PDU_GETBULK:
case SNMP_PDU_INFORM:
case SNMP_PDU_TRAP2:
case SNMP_PDU_REPORT:
if (pdu->version == SNMP_V1) {
snmp_error("bad pdu type %u", pdu->type);
return (SNMP_CODE_FAILED);
}
break;
default:
snmp_error("bad pdu type %u", pdu->type);
return (SNMP_CODE_FAILED);
}
trailer = b->asn_len - len;
b->asn_len = len;
err = parse_pdus(b, pdu, ip);
if (ASN_ERR_STOPPED(err))
return (SNMP_CODE_FAILED);
if (b->asn_len != 0)
snmp_error("ignoring trailing junk after pdu");
b->asn_len = trailer;
return (SNMP_CODE_OK);
}
enum snmp_code
snmp_pdu_decode_secmode(struct asn_buf *b, struct snmp_pdu *pdu)
{
u_char type;
enum snmp_code code;
uint8_t digest[SNMP_USM_AUTH_SIZE];
if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH &&
(pdu->flags & SNMP_MSG_AUTH_FLAG) == 0)
return (SNMP_CODE_BADSECLEVEL);
if ((code = snmp_pdu_calc_digest(b, pdu, digest)) !=
SNMP_CODE_OK)
return (SNMP_CODE_FAILED);
if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH &&
memcmp(digest, pdu->msg_digest, sizeof(pdu->msg_digest)) != 0)
return (SNMP_CODE_BADDIGEST);
if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && (asn_get_header(b, &type,
&pdu->scoped_len) != ASN_ERR_OK || type != ASN_TYPE_OCTETSTRING)) {
snmp_error("cannot decode encrypted pdu");
return (SNMP_CODE_FAILED);
}
pdu->scoped_ptr = b->asn_ptr;
if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV &&
(pdu->flags & SNMP_MSG_PRIV_FLAG) == 0)
return (SNMP_CODE_BADSECLEVEL);
if ((code = snmp_pdu_decrypt(b, pdu)) != SNMP_CODE_OK)
return (SNMP_CODE_FAILED);
return (code);
}
/*
* Check whether what we have is the complete PDU by snooping at the
* enclosing structure header. This returns:
@ -500,6 +734,7 @@ enum snmp_code
snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu)
{
enum asn_err err;
u_char *v3_hdr_ptr;
if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
&pdu->outer_ptr) != ASN_ERR_OK)
@ -509,14 +744,62 @@ snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu)
err = asn_put_integer(b, 0);
else if (pdu->version == SNMP_V2c)
err = asn_put_integer(b, 1);
else if (pdu->version == SNMP_V3)
err = asn_put_integer(b, 3);
else
return (SNMP_CODE_BADVERS);
if (err != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (asn_put_octetstring(b, (u_char *)pdu->community,
strlen(pdu->community)) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (pdu->version == SNMP_V3) {
if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE |
ASN_TYPE_CONSTRUCTED), &v3_hdr_ptr) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (asn_put_integer(b, pdu->identifier) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (asn_put_integer(b, pdu->engine.max_msg_size) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (pdu->type != SNMP_PDU_RESPONSE &&
pdu->type != SNMP_PDU_TRAP &&
pdu->type != SNMP_PDU_REPORT)
pdu->flags |= SNMP_MSG_REPORT_FLAG;
if (asn_put_octetstring(b, (u_char *)&pdu->flags, 1)
!= ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (asn_put_integer(b, pdu->security_model) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (asn_commit_header(b, v3_hdr_ptr, NULL) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (pdu->security_model != SNMP_SECMODEL_USM)
return (SNMP_CODE_FAILED);
if (pdu_encode_secparams(b, pdu) != SNMP_CODE_OK)
return (SNMP_CODE_FAILED);
/* View-based Access Conntrol information */
if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE |
ASN_TYPE_CONSTRUCTED), &pdu->scoped_ptr) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (asn_put_octetstring(b, (u_char *)pdu->context_engine,
pdu->context_engine_len) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (asn_put_octetstring(b, (u_char *)pdu->context_name,
strlen(pdu->context_name)) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
} else {
if (asn_put_octetstring(b, (u_char *)pdu->community,
strlen(pdu->community)) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
}
if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT |
pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK)
@ -550,13 +833,66 @@ snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu)
return (SNMP_CODE_OK);
}
enum snmp_code
snmp_fix_encoding(struct asn_buf *b, const struct snmp_pdu *pdu)
static enum asn_err
snmp_pdu_fix_padd(struct asn_buf *b, struct snmp_pdu *pdu)
{
if (asn_commit_header(b, pdu->vars_ptr) != ASN_ERR_OK ||
asn_commit_header(b, pdu->pdu_ptr) != ASN_ERR_OK ||
asn_commit_header(b, pdu->outer_ptr) != ASN_ERR_OK)
asn_len_t padlen;
if (pdu->user.priv_proto == SNMP_PRIV_DES && pdu->scoped_len % 8 != 0) {
padlen = 8 - (pdu->scoped_len % 8);
if (asn_pad(b, padlen) != ASN_ERR_OK)
return (ASN_ERR_FAILED);
pdu->scoped_len += padlen;
}
return (ASN_ERR_OK);
}
enum snmp_code
snmp_fix_encoding(struct asn_buf *b, struct snmp_pdu *pdu)
{
size_t moved = 0;
enum snmp_code code;
if (asn_commit_header(b, pdu->vars_ptr, NULL) != ASN_ERR_OK ||
asn_commit_header(b, pdu->pdu_ptr, NULL) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (pdu->version == SNMP_V3) {
if (asn_commit_header(b, pdu->scoped_ptr, NULL) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
pdu->scoped_len = b->asn_ptr - pdu->scoped_ptr;
if ((code = snmp_pdu_fix_padd(b, pdu))!= ASN_ERR_OK)
return (SNMP_CODE_FAILED);
if (pdu->security_model != SNMP_SECMODEL_USM)
return (SNMP_CODE_FAILED);
if (snmp_pdu_encrypt(b, pdu) != SNMP_CODE_OK)
return (SNMP_CODE_FAILED);
if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV &&
asn_commit_header(b, pdu->encrypted_ptr, NULL) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
}
if (asn_commit_header(b, pdu->outer_ptr, &moved) != ASN_ERR_OK)
return (SNMP_CODE_FAILED);
pdu->outer_len = b->asn_ptr - pdu->outer_ptr;
pdu->digest_ptr -= moved;
if (pdu->version == SNMP_V3) {
if ((code = snmp_pdu_calc_digest(b, pdu, pdu->msg_digest)) !=
SNMP_CODE_OK)
return (SNMP_CODE_FAILED);
if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0)
memcpy(pdu->digest_ptr, pdu->msg_digest,
sizeof(pdu->msg_digest));
}
return (SNMP_CODE_OK);
}
@ -639,7 +975,7 @@ snmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding)
return (err);
}
err = asn_commit_header(b, ptr);
err = asn_commit_header(b, ptr, NULL);
if (err != ASN_ERR_OK) {
*b = save;
return (err);
@ -775,6 +1111,8 @@ snmp_pdu_dump(const struct snmp_pdu *pdu)
vers = "SNMPv1";
else if (pdu->version == SNMP_V2c)
vers = "SNMPv2c";
else if (pdu->version == SNMP_V3)
vers = "SNMPv3";
else
vers = "v?";
@ -837,6 +1175,39 @@ snmp_value_copy(struct snmp_value *to, const struct snmp_value *from)
return (0);
}
void
snmp_pdu_init_secparams(struct snmp_pdu *pdu, struct snmp_engine *eng,
struct snmp_user *user)
{
int32_t rval;
memcpy(&pdu->engine, eng, sizeof(pdu->engine));
memcpy(&pdu->user, user, sizeof(pdu->user));
if (user->auth_proto != SNMP_AUTH_NOAUTH)
pdu->flags |= SNMP_MSG_AUTH_FLAG;
switch (user->priv_proto) {
case SNMP_PRIV_DES:
memcpy(pdu->msg_salt, &eng->engine_boots,
sizeof(eng->engine_boots));
rval = random();
memcpy(pdu->msg_salt + sizeof(eng->engine_boots), &rval,
sizeof(int32_t));
pdu->flags |= SNMP_MSG_PRIV_FLAG;
break;
case SNMP_PRIV_AES:
rval = random();
memcpy(pdu->msg_salt, &rval, sizeof(int32_t));
rval = random();
memcpy(pdu->msg_salt + sizeof(int32_t), &rval, sizeof(int32_t));
pdu->flags |= SNMP_MSG_PRIV_FLAG;
break;
default:
break;
}
}
void
snmp_pdu_free(struct snmp_pdu *pdu)
{

View File

@ -5,6 +5,13 @@
*
* Author: Harti Brandt <harti@freebsd.org>
*
* Copyright (c) 2010 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Shteryana Sotirova Shopova
* under sponsorship from the FreeBSD Foundation.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@ -35,8 +42,11 @@
#include <sys/types.h>
#define SNMP_COMMUNITY_MAXLEN 128
#define SNMP_MAX_BINDINGS 100
#define SNMP_COMMUNITY_MAXLEN 128
#define SNMP_MAX_BINDINGS 100
#define SNMP_CONTEXT_NAME_SIZ (32 + 1)
#define SNMP_ENGINE_ID_SIZ 32
#define SNMP_TIME_WINDOW 150
enum snmp_syntax {
SNMP_SYNTAX_NULL = 0,
@ -75,33 +85,110 @@ struct snmp_value {
enum snmp_version {
SNMP_Verr = 0,
SNMP_V1 = 1,
SNMP_V2c,
SNMP_V2c = 2,
SNMP_V3,
};
#define SNMP_ADM_STR32_SIZ (32 + 1)
#define SNMP_AUTH_KEY_SIZ 40
#define SNMP_PRIV_KEY_SIZ 32
#define SNMP_USM_AUTH_SIZE 12
#define SNMP_USM_PRIV_SIZE 8
#define SNMP_AUTH_HMACMD5_KEY_SIZ 16
#define SNMP_AUTH_HMACSHA_KEY_SIZ 20
#define SNMP_PRIV_AES_KEY_SIZ 16
#define SNMP_PRIV_DES_KEY_SIZ 8
enum snmp_secmodel {
SNMP_SECMODEL_ANY = 0,
SNMP_SECMODEL_SNMPv1 = 1,
SNMP_SECMODEL_SNMPv2c = 2,
SNMP_SECMODEL_USM = 3,
SNMP_SECMODEL_UNKNOWN
};
enum snmp_usm_level {
SNMP_noAuthNoPriv = 1,
SNMP_authNoPriv = 2,
SNMP_authPriv = 3
};
enum snmp_authentication {
SNMP_AUTH_NOAUTH = 0,
SNMP_AUTH_HMAC_MD5,
SNMP_AUTH_HMAC_SHA
};
enum snmp_privacy {
SNMP_PRIV_NOPRIV = 0,
SNMP_PRIV_DES = 1,
SNMP_PRIV_AES
};
struct snmp_engine {
uint8_t engine_id[SNMP_ENGINE_ID_SIZ];
uint32_t engine_len;
int32_t engine_boots;
int32_t engine_time;
int32_t max_msg_size;
};
struct snmp_user {
char sec_name[SNMP_ADM_STR32_SIZ];
enum snmp_authentication auth_proto;
enum snmp_privacy priv_proto;
uint8_t auth_key[SNMP_AUTH_KEY_SIZ];
uint8_t priv_key[SNMP_PRIV_KEY_SIZ];
};
struct snmp_pdu {
char community[SNMP_COMMUNITY_MAXLEN + 1];
enum snmp_version version;
u_int type;
char community[SNMP_COMMUNITY_MAXLEN + 1];
enum snmp_version version;
u_int type;
/* SNMPv3 PDU header fields */
int32_t identifier;
uint8_t flags;
int32_t security_model;
struct snmp_engine engine;
/* Associated USM user parameters */
struct snmp_user user;
uint8_t msg_digest[SNMP_USM_AUTH_SIZE];
uint8_t msg_salt[SNMP_USM_PRIV_SIZE];
/* View-based Access Model */
/* XXX: put in separate structure - conflicts with struct snmp_context */
uint32_t context_engine_len;
uint8_t context_engine[SNMP_ENGINE_ID_SIZ];
char context_name[SNMP_CONTEXT_NAME_SIZ];
/* trap only */
struct asn_oid enterprise;
u_char agent_addr[4];
int32_t generic_trap;
int32_t specific_trap;
uint32_t time_stamp;
struct asn_oid enterprise;
u_char agent_addr[4];
int32_t generic_trap;
int32_t specific_trap;
uint32_t time_stamp;
/* others */
int32_t request_id;
int32_t error_status;
int32_t error_index;
int32_t request_id;
int32_t error_status;
int32_t error_index;
/* fixes for encoding */
u_char *outer_ptr;
u_char *pdu_ptr;
u_char *vars_ptr;
size_t outer_len;
size_t scoped_len;
u_char *outer_ptr;
u_char *digest_ptr;
u_char *encrypted_ptr;
u_char *scoped_ptr;
u_char *pdu_ptr;
u_char *vars_ptr;
struct snmp_value bindings[SNMP_MAX_BINDINGS];
u_int nbindings;
struct snmp_value bindings[SNMP_MAX_BINDINGS];
u_int nbindings;
};
#define snmp_v1_pdu snmp_pdu
@ -150,20 +237,38 @@ enum snmp_code {
SNMP_CODE_BADLEN,
SNMP_CODE_BADENC,
SNMP_CODE_OORANGE,
SNMP_CODE_BADSECLEVEL,
SNMP_CODE_NOTINTIME,
SNMP_CODE_BADUSER,
SNMP_CODE_BADENGINE,
SNMP_CODE_BADDIGEST,
SNMP_CODE_EDECRYPT
};
#define SNMP_MSG_AUTH_FLAG 0x1
#define SNMP_MSG_PRIV_FLAG 0x2
#define SNMP_MSG_REPORT_FLAG 0x4
#define SNMP_MSG_AUTODISCOVER 0x80
void snmp_value_free(struct snmp_value *);
int snmp_value_parse(const char *, enum snmp_syntax, union snmp_values *);
int snmp_value_copy(struct snmp_value *, const struct snmp_value *);
void snmp_pdu_free(struct snmp_pdu *);
enum snmp_code snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *);
enum snmp_code snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b);
enum snmp_code snmp_pdu_decode_header(struct asn_buf *, struct snmp_pdu *);
enum snmp_code snmp_pdu_decode_scoped(struct asn_buf *, struct snmp_pdu *, int32_t *);
enum snmp_code snmp_pdu_encode(struct snmp_pdu *, struct asn_buf *);
enum snmp_code snmp_pdu_decode_secmode(struct asn_buf *, struct snmp_pdu *);
int snmp_pdu_snoop(const struct asn_buf *);
void snmp_pdu_dump(const struct snmp_pdu *pdu);
enum snmp_code snmp_passwd_to_keys(struct snmp_user *, char *);
enum snmp_code snmp_get_local_keys(struct snmp_user *, uint8_t *, uint32_t);
enum snmp_code snmp_calc_keychange(struct snmp_user *, uint8_t *);
extern void (*snmp_error)(const char *, ...);
extern void (*snmp_printf)(const char *, ...);

View File

@ -165,6 +165,29 @@ find_subnode(const struct snmp_value *value)
return (NULL);
}
static void
snmp_pdu_create_response(struct snmp_pdu *pdu, struct snmp_pdu *resp)
{
memset(resp, 0, sizeof(*resp));
strcpy(resp->community, pdu->community);
resp->version = pdu->version;
resp->type = SNMP_PDU_RESPONSE;
resp->request_id = pdu->request_id;
resp->version = pdu->version;
if (resp->version != SNMP_V3)
return;
snmp_pdu_init_secparams(resp, &pdu->engine, &pdu->user);
resp->identifier = pdu->identifier;
resp->security_model = pdu->security_model;
resp->context_engine_len = pdu->context_engine_len;
memcpy(resp->context_engine, pdu->context_engine,
resp->context_engine_len);
strlcpy(resp->context_name, pdu->context_name,
sizeof(resp->context_name));
}
/*
* Execute a GET operation. The tree is rooted at the global 'root'.
* Build the response PDU on the fly. If the return code is SNMP_RET_ERR
@ -184,12 +207,7 @@ snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b,
memset(&context, 0, sizeof(context));
context.ctx.data = data;
memset(resp, 0, sizeof(*resp));
strcpy(resp->community, pdu->community);
resp->version = pdu->version;
resp->type = SNMP_PDU_RESPONSE;
resp->request_id = pdu->request_id;
resp->version = pdu->version;
snmp_pdu_create_response(pdu, resp);
if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
/* cannot even encode header - very bad */
@ -384,11 +402,7 @@ snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b,
memset(&context, 0, sizeof(context));
context.ctx.data = data;
memset(resp, 0, sizeof(*resp));
strcpy(resp->community, pdu->community);
resp->type = SNMP_PDU_RESPONSE;
resp->request_id = pdu->request_id;
resp->version = pdu->version;
snmp_pdu_create_response(pdu, resp);
if (snmp_pdu_encode_header(resp_b, resp))
return (SNMP_RET_IGN);
@ -440,12 +454,7 @@ snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b,
memset(&context, 0, sizeof(context));
context.ctx.data = data;
memset(resp, 0, sizeof(*resp));
strcpy(resp->community, pdu->community);
resp->version = pdu->version;
resp->type = SNMP_PDU_RESPONSE;
resp->request_id = pdu->request_id;
resp->version = pdu->version;
snmp_pdu_create_response(pdu, resp);
if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK)
/* cannot even encode header - very bad */
@ -652,11 +661,7 @@ snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b,
TAILQ_INIT(&context.dlist);
context.ctx.data = data;
memset(resp, 0, sizeof(*resp));
strcpy(resp->community, pdu->community);
resp->type = SNMP_PDU_RESPONSE;
resp->request_id = pdu->request_id;
resp->version = pdu->version;
snmp_pdu_create_response(pdu, resp);
if (snmp_pdu_encode_header(resp_b, resp))
return (SNMP_RET_IGN);
@ -951,16 +956,9 @@ snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b,
enum snmp_code code;
memset(&resp, 0, sizeof(resp));
/* Message sequence */
if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK)
return (SNMP_RET_IGN);
if (pdu_b->asn_len < len)
if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK)
return (SNMP_RET_IGN);
err = snmp_parse_message_hdr(pdu_b, &resp, &len);
if (ASN_ERR_STOPPED(err))
return (SNMP_RET_IGN);
if (pdu_b->asn_len < len)
return (SNMP_RET_IGN);
pdu_b->asn_len = len;

View File

@ -852,6 +852,9 @@ snmp_client_init(struct snmp_client *c)
strcpy(c->read_community, "public");
strcpy(c->write_community, "private");
c->security_model = SNMP_SECMODEL_USM;
strcpy(c->cname, "");
c->timeout.tv_sec = 3;
c->timeout.tv_usec = 0;
@ -864,6 +867,8 @@ snmp_client_init(struct snmp_client *c)
c->max_reqid = INT32_MAX;
c->min_reqid = 0;
c->next_reqid = 0;
c->engine.max_msg_size = 1500; /* XXX */
}
@ -1132,7 +1137,8 @@ snmp_close(void)
void
snmp_pdu_create(struct snmp_pdu *pdu, u_int op)
{
memset(pdu,0,sizeof(struct snmp_pdu));
memset(pdu, 0, sizeof(struct snmp_pdu));
if (op == SNMP_PDU_SET)
strlcpy(pdu->community, snmp_client.write_community,
sizeof(pdu->community));
@ -1145,6 +1151,33 @@ snmp_pdu_create(struct snmp_pdu *pdu, u_int op)
pdu->error_status = 0;
pdu->error_index = 0;
pdu->nbindings = 0;
if (snmp_client.version != SNMP_V3)
return;
pdu->identifier = ++snmp_client.identifier;
pdu->engine.max_msg_size = snmp_client.engine.max_msg_size;
pdu->flags = 0;
pdu->security_model = snmp_client.security_model;
if (snmp_client.security_model == SNMP_SECMODEL_USM)
snmp_pdu_init_secparams(pdu, &snmp_client.engine,
&snmp_client.user);
else
seterr(&snmp_client, "unknown security model");
if (snmp_client.clen > 0) {
memcpy(pdu->context_engine, snmp_client.cengine,
snmp_client.clen);
pdu->context_engine_len = snmp_client.clen;
} else {
memcpy(pdu->context_engine, snmp_client.engine.engine_id,
snmp_client.engine.engine_len);
pdu->context_engine_len = snmp_client.engine.engine_len;
}
strlcpy(pdu->context_name, snmp_client.cname,
sizeof(pdu->context_name));
}
/* add pairs of (struct asn_oid, enum snmp_syntax) to an existing pdu */
@ -1406,15 +1439,24 @@ snmp_receive_packet(struct snmp_pdu *pdu, struct timeval *tv)
abuf.asn_ptr = buf;
abuf.asn_len = ret;
memset(pdu, 0, sizeof(*pdu));
if (snmp_client.security_model == SNMP_SECMODEL_USM)
snmp_pdu_init_secparams(pdu, &snmp_client.engine,
&snmp_client.user);
if (SNMP_CODE_OK != (ret = snmp_pdu_decode(&abuf, pdu, &ip))) {
seterr(&snmp_client, "snmp_decode_pdu: failed %d", ret);
free(buf);
return (-1);
}
free(buf);
if (snmp_client.dump_pdus)
snmp_pdu_dump(pdu);
snmp_client.engine.engine_time = pdu->engine.engine_time;
snmp_client.engine.engine_boots = pdu->engine.engine_boots;
return (+1);
}
@ -1684,6 +1726,93 @@ snmp_dialog(struct snmp_v1_pdu *req, struct snmp_v1_pdu *resp)
return (-1);
}
int
snmp_discover_engine(char *passwd)
{
char cname[SNMP_ADM_STR32_SIZ];
enum snmp_authentication cap;
enum snmp_privacy cpp;
struct snmp_pdu req, resp;
if (snmp_client.version != SNMP_V3)
seterr(&snmp_client, "wrong version");
strlcpy(cname, snmp_client.user.sec_name, sizeof(cname));
cap = snmp_client.user.auth_proto;
cpp = snmp_client.user.priv_proto;
snmp_client.engine.engine_len = 0;
snmp_client.engine.engine_boots = 0;
snmp_client.engine.engine_time = 0;
snmp_client.user.auth_proto = SNMP_AUTH_NOAUTH;
snmp_client.user.priv_proto = SNMP_PRIV_NOPRIV;
memset(snmp_client.user.sec_name, 0, sizeof(snmp_client.user.sec_name));
snmp_pdu_create(&req, SNMP_PDU_GET);
if (snmp_dialog(&req, &resp) == -1)
return (-1);
if (resp.version != req.version) {
seterr(&snmp_client, "wrong version");
return (-1);
}
if (resp.error_status != SNMP_ERR_NOERROR) {
seterr(&snmp_client, "Error %d in responce", resp.error_status);
return (-1);
}
snmp_client.engine.engine_len = resp.engine.engine_len;
snmp_client.engine.max_msg_size = resp.engine.max_msg_size;
memcpy(snmp_client.engine.engine_id, resp.engine.engine_id,
resp.engine.engine_len);
strlcpy(snmp_client.user.sec_name, cname,
sizeof(snmp_client.user.sec_name));
snmp_client.user.auth_proto = cap;
snmp_client.user.priv_proto = cpp;
if (snmp_client.user.auth_proto == SNMP_AUTH_NOAUTH)
return (0);
if (passwd == NULL ||
snmp_passwd_to_keys(&snmp_client.user, passwd) != SNMP_CODE_OK ||
snmp_get_local_keys(&snmp_client.user, snmp_client.engine.engine_id,
snmp_client.engine.engine_len) != SNMP_CODE_OK)
return (-1);
if (resp.engine.engine_boots != 0)
snmp_client.engine.engine_boots = resp.engine.engine_boots;
if (resp.engine.engine_time != 0) {
snmp_client.engine.engine_time = resp.engine.engine_time;
return (0);
}
snmp_pdu_create(&req, SNMP_PDU_GET);
req.engine.engine_boots = 0;
req.engine.engine_time = 0;
if (snmp_dialog(&req, &resp) == -1)
return (-1);
if (resp.version != req.version) {
seterr(&snmp_client, "wrong version");
return (-1);
}
if (resp.error_status != SNMP_ERR_NOERROR) {
seterr(&snmp_client, "Error %d in responce", resp.error_status);
return (-1);
}
snmp_client.engine.engine_boots = resp.engine.engine_boots;
snmp_client.engine.engine_time = resp.engine.engine_time;
return (0);
}
int
snmp_client_set_host(struct snmp_client *cl, const char *h)
{

View File

@ -69,36 +69,47 @@ typedef void (*snmp_timeout_stop_f)(void *timeout_id);
* Client context.
*/
struct snmp_client {
enum snmp_version version;
int trans; /* which transport to use */
enum snmp_version version;
int trans; /* which transport to use */
/* these two are read-only for the application */
char *cport; /* port number as string */
char *chost; /* host name or IP address as string */
char *cport; /* port number as string */
char *chost; /* host name or IP address as string */
char read_community[SNMP_COMMUNITY_MAXLEN + 1];
char write_community[SNMP_COMMUNITY_MAXLEN + 1];
char read_community[SNMP_COMMUNITY_MAXLEN + 1];
char write_community[SNMP_COMMUNITY_MAXLEN + 1];
struct timeval timeout;
u_int retries;
/* SNMPv3 specific fields */
int32_t identifier;
int32_t security_model;
struct snmp_engine engine;
struct snmp_user user;
int dump_pdus;
/* SNMPv3 Access control - VACM*/
uint32_t clen;
uint8_t cengine[SNMP_ENGINE_ID_SIZ];
char cname[SNMP_CONTEXT_NAME_SIZ];
size_t txbuflen;
size_t rxbuflen;
struct timeval timeout;
u_int retries;
int fd;
int dump_pdus;
int32_t next_reqid;
int32_t max_reqid;
int32_t min_reqid;
size_t txbuflen;
size_t rxbuflen;
char error[SNMP_STRERROR_LEN];
int fd;
snmp_timeout_start_f timeout_start;
snmp_timeout_stop_f timeout_stop;
int32_t next_reqid;
int32_t max_reqid;
int32_t min_reqid;
char local_path[sizeof(SNMP_LOCAL_PATH)];
char error[SNMP_STRERROR_LEN];
snmp_timeout_start_f timeout_start;
snmp_timeout_stop_f timeout_stop;
char local_path[sizeof(SNMP_LOCAL_PATH)];
};
/* the global context */
@ -181,6 +192,9 @@ int snmp_table_fetch_async(const struct snmp_table *, void *,
/* send a request and wait for the response */
int snmp_dialog(struct snmp_pdu *_req, struct snmp_pdu *_resp);
/* discover an authorative snmpEngineId */
int snmp_discover_engine(char *);
/* parse a server specification */
int snmp_parse_server(struct snmp_client *, const char *);

View File

@ -34,12 +34,18 @@
enum asn_err snmp_binding_encode(struct asn_buf *, const struct snmp_value *);
enum snmp_code snmp_pdu_encode_header(struct asn_buf *, struct snmp_pdu *);
enum snmp_code snmp_fix_encoding(struct asn_buf *, const struct snmp_pdu *);
enum asn_err snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu,
asn_len_t *lenp);
enum snmp_code snmp_fix_encoding(struct asn_buf *, struct snmp_pdu *);
enum asn_err snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu,
asn_len_t *lenp);
void snmp_pdu_init_secparams(struct snmp_pdu *, struct snmp_engine *,
struct snmp_user *);
enum snmp_code snmp_pdu_calc_digest(struct asn_buf *, const struct snmp_pdu *,
uint8_t *);
enum snmp_code snmp_pdu_encrypt(struct asn_buf *, const struct snmp_pdu *);
enum snmp_code snmp_pdu_decrypt(struct asn_buf *, const struct snmp_pdu *);
#define DEFAULT_HOST "localhost"
#define DEFAULT_PORT "snmp"
#define DEFAULT_LOCAL "/var/run/snmp.sock"

132
contrib/bsnmp/snmp_usm/snmp_usm.3 Executable file
View File

@ -0,0 +1,132 @@
.\"-
.\" Copyright (C) 2010 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This documentation was written by Shteryana Sotirova Shopova under
.\" sponsorship from the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd September 9, 2010
.Dt SNMP_USM 3
.Os
.Sh NAME
.Nm snmp_usm
.Nd "user-based security module for
.Xr bsnmpd 1
.Sh LIBRARY
.Pq begemotSnmpdModulePath."usm" = "/usr/lib/snmp_usm.so"
.Sh DESCRIPTION
The
.Nm snmp_usm
module implements SNMPv3 User-Based Security Model MIB as defined in RFC 3414.
The module is used to manage the internal list of SNMPv3 USM active users in
.Nm bsnmpd .
The module must be loaded for
.Nm bsnmpd
to receive and process SNMPv3 USM PDUs correctly.
.Sh IMPLEMENTATION NOTES
A short description of the objects in the MIB follows.
.Bl -tag -width "XXXXXXXXX"
.It Va usmStats
The subtree contains statistics for the User-based Security Model PDU processing.
The statistics are reset each time the module is loaded.
.It Va usmUserSpinLock
An advisory lock used to coordinate several Command Generator Applications when
altering the SNMP USM users.
.It Va usmUserTable
The table contains all SNMP USM users configured in
.Nm bsnmpd.
The table contains the following objects
.Bl -tag -width ".It Va usmUserEngineID"
.It Va usmUserEngineID
An SNMP engine's administratively-unique identifier. Must be set to the same
Engine ID as
.Nm bsnmpd
so that the user will actually be allowed to communicate with the daemon.
The column is used as entry key and is not accessible for GET or SET operations.
.It Va usmUserName
The USM user name. The second entry key, again not accessible for GET or SET
operations.
.It Va usmUserSecurityName
The column has the exact same value as the
.Va usmUserName
column, however is accessible for GET operations.
.It Va usmUserCloneFrom
A GET on this column will return an empty OID. SET operations are currently not
supported.
.It Va usmUserAuthProtocol
The value of this column contains the OID corresponding to the authentication
protocol used by the USM user. The following protocols and their OIDs are known to
.Nm
module
.Bl -tag -width ".It Va NoAuthProtocol"
.It NoAuthProtocol 1.3.6.1.6.3.10.1.1.1
.It HMACMD5AuthProtocol 1.3.6.1.6.3.10.1.1.2
.It HMACSHAAuthProtocol 1.3.6.1.6.3.10.1.1.3
.El
.It Va usmUserAuthKeyChange , Va usmUserOwnAuthKeyChange
These columns may be used to change the user's authentication key.
.It Va usmUserPrivProtocol
The value of this column contains the OID corresponding to the privacy
protocol used by the USM user. The following protocols and their OIDs are known to
.Nm
module
.Bl -tag -width ".It Va NoPrivProtocol"
.It NoPrivProtocol 1.3.6.1.6.3.10.1.2.1
.It DESPrivProtoco 1.3.6.1.6.3.10.1.2.2
.It AesCfb128Protocol 1.3.6.1.6.3.10.1.2.4
.El
.It Va usmUserPrivKeyChange , Va usmUserOwnPrivKeyChange
These columns may be used to change the user's privacy key.
.It Va usmUserPublic
An arbitrary octet string that may be modified to confirm a SET operation on any
of the columns was successfull.
.It Va usmUserStorageType
This column always has either of two values. Entries created via
.Nm bsnmpd's
configuration file always have this column set to readOnly (5) and
it is not possible to modify those entries. Entries created by Command Generator
Applications always have this column set to volatile(2) and such entries are
lost when the module is restarted. A SET operation on this column is not
allowed.
.It Va usmUserStatus
This column is used to create new USM user entries or delete exsiting ones from
the table.
.El
.EL
.Sh FILES
.Bl -tag -width "XXXXXXXXX"
.It Pa /usr/share/snmp/defs/usm_tree.def
The description of the MIB tree implemented by
.Nm .
.El
.Sh SEE ALSO
.Xr bsnmpd 1 ,
.Xr gensnmptree 1 ,
.Xr snmpmod 3
.Sh STANDARDS
IETF RFC 3414
.Sh AUTHORS
.An Shteryana Shopova Aq syrinx@FreeBSD.org

614
contrib/bsnmp/snmp_usm/usm_snmp.c Executable file
View File

@ -0,0 +1,614 @@
/*-
* Copyright (c) 2010 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Shteryana Sotirova Shopova under
* sponsorship from the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/queue.h>
#include <sys/types.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <syslog.h>
#include "asn1.h"
#include "snmp.h"
#include "snmpmod.h"
#include "usm_tree.h"
#include "usm_oid.h"
static struct lmodule *usm_module;
/* For the registration. */
static const struct asn_oid oid_usm = OIDX_snmpUsmMIB;
static const struct asn_oid oid_usmNoAuthProtocol = OIDX_usmNoAuthProtocol;
static const struct asn_oid oid_usmHMACMD5AuthProtocol = \
OIDX_usmHMACMD5AuthProtocol;
static const struct asn_oid oid_usmHMACSHAAuthProtocol = \
OIDX_usmHMACSHAAuthProtocol;
static const struct asn_oid oid_usmNoPrivProtocol = OIDX_usmNoPrivProtocol;
static const struct asn_oid oid_usmDESPrivProtocol = OIDX_usmDESPrivProtocol;
static const struct asn_oid oid_usmAesCfb128Protocol = OIDX_usmAesCfb128Protocol;
static const struct asn_oid oid_usmUserSecurityName = OIDX_usmUserSecurityName;
/* The registration. */
static uint reg_usm;
static int32_t usm_lock;
static struct usm_user * usm_get_user(const struct asn_oid *, uint);
static struct usm_user * usm_get_next_user(const struct asn_oid *, uint);
static void usm_append_userindex(struct asn_oid *, uint,
const struct usm_user *);
static int usm_user_index_decode(const struct asn_oid *, uint, uint8_t *,
uint32_t *, char *);
int
op_usm_stats(struct snmp_context *ctx __unused, struct snmp_value *val,
uint32_t sub __unused, uint32_t iidx __unused, enum snmp_op op)
{
struct snmpd_usmstat *usmstats;
if (op == SNMP_OP_SET)
return (SNMP_ERR_NOT_WRITEABLE);
if ((usmstats = bsnmpd_get_usm_stats()) == NULL)
return (SNMP_ERR_GENERR);
if (op == SNMP_OP_GET) {
switch (val->var.subs[sub - 1]) {
case LEAF_usmStatsUnsupportedSecLevels:
val->v.uint32 = usmstats->unsupported_seclevels;
break;
case LEAF_usmStatsNotInTimeWindows:
val->v.uint32 = usmstats->not_in_time_windows;
break;
case LEAF_usmStatsUnknownUserNames:
val->v.uint32 = usmstats->unknown_users;
break;
case LEAF_usmStatsUnknownEngineIDs:
val->v.uint32 = usmstats->unknown_engine_ids;
break;
case LEAF_usmStatsWrongDigests:
val->v.uint32 = usmstats->wrong_digests;
break;
case LEAF_usmStatsDecryptionErrors:
val->v.uint32 = usmstats->decrypt_errors;
break;
default:
return (SNMP_ERR_NOSUCHNAME);
}
return (SNMP_ERR_NOERROR);
}
abort();
}
int
op_usm_lock(struct snmp_context *ctx __unused, struct snmp_value *val,
uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
if (val->var.subs[sub - 1] != LEAF_usmUserSpinLock)
return (SNMP_ERR_NOSUCHNAME);
switch (op) {
case SNMP_OP_GET:
if (++usm_lock == INT32_MAX)
usm_lock = 0;
val->v.integer = usm_lock;
break;
case SNMP_OP_GETNEXT:
abort();
case SNMP_OP_SET:
if (val->v.integer != usm_lock)
return (SNMP_ERR_INCONS_VALUE);
break;
case SNMP_OP_ROLLBACK:
/* FALLTHROUGH */
case SNMP_OP_COMMIT:
break;
}
return (SNMP_ERR_NOERROR);
}
int
op_usm_users(struct snmp_context *ctx, struct snmp_value *val,
uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
{
uint32_t elen;
struct usm_user *uuser, *clone;
char uname[SNMP_ADM_STR32_SIZ];
uint8_t eid[SNMP_ENGINE_ID_SIZ];
switch (op) {
case SNMP_OP_GET:
if ((uuser = usm_get_user(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
break;
case SNMP_OP_GETNEXT:
if ((uuser = usm_get_next_user(&val->var, sub)) == NULL)
return (SNMP_ERR_NOSUCHNAME);
usm_append_userindex(&val->var, sub, uuser);
break;
case SNMP_OP_SET:
if ((uuser = usm_get_user(&val->var, sub)) == NULL &&
val->var.subs[sub - 1] != LEAF_usmUserStatus &&
val->var.subs[sub - 1] != LEAF_usmUserCloneFrom)
return (SNMP_ERR_NOSUCHNAME);
if (community != COMM_INITIALIZE &&
uuser->type == StorageType_readOnly)
return (SNMP_ERR_NOT_WRITEABLE);
switch (val->var.subs[sub - 1]) {
case LEAF_usmUserSecurityName:
return (SNMP_ERR_NOT_WRITEABLE);
case LEAF_usmUserCloneFrom:
if (uuser != NULL || usm_user_index_decode(&val->var,
sub, eid, &elen, uname) < 0 ||
!(asn_is_suboid(&oid_usmUserSecurityName, &val->v.oid)))
return (SNMP_ERR_WRONG_VALUE);
if ((clone = usm_get_user(&val->v.oid, sub)) == NULL)
return (SNMP_ERR_INCONS_VALUE);
if ((uuser = usm_new_user(eid, elen, uname)) == NULL)
return (SNMP_ERR_GENERR);
uuser->status = RowStatus_notReady;
if (community != COMM_INITIALIZE)
uuser->type = StorageType_volatile;
else
uuser->type = StorageType_readOnly;
uuser->suser.auth_proto = clone->suser.auth_proto;
uuser->suser.priv_proto = clone->suser.priv_proto;
memcpy(uuser->suser.auth_key, clone->suser.auth_key,
sizeof(uuser->suser.auth_key));
memcpy(uuser->suser.priv_key, clone->suser.priv_key,
sizeof(uuser->suser.priv_key));
ctx->scratch->int1 = RowStatus_createAndWait;
break;
case LEAF_usmUserAuthProtocol:
ctx->scratch->int1 = uuser->suser.auth_proto;
if (asn_compare_oid(&oid_usmNoAuthProtocol,
&val->v.oid) == 0)
uuser->suser.auth_proto = SNMP_AUTH_NOAUTH;
else if (asn_compare_oid(&oid_usmHMACMD5AuthProtocol,
&val->v.oid) == 0)
uuser->suser.auth_proto = SNMP_AUTH_HMAC_MD5;
else if (asn_compare_oid(&oid_usmHMACSHAAuthProtocol,
&val->v.oid) == 0)
uuser->suser.auth_proto = SNMP_AUTH_HMAC_SHA;
else
return (SNMP_ERR_WRONG_VALUE);
break;
case LEAF_usmUserAuthKeyChange:
case LEAF_usmUserOwnAuthKeyChange:
if (val->var.subs[sub - 1] ==
LEAF_usmUserOwnAuthKeyChange &&
(usm_user == NULL || strcmp(uuser->suser.sec_name,
usm_user->suser.sec_name) != 0))
return (SNMP_ERR_NO_ACCESS);
if (val->v.octetstring.len > SNMP_AUTH_KEY_SIZ)
return (SNMP_ERR_INCONS_VALUE);
ctx->scratch->ptr1 = malloc(SNMP_AUTH_KEY_SIZ);
if (ctx->scratch->ptr1 == NULL)
return (SNMP_ERR_GENERR);
memcpy(ctx->scratch->ptr1, uuser->suser.auth_key,
SNMP_AUTH_KEY_SIZ);
memcpy(uuser->suser.auth_key, val->v.octetstring.octets,
val->v.octetstring.len);
break;
case LEAF_usmUserPrivProtocol:
ctx->scratch->int1 = uuser->suser.priv_proto;
if (asn_compare_oid(&oid_usmNoPrivProtocol,
&val->v.oid) == 0)
uuser->suser.priv_proto = SNMP_PRIV_NOPRIV;
else if (asn_compare_oid(&oid_usmDESPrivProtocol,
&val->v.oid) == 0)
uuser->suser.priv_proto = SNMP_PRIV_DES;
else if (asn_compare_oid(&oid_usmAesCfb128Protocol,
&val->v.oid) == 0)
uuser->suser.priv_proto = SNMP_PRIV_AES;
else
return (SNMP_ERR_WRONG_VALUE);
break;
case LEAF_usmUserPrivKeyChange:
case LEAF_usmUserOwnPrivKeyChange:
if (val->var.subs[sub - 1] ==
LEAF_usmUserOwnPrivKeyChange &&
(usm_user == NULL || strcmp(uuser->suser.sec_name,
usm_user->suser.sec_name) != 0))
return (SNMP_ERR_NO_ACCESS);
if (val->v.octetstring.len > SNMP_PRIV_KEY_SIZ)
return (SNMP_ERR_INCONS_VALUE);
ctx->scratch->ptr1 = malloc(SNMP_PRIV_KEY_SIZ);
if (ctx->scratch->ptr1 == NULL)
return (SNMP_ERR_GENERR);
memcpy(ctx->scratch->ptr1, uuser->suser.priv_key,
SNMP_PRIV_KEY_SIZ);
memcpy(uuser->suser.priv_key, val->v.octetstring.octets,
val->v.octetstring.len);
break;
case LEAF_usmUserPublic:
if (val->v.octetstring.len > SNMP_ADM_STR32_SIZ)
return (SNMP_ERR_INCONS_VALUE);
if (uuser->user_public_len > 0) {
ctx->scratch->ptr2 =
malloc(uuser->user_public_len);
if (ctx->scratch->ptr2 == NULL)
return (SNMP_ERR_GENERR);
memcpy(ctx->scratch->ptr2, uuser->user_public,
uuser->user_public_len);
ctx->scratch->int2 = uuser->user_public_len;
}
if (val->v.octetstring.len > 0) {
memcpy(uuser->user_public,
val->v.octetstring.octets,
val->v.octetstring.len);
uuser->user_public_len = val->v.octetstring.len;
} else {
memset(uuser->user_public, 0,
SNMP_ADM_STR32_SIZ);
uuser->user_public_len = 0;
}
break;
case LEAF_usmUserStorageType:
return (SNMP_ERR_INCONS_VALUE);
case LEAF_usmUserStatus:
if (uuser == NULL) {
if (val->v.integer != RowStatus_createAndWait ||
usm_user_index_decode(&val->var, sub, eid,
&elen, uname) < 0)
return (SNMP_ERR_INCONS_VALUE);
uuser = usm_new_user(eid, elen, uname);
if (uuser == NULL)
return (SNMP_ERR_GENERR);
uuser->status = RowStatus_notReady;
if (community != COMM_INITIALIZE)
uuser->type = StorageType_volatile;
else
uuser->type = StorageType_readOnly;
} else if (val->v.integer != RowStatus_active &&
val->v.integer != RowStatus_destroy)
return (SNMP_ERR_INCONS_VALUE);
uuser->status = val->v.integer;
break;
}
return (SNMP_ERR_NOERROR);
case SNMP_OP_COMMIT:
switch (val->var.subs[sub - 1]) {
case LEAF_usmUserAuthKeyChange:
case LEAF_usmUserOwnAuthKeyChange:
case LEAF_usmUserPrivKeyChange:
case LEAF_usmUserOwnPrivKeyChange:
free(ctx->scratch->ptr1);
break;
case LEAF_usmUserPublic:
if (ctx->scratch->ptr2 != NULL)
free(ctx->scratch->ptr2);
break;
case LEAF_usmUserStatus:
if (val->v.integer != RowStatus_destroy)
break;
if ((uuser = usm_get_user(&val->var, sub)) == NULL)
return (SNMP_ERR_GENERR);
usm_delete_user(uuser);
break;
default:
break;
}
return (SNMP_ERR_NOERROR);
case SNMP_OP_ROLLBACK:
if ((uuser = usm_get_user(&val->var, sub)) == NULL)
return (SNMP_ERR_GENERR);
switch (val->var.subs[sub - 1]) {
case LEAF_usmUserAuthProtocol:
uuser->suser.auth_proto = ctx->scratch->int1;
break;
case LEAF_usmUserAuthKeyChange:
case LEAF_usmUserOwnAuthKeyChange:
memcpy(uuser->suser.auth_key, ctx->scratch->ptr1,
SNMP_AUTH_KEY_SIZ);
free(ctx->scratch->ptr1);
break;
case LEAF_usmUserPrivProtocol:
uuser->suser.priv_proto = ctx->scratch->int1;
break;
case LEAF_usmUserPrivKeyChange:
case LEAF_usmUserOwnPrivKeyChange:
memcpy(uuser->suser.priv_key, ctx->scratch->ptr1,
SNMP_AUTH_KEY_SIZ);
free(ctx->scratch->ptr1);
break;
case LEAF_usmUserPublic:
if (ctx->scratch->ptr2 != NULL) {
memcpy(uuser->user_public, ctx->scratch->ptr2,
ctx->scratch->int2);
uuser->user_public_len = ctx->scratch->int2;
free(ctx->scratch->ptr2);
} else {
memset(uuser->user_public, 0,
SNMP_ADM_STR32_SIZ);
uuser->user_public_len = 0;
}
break;
case LEAF_usmUserCloneFrom:
case LEAF_usmUserStatus:
if (ctx->scratch->int1 == RowStatus_createAndWait)
usm_delete_user(uuser);
break;
default:
break;
}
return (SNMP_ERR_NOERROR);
default:
abort();
}
switch (val->var.subs[sub - 1]) {
case LEAF_usmUserSecurityName:
return (string_get(val, uuser->suser.sec_name, -1));
case LEAF_usmUserCloneFrom:
memcpy(&val->v.oid, &oid_zeroDotZero, sizeof(oid_zeroDotZero));
break;
case LEAF_usmUserAuthProtocol:
switch (uuser->suser.auth_proto) {
case SNMP_AUTH_HMAC_MD5:
memcpy(&val->v.oid, &oid_usmHMACMD5AuthProtocol,
sizeof(oid_usmHMACMD5AuthProtocol));
break;
case SNMP_AUTH_HMAC_SHA:
memcpy(&val->v.oid, &oid_usmHMACSHAAuthProtocol,
sizeof(oid_usmHMACSHAAuthProtocol));
break;
default:
memcpy(&val->v.oid, &oid_usmNoAuthProtocol,
sizeof(oid_usmNoAuthProtocol));
break;
}
break;
case LEAF_usmUserAuthKeyChange:
case LEAF_usmUserOwnAuthKeyChange:
return (string_get(val, (char *)uuser->suser.auth_key, 0));
case LEAF_usmUserPrivProtocol:
switch (uuser->suser.priv_proto) {
case SNMP_PRIV_DES:
memcpy(&val->v.oid, &oid_usmDESPrivProtocol,
sizeof(oid_usmDESPrivProtocol));
break;
case SNMP_PRIV_AES:
memcpy(&val->v.oid, &oid_usmAesCfb128Protocol,
sizeof(oid_usmAesCfb128Protocol));
break;
default:
memcpy(&val->v.oid, &oid_usmNoPrivProtocol,
sizeof(oid_usmNoPrivProtocol));
break;
}
break;
case LEAF_usmUserPrivKeyChange:
case LEAF_usmUserOwnPrivKeyChange:
return (string_get(val, (char *)uuser->suser.priv_key, 0));
case LEAF_usmUserPublic:
return (string_get(val, uuser->user_public,
uuser->user_public_len));
case LEAF_usmUserStorageType:
val->v.integer = uuser->type;
break;
case LEAF_usmUserStatus:
val->v.integer = uuser->status;
break;
}
return (SNMP_ERR_NOERROR);
}
static int
usm_user_index_decode(const struct asn_oid *oid, uint sub, uint8_t *engine,
uint32_t *elen, char *uname)
{
uint32_t i, nlen;
int uname_off;
if (oid->subs[sub] > SNMP_ENGINE_ID_SIZ)
return (-1);
for (i = 0; i < oid->subs[sub]; i++)
engine[i] = oid->subs[sub + i + 1];
*elen = i;
uname_off = sub + oid->subs[sub] + 1;
if ((nlen = oid->subs[uname_off]) >= SNMP_ADM_STR32_SIZ)
return (-1);
for (i = 0; i < nlen; i++)
uname[i] = oid->subs[uname_off + i + 1];
uname[nlen] = '\0';
return (0);
}
static void
usm_append_userindex(struct asn_oid *oid, uint sub,
const struct usm_user *uuser)
{
uint32_t i;
oid->len = sub + uuser->user_engine_len + strlen(uuser->suser.sec_name);
oid->len += 2;
oid->subs[sub] = uuser->user_engine_len;
for (i = 1; i < uuser->user_engine_len + 1; i++)
oid->subs[sub + i] = uuser->user_engine_id[i - 1];
sub += uuser->user_engine_len + 1;
oid->subs[sub] = strlen(uuser->suser.sec_name);
for (i = 1; i <= oid->subs[sub]; i++)
oid->subs[sub + i] = uuser->suser.sec_name[i - 1];
}
static struct usm_user *
usm_get_user(const struct asn_oid *oid, uint sub)
{
uint32_t enginelen;
char username[SNMP_ADM_STR32_SIZ];
uint8_t engineid[SNMP_ENGINE_ID_SIZ];
if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0)
return (NULL);
return (usm_find_user(engineid, enginelen, username));
}
static struct usm_user *
usm_get_next_user(const struct asn_oid *oid, uint sub)
{
uint32_t enginelen;
char username[SNMP_ADM_STR32_SIZ];
uint8_t engineid[SNMP_ENGINE_ID_SIZ];
struct usm_user *uuser;
if (oid->len - sub == 0)
return (usm_first_user());
if (usm_user_index_decode(oid, sub, engineid, &enginelen, username) < 0)
return (NULL);
if ((uuser = usm_find_user(engineid, enginelen, username)) != NULL)
return (usm_next_user(uuser));
return (NULL);
}
/*
* USM snmp module initialization hook.
* Returns 0 on success, < 0 on error.
*/
static int
usm_init(struct lmodule * mod, int argc __unused, char *argv[] __unused)
{
usm_module = mod;
usm_lock = random();
bsnmpd_reset_usm_stats();
return (0);
}
/*
* USM snmp module finalization hook.
*/
static int
usm_fini(void)
{
usm_flush_users();
or_unregister(reg_usm);
return (0);
}
/*
* USM snmp module start operation.
*/
static void
usm_start(void)
{
reg_usm = or_register(&oid_usm,
"The MIB module for managing SNMP User-Based Security Model.",
usm_module);
}
static void
usm_dump(void)
{
struct usm_user *uuser;
struct snmpd_usmstat *usmstats;
const char *const authstr[] = {
"noauth",
"md5",
"sha",
NULL
};
const char *const privstr[] = {
"nopriv",
"des",
"aes",
NULL
};
if ((usmstats = bsnmpd_get_usm_stats()) != NULL) {
syslog(LOG_ERR, "UnsupportedSecLevels\t\t%u",
usmstats->unsupported_seclevels);
syslog(LOG_ERR, "NotInTimeWindows\t\t%u",
usmstats->not_in_time_windows);
syslog(LOG_ERR, "UnknownUserNames\t\t%u",
usmstats->unknown_users);
syslog(LOG_ERR, "UnknownEngineIDs\t\t%u",
usmstats->unknown_engine_ids);
syslog(LOG_ERR, "WrongDigests\t\t%u",
usmstats->wrong_digests);
syslog(LOG_ERR, "DecryptionErrors\t\t%u",
usmstats->decrypt_errors);
}
syslog(LOG_ERR, "USM users");
for (uuser = usm_first_user(); uuser != NULL;
(uuser = usm_next_user(uuser)))
syslog(LOG_ERR, "user %s\t\t%s, %s", uuser->suser.sec_name,
authstr[uuser->suser.auth_proto],
privstr[uuser->suser.priv_proto]);
}
const char usm_comment[] = \
"This module implements SNMP User-based Security Model defined in RFC 3414.";
const struct snmp_module config = {
.comment = usm_comment,
.init = usm_init,
.fini = usm_fini,
.start = usm_start,
.tree = usm_ctree,
.dump = usm_dump,
.tree_size = usm_CTREE_SIZE,
};

View File

@ -0,0 +1,109 @@
#-
# Copyright (C) 2010 The FreeBSD Foundation
# All rights reserved.
#
# This software was developed by Shteryana Sotirova Shopova under
# sponsorship from the FreeBSD Foundation.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
typedef StorageType ENUM (
1 other
2 volatile
3 nonVolatile
4 permanent
5 readOnly
)
typedef RowStatus ENUM (
1 active
2 notInService
3 notReady
4 createAndGo
5 createAndWait
6 destroy
)
(1 internet
(6 snmpV2
(3 snmpModules
(10 snmpFrameworkMIB
(1 snmpFrameworkAdmin
(1 snmpAuthProtocols
(1 usmNoAuthProtocol
)
(2 usmHMACMD5AuthProtocol
)
(3 usmHMACSHAAuthProtocol
)
)
(2 snmpPrivProtocols
(1 usmNoPrivProtocol
)
(2 usmDESPrivProtocol
)
(4 usmAesCfb128Protocol
)
)
)
)
(15 snmpUsmMIB
(1 usmMIBObjects
(1 usmStats
(1 usmStatsUnsupportedSecLevels COUNTER op_usm_stats GET)
(2 usmStatsNotInTimeWindows COUNTER op_usm_stats GET)
(3 usmStatsUnknownUserNames COUNTER op_usm_stats GET)
(4 usmStatsUnknownEngineIDs COUNTER op_usm_stats GET)
(5 usmStatsWrongDigests COUNTER op_usm_stats GET)
(6 usmStatsDecryptionErrors COUNTER op_usm_stats GET)
)
(2 usmUser
(1 usmUserSpinLock INTEGER op_usm_lock GET SET)
(2 usmUserTable
(1 usmUserEntry : OCTETSTRING | SnmpEngineID OCTETSTRING op_usm_users
(1 usmUserEngineID OCTETSTRING | SnmpEngineID)
(2 usmUserName OCTETSTRING)
(3 usmUserSecurityName OCTETSTRING | SnmpAdminString GET)
(4 usmUserCloneFrom OID GET SET)
(5 usmUserAuthProtocol OID GET SET)
(6 usmUserAuthKeyChange OCTETSTRING | KeyChange GET SET)
(7 usmUserOwnAuthKeyChange OCTETSTRING | KeyChange GET SET)
(8 usmUserPrivProtocol OID GET SET)
(9 usmUserPrivKeyChange OCTETSTRING | KeyChange GET SET)
(10 usmUserOwnPrivKeyChange OCTETSTRING | KeyChange GET SET)
(11 usmUserPublic OCTETSTRING GET SET)
(12 usmUserStorageType StorageType GET SET)
(13 usmUserStatus RowStatus GET SET)
)
)
)
)
)
(20 snmpUsmAesMIB
)
)
)
)

View File

@ -0,0 +1,94 @@
.\"-
.\" Copyright (C) 2010 The FreeBSD Foundation
.\" All rights reserved.
.\"
.\" This documentation was written by Shteryana Sotirova Shopova under
.\" sponsorship from the FreeBSD Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd October 7, 2010
.Dt SNMP_VACM 3
.Os
.Sh NAME
.Nm snmp_vacm
.Nd "View-based Access Control module for
.Xr bsnmpd 1
.Sh LIBRARY
.Pq begemotSnmpdModulePath."vacm" = "/usr/lib/snmp_vacm.so"
.Sh DESCRIPTION
The
.Nm snmp_vacm
module implements SNMPv3 View-based Access Control Model MIB as defined in
RFC 3415. The module is used to manage the internal lists of SNMPv1, v2c and
v3 user names and groups and their access rights to fetch or modify the values
of the MIB objects maintained by
.Nm bsnmpd
and the modules loaded in the daemon.
The module must be loaded for
.Nm bsnmpd
to implement proper view-based access control. If the module is not loaded,
access is granted to all configured SNMPv1 & SNMPv2c communities and SNMPv3
USM users.
.Sh IMPLEMENTATION NOTES
An entry in any table implemented by this MIB may be created by setting the
relevant RowStatus column to createAndGo (4) - in fact, any other value for
those columns in a SET operation will cause an error. When an entry is created,
any of its columns that is not used as index, is set to the default value as
specified in the SNMP-VIEW-BASED-ACM-MIB. All entries maintained by the module
are persistent after reboot if created via
.Nm bsnmpd 's
config file, otherwise entries created via SNMP are lost after reboot.
A short description of the objects in the MIB follows.
.Bl -tag -width "XXXXXXXXX"
.It Va vacmContextTable
A read-only table that consists of a list of SNMP contexts available in
.Nm bsnmpd .
.It Va vacmSecurityToGroupTable
The table contains a list of SNMPv1, v2c and v3 user names and the groups
they belong to.
.It Va vacmAccessTable
The table contains a list of SNMP contexts to groups mappings and respectively
the names of the SNMP views under those contexts, to which users in the group
are granted read-only, read-write access or receive notifications for the
objects under the subtree in the relevant view.
.It Va vacmViewTreeFamilyTable
The table contains a list of SNMP views, i.e. entries specifying the OID of a
MIB subtree and whether access to the objects under this subtree is to be
allowed or forbiden.
.El
.Sh FILES
.Bl -tag -width "XXXXXXXXX"
.It Pa /usr/share/snmp/defs/vacm_tree.def
The description of the MIB tree implemented by
.Nm .
.El
.Sh SEE ALSO
.Xr bsnmpd 1 ,
.Xr gensnmptree 1 ,
.Xr snmpmod 3
.Sh STANDARDS
IETF RFC 3415
.Sh AUTHORS
.An Shteryana Shopova Aq syrinx@FreeBSD.org

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
#-
# Copyright (C) 2010 The FreeBSD Foundation
# All rights reserved.
#
# This software was developed by Shteryana Sotirova Shopova under
# sponsorship from the FreeBSD Foundation.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
typedef StorageType ENUM (
1 other
2 volatile
3 nonVolatile
4 permanent
5 readOnly
)
typedef RowStatus ENUM (
1 active
2 notInService
3 notReady
4 createAndGo
5 createAndWait
6 destroy
)
(1 internet
(6 snmpV2
(3 snmpModules
(16 snmpVacmMIB
(1 vacmMIBObjects
(1 vacmContextTable
(1 vacmContextEntry : OCTETSTRING op_vacm_context
(1 vacmContextName OCTETSTRING GET)
)
)
(2 vacmSecurityToGroupTable
(1 vacmSecurityToGroupEntry : INTEGER OCTETSTRING op_vacm_security_to_group
(1 vacmSecurityModel INTEGER)
(2 vacmSecurityName OCTETSTRING)
(3 vacmGroupName OCTETSTRING GET SET)
(4 vacmSecurityToGroupStorageType StorageType GET SET)
(5 vacmSecurityToGroupStatus RowStatus GET SET)
)
)
(4 vacmAccessTable
(1 vacmAccessEntry : OCTETSTRING OCTETSTRING INTEGER ENUM ( 1 noAuthNoPriv 2 authNoPriv 3 authPriv ) op_vacm_access
(1 vacmAccessContextPrefix OCTETSTRING)
(2 vacmAccessSecurityModel INTEGER)
(3 vacmAccessSecurityLevel ENUM ( 1 noAuthNoPriv 2 authNoPriv 3 authPriv ))
(4 vacmAccessContextMatch ENUM ( 1 exact 2 prefix ) GET SET)
(5 vacmAccessReadViewName OCTETSTRING GET SET)
(6 vacmAccessWriteViewName OCTETSTRING GET SET)
(7 vacmAccessNotifyViewName OCTETSTRING GET SET)
(8 vacmAccessStorageType StorageType GET SET)
(9 vacmAccessStatus RowStatus GET SET)
)
)
(5 vacmMIBViews
(1 vacmViewSpinLock INTEGER op_vacm_view_lock GET SET)
(2 vacmViewTreeFamilyTable
(1 vacmViewTreeFamilyEntry : OCTETSTRING OID op_vacm_view
(1 vacmViewTreeFamilyViewName OCTETSTRING)
(2 vacmViewTreeFamilySubtree OID)
(3 vacmViewTreeFamilyMask OCTETSTRING GET SET)
(4 vacmViewTreeFamilyType ENUM ( 1 included 2 excluded ) GET SET)
(5 vacmViewTreeFamilyStorageType StorageType GET SET)
(6 vacmViewTreeFamilyStatus RowStatus GET SET)
)
)
)
)
(2 vacmMIBConformance
(1 vacmMIBCompliances
)
(2 vacmMIBGroups
)
)
)
)
)
)

View File

@ -139,7 +139,8 @@ begemotSnmpdVersionEnable OBJECT-TYPE
bits are defined:
0x00000001 - SNMPv1
0x00000002 - SNMPv2c"
0x00000002 - SNMPv2c
0x00000004 - SNMPv3"
DEFVAL { 3 }
::= { begemotSnmpdConfig 5 }

View File

@ -34,6 +34,7 @@
* Variable access for SNMPd
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <sys/un.h>
#include <sys/utsname.h>
@ -42,6 +43,7 @@
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <syslog.h>
#include "snmpmod.h"
@ -162,7 +164,83 @@ init_actvals(void)
return (0);
}
/*
* Initialize global variables of the snmpEngine group.
*/
int
init_snmpd_engine(void)
{
char *hostid;
snmpd_engine.engine_boots = 1;
snmpd_engine.engine_time = 1;
snmpd_engine.max_msg_size = 1500; /* XXX */
snmpd_engine.engine_id[0] = ((OID_freeBSD & 0xff000000) >> 24) | 0x80;
snmpd_engine.engine_id[1] = (OID_freeBSD & 0xff0000) >> 16;
snmpd_engine.engine_id[2] = (OID_freeBSD & 0xff00) >> 8;
snmpd_engine.engine_id[3] = OID_freeBSD & 0xff;
snmpd_engine.engine_id[4] = 128;
snmpd_engine.engine_len = 5;
if ((hostid = act_getkernint(KERN_HOSTID)) == NULL)
return (-1);
if (strlen(hostid) > SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len) {
memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len,
hostid, SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len);
snmpd_engine.engine_len = SNMP_ENGINE_ID_SIZ;
} else {
memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len,
hostid, strlen(hostid));
snmpd_engine.engine_len += strlen(hostid);
}
free(hostid);
return (0);
}
int
set_snmpd_engine(void)
{
FILE *fp;
uint32_t i;
uint8_t *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2];
uint8_t myengine[2 * SNMP_ENGINE_ID_SIZ + 2];
if (engine_file[0] == '\0')
return (-1);
cptr = myengine;
for (i = 0; i < snmpd_engine.engine_len; i++)
cptr += sprintf(cptr, "%.2x", snmpd_engine.engine_id[i]);
*cptr++ = '\n';
*cptr++ = '\0';
if ((fp = fopen(engine_file, "r+")) != NULL) {
if (fgets(engine, sizeof(engine) - 1, fp) == NULL ||
fscanf(fp, "%u", &snmpd_engine.engine_boots) <= 0) {
fclose(fp);
goto save_boots;
}
fclose(fp);
if (strcmp(myengine, engine) != 0)
snmpd_engine.engine_boots = 1;
else
snmpd_engine.engine_boots++;
} else if (errno != ENOENT)
return (-1);
save_boots:
if ((fp = fopen(engine_file, "w+")) == NULL)
return (-1);
fprintf(fp, "%s%u\n", myengine, snmpd_engine.engine_boots);
fclose(fp);
return (0);
}
/*************************************************************
*
@ -979,6 +1057,103 @@ op_snmp_set(struct snmp_context *ctx __unused, struct snmp_value *value,
abort();
}
/*
* SNMP Engine
*/
int
op_snmp_engine(struct snmp_context *ctx __unused, struct snmp_value *value,
u_int sub, u_int iidx __unused, enum snmp_op op)
{
asn_subid_t which = value->var.subs[sub - 1];
switch (op) {
case SNMP_OP_GETNEXT:
abort();
case SNMP_OP_GET:
break;
case SNMP_OP_SET:
if (community != COMM_INITIALIZE)
return (SNMP_ERR_NOT_WRITEABLE);
switch (which) {
case LEAF_snmpEngineID:
if (value->v.octetstring.len > SNMP_ENGINE_ID_SIZ)
return (SNMP_ERR_WRONG_VALUE);
ctx->scratch->ptr1 = malloc(snmpd_engine.engine_len);
if (ctx->scratch->ptr1 == NULL)
return (SNMP_ERR_GENERR);
memcpy(ctx->scratch->ptr1, snmpd_engine.engine_id,
snmpd_engine.engine_len);
ctx->scratch->int1 = snmpd_engine.engine_len;
snmpd_engine.engine_len = value->v.octetstring.len;
memcpy(snmpd_engine.engine_id,
value->v.octetstring.octets,
value->v.octetstring.len);
break;
case LEAF_snmpEngineMaxMessageSize:
ctx->scratch->int1 = snmpd_engine.max_msg_size;
snmpd_engine.max_msg_size = value->v.integer;
break;
default:
return (SNMP_ERR_NOT_WRITEABLE);
}
return (SNMP_ERR_NOERROR);
case SNMP_OP_ROLLBACK:
switch (which) {
case LEAF_snmpEngineID:
snmpd_engine.engine_len = ctx->scratch->int1;
memcpy(snmpd_engine.engine_id, ctx->scratch->ptr1,
snmpd_engine.engine_len);
free(ctx->scratch->ptr1);
break;
case LEAF_snmpEngineMaxMessageSize:
snmpd_engine.max_msg_size = ctx->scratch->int1;
break;
default:
abort();
}
return (SNMP_ERR_NOERROR);
case SNMP_OP_COMMIT:
if (which == LEAF_snmpEngineID) {
if (set_snmpd_engine() < 0) {
snmpd_engine.engine_len = ctx->scratch->int1;
memcpy(snmpd_engine.engine_id,
ctx->scratch->ptr1, ctx->scratch->int1);
}
free(ctx->scratch->ptr1);
}
return (SNMP_ERR_NOERROR);
}
switch (which) {
case LEAF_snmpEngineID:
return (string_get(value, snmpd_engine.engine_id,
snmpd_engine.engine_len));
case LEAF_snmpEngineBoots:
value->v.integer = snmpd_engine.engine_boots;
break;
case LEAF_snmpEngineTime:
snmpd_engine.engine_time = (get_ticks() - start_tick) / 100ULL;
value->v.integer = snmpd_engine.engine_time;
break;
case LEAF_snmpEngineMaxMessageSize:
value->v.integer = snmpd_engine.max_msg_size;
break;
default:
return (SNMP_ERR_NOSUCHNAME);
}
return (SNMP_ERR_NOERROR);
}
/*
* Transport table
*/

View File

@ -31,7 +31,7 @@
.\"
.\" $Begemot: bsnmp/snmpd/bsnmpd.1,v 1.12 2006/02/27 09:50:03 brandt_h Exp $
.\"
.Dd October 23, 2010
.Dd September 9, 2010
.Dt BSNMPD 1
.Os
.Sh NAME
@ -42,6 +42,7 @@
.Op Fl dh
.Op Fl c Ar file
.Op Fl D Ar options
.Op Fl e Ar file
.Op Fl I Ar paths
.Op Fl l Ar prefix
.Op Fl m Ar variable Ns Op = Ns Ar value
@ -68,9 +69,11 @@ Use
.Ar file
as configuration file instead of the standard one.
.It Fl D Ar options
Debugging options are specified as a comma separated string.
Debugging options are specified with a
.Fl o
flag followed by a comma separated string of options.
The following options are available.
.Bl -tag -width "trace=level"
.Bl -tag -width ".It Cm trace Ns Cm = Ns Cm level"
.It Cm dump
Dump all sent and received PDUs to the terminal.
.It Cm events
@ -80,8 +83,11 @@ to 10.
.It Cm trace Ns Cm = Ns Cm level
Set the snmp library trace flag to the specified
value.
The value can be specified in the usual C-syntax for numbers.
.El
The value can be specified in the usual C-syntax for numbers.
.It Fl e Ar file
Specify an alternate file where the agent's engine id and number of boots
are saved.
.It Fl I Ar paths
Specify a colon separated list of directories to search for configuration
include files.
@ -246,6 +252,8 @@ Default configuration file, where the default
.Aq prefix
is
.Dq snmpd .
.It Pa /var/ Ns Ao Ar prefix Ac Ns \&.engine
Default engine id file.
.It Pa /var/run/ Ns Ao Ar prefix Ac Ns \&.pid
Default pid file.
.It Pa /etc:/usr/etc/:/usr/local/etc

View File

@ -31,6 +31,7 @@
* Parse configuration file.
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
@ -810,6 +811,7 @@ parse_oid(const char *varname, struct asn_oid *oid)
struct snmp_node *node;
u_int i;
u_char ip[4];
struct asn_oid str_oid;
for (node = tree; node < &tree[tree_size]; node++)
if (strcmp(varname, node->name) == 0)
@ -824,7 +826,19 @@ parse_oid(const char *varname, struct asn_oid *oid)
report("subid too large %#"QUADXFMT, numval);
if (oid->len == ASN_MAXOIDLEN)
report("index too long");
oid->subs[oid->len++] = numval;
if (gettoken() != ':')
oid->subs[oid->len++] = numval;
else {
str_oid.len = 0;
str_oid.subs[str_oid.len++] = numval;
while (gettoken() == TOK_NUM) {
str_oid.subs[str_oid.len++] = numval;
if (gettoken() != ':')
break;
}
oid->subs[oid->len++] = str_oid.len;
asn_append_oid(oid, &str_oid);
}
} else if (token == TOK_STR) {
if (strvallen + oid->len + 1 > ASN_MAXOIDLEN)
@ -832,6 +846,7 @@ parse_oid(const char *varname, struct asn_oid *oid)
oid->subs[oid->len++] = strvallen;
for (i = 0; i < strvallen; i++)
oid->subs[oid->len++] = strval[i];
gettoken();
} else if (token == TOK_HOST) {
gethost(strval, ip);
@ -839,10 +854,9 @@ parse_oid(const char *varname, struct asn_oid *oid)
report("index too long");
for (i = 0; i < 4; i++)
oid->subs[oid->len++] = ip[i];
gettoken();
} else
report("bad token in index");
gettoken();
}
return (node);
@ -1006,7 +1020,7 @@ parse_assign(const char *varname)
node = parse_oid(varname, &vindex);
if (token != '=')
report("'=' expected");
report("'=' expected, got '%c'", token);
gettoken();
if (ignore) {

View File

@ -31,6 +31,7 @@
* Support functions for modules.
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,7 @@
*
* Private SNMPd data and functions.
*/
#include <sys/queue.h>
#ifdef USE_LIBBEGEMOT
#include <rpoll.h>
#else
@ -247,7 +247,8 @@ extern struct snmpd snmpd;
#define VERS_ENABLE_V1 0x00000001
#define VERS_ENABLE_V2C 0x00000002
#define VERS_ENABLE_ALL 0x00000003
#define VERS_ENABLE_V3 0x00000004
#define VERS_ENABLE_ALL (VERS_ENABLE_V1 | VERS_ENABLE_V2C | VERS_ENABLE_V3)
/*
* The debug group
@ -279,6 +280,11 @@ struct snmpd_stats {
};
extern struct snmpd_stats snmpd_stats;
/*
* SNMPd Engine
*/
extern struct snmp_engine snmpd_engine;
/*
* OR Table
*/
@ -322,6 +328,11 @@ extern const char *syspath;
extern int32_t snmp_serial_no;
int init_actvals(void);
extern char engine_file[];
int init_snmpd_engine(void);
int set_snmpd_engine(void);
int read_config(const char *, struct lmodule *);
int define_macro(const char *name, const char *value);

View File

@ -31,7 +31,7 @@
.\"
.\" $Begemot: bsnmp/snmpd/snmpmod.3,v 1.14 2005/10/04 13:30:35 brandt_h Exp $
.\"
.Dd February 27, 2006
.Dd September 9, 2010
.Dt SNMPMOD 3
.Os
.Sh NAME
@ -60,6 +60,8 @@
.Nm comm_define ,
.Nm community ,
.Nm oid_zeroDotZero ,
.Nm oid_usmUnknownEngineIDs ,
.Nm oid_usmNotInTimeWindows ,
.Nm reqid_allocate ,
.Nm reqid_next ,
.Nm reqid_base ,
@ -99,7 +101,16 @@
.Nm index_compare ,
.Nm index_compare_off ,
.Nm index_append ,
.Nm index_append_off
.Nm index_append_off,
.Nm bsnmpd_get_usm_stats,
.Nm bsnmpd_reset_usm_stats,
.Nm usm_first_user,
.Nm usm_next_user,
.Nm usm_find_user,
.Nm usm_new_user,
.Nm usm_delete_user,
.Nm usm_flush_users,
.Nm usm_user
.Nd "SNMP daemon loadable module interface"
.Sh LIBRARY
Begemot SNMP library
@ -228,6 +239,25 @@ Begemot SNMP library
.Fn index_append "struct asn_oid *dst" "u_int sub" "const struct asn_oid *src"
.Ft void
.Fn index_append_off "struct asn_oid *dst" "u_int sub" "const struct asn_oid *src" "u_int off"
.Ft struct snmpd_usmstat *
.Fn bsnmpd_get_usm_stats "void"
.Ft void
.Fn bsnmpd_reset_usm_stats "void"
.Ft struct usm_user *
.Fn usm_first_user "void"
.Ft struct usm_user *
.Fn usm_next_user "struct usm_user *uuser"
.Ft struct usm_user *
.Fn usm_find_user "uint8_t *engine" "uint32_t elen" "char *uname"
.Ft struct usm_user *
.Fn usm_new_user "uint8_t *engine" "uint32_t elen" "char *uname"
.Ft void
.Fn usm_delete_user "struct usm_user *"
.Ft void
.Fn usm_flush_users "void"
.Vt extern struct usm_user *usm_user;
.Vt extern const struct asn_oid oid_usmUnknownEngineIDs;
.Vt extern const struct asn_oid oid_usmNotInTimeWindows;
.Sh DESCRIPTION
The
.Xr bsnmpd 1
@ -539,7 +569,7 @@ This is the initial community string.
.El
.Pp
The function returns a globally unique community identifier.
If a PDU is
If a SNMPv1 or SNMPv2 PDU is
received who's community string matches, this identifier is set into the global
.Va community .
.Pp
@ -549,10 +579,76 @@ returns the current community string for the given community.
.Pp
All communities defined by a module are automatically released when the module
is unloaded.
.Ss THE USER-BASED SECURITY GROUP
The scalar statistics of the USM group are held in the global variable
.Va snmpd_usmstats :
.Bd -literal -offset indent
struct snmpd_usmstat {
uint32_t unsupported_seclevels;
uint32_t not_in_time_windows;
uint32_t unknown_users;
uint32_t unknown_engine_ids;
uint32_t wrong_digests;
uint32_t decrypt_errors;
};
.Ed
.Fn bsnmpd_get_usm_stats
returns a pointer to the global structure containing the statistics.
.Fn bsnmpd_reset_usm_stats
clears the statistics of the USM group.
.Pp
A global list of configured USM users is maintained by the daemon.
.Bd -literal -offset indent
struct usm_user {
struct snmp_user suser;
uint8_t user_engine_id[SNMP_ENGINE_ID_SIZ];
uint32_t user_engine_len;
char user_public[SNMP_USM_NAME_SIZ];
uint32_t user_public_len;
int32_t status;
int32_t type;
SLIST_ENTRY(usm_user) up;
};
.Ed
This structure represents an USM user. The daemon only responds to SNMPv3 PDUs
with user credentials matching an USM user entry in its global list.
If a SNMPv3 PDU is received, whose security model is USM, the global
.Va usm_user
is set to point at the user entry that matches the credentials contained in
the PDU.
However, the daemon does not create or remove USM users, it gives an interface
to external loadable module(s) to manage the list.
.Fn usm_new_user
adds an user entry in the list, and
.Fn usm_delete_user
deletes an existing entry from the list.
.Fn usm_flush_users
is used to remove all configured USM users.
.Fn usm_first_user
will return the first user in the list, or
.Li NULL
if the list is empty.
.Fn usm_next_user
will return the next user of a given entry if one exists, or
.Li NULL .
The list is sorted according to the USM user name and Engine ID.
.Fn usm_find_user
returns the USM user entry matching the given
.Fa engine
and
.Fa uname
or
.Li NULL
if an user with the specified name and engine id is not present in the list.
.Ss WELL KNOWN OIDS
The global variable
.Va oid_zeroDotZero
contains the OID 0.0.
The global variables
.Va oid_usmUnknownEngineIDs
.Va oid_usmNotInTimeWindows
contains the OIDs 1.3.6.1.6.3.15.1.1.4.0 and 1.3.6.1.6.3.15.1.1.2.0 used
in the SNMPv3 USM Engine Discovery.
.Ss REQUEST ID RANGES
For modules that implement SNMP client functions besides SNMP agent functions
it may be necessary to identify SNMP requests by their identifier to allow

View File

@ -332,11 +332,137 @@ const char * comm_string(u_int);
/* community for current packet */
extern u_int community;
/*
/*
* SNMP User-based Security Model data. Modified via the snmp_usm(3) module.
*/
struct snmpd_usmstat {
uint32_t unsupported_seclevels;
uint32_t not_in_time_windows;
uint32_t unknown_users;
uint32_t unknown_engine_ids;
uint32_t wrong_digests;
uint32_t decrypt_errors;
};
extern struct snmpd_usmstat snmpd_usmstats;
struct snmpd_usmstat *bsnmpd_get_usm_stats(void);
void bsnmpd_reset_usm_stats(void);
struct usm_user {
struct snmp_user suser;
uint8_t user_engine_id[SNMP_ENGINE_ID_SIZ];
uint32_t user_engine_len;
char user_public[SNMP_ADM_STR32_SIZ];
uint32_t user_public_len;
int32_t status;
int32_t type;
SLIST_ENTRY(usm_user) up;
};
SLIST_HEAD(usm_userlist, usm_user);
struct usm_user *usm_first_user(void);
struct usm_user *usm_next_user(struct usm_user *);
struct usm_user *usm_find_user(uint8_t *, uint32_t, char *);
struct usm_user *usm_new_user(uint8_t *, uint32_t, char *);
void usm_delete_user(struct usm_user *);
void usm_flush_users(void);
/* USM user for current packet */
extern struct usm_user *usm_user;
/*
* SNMP View-based Access Control Model data. Modified via the snmp_vacm(3) module.
*/
struct vacm_group;
struct vacm_user {
/* Security user name from USM */
char secname[SNMP_ADM_STR32_SIZ];
int32_t sec_model;
/* Back pointer to user assigned group name */
struct vacm_group *group;
int32_t type;
int32_t status;
SLIST_ENTRY(vacm_user) vvu;
SLIST_ENTRY(vacm_user) vvg;
};
SLIST_HEAD(vacm_userlist, vacm_user);
struct vacm_group {
char groupname[SNMP_ADM_STR32_SIZ];
struct vacm_userlist group_users;
SLIST_ENTRY(vacm_group) vge;
};
SLIST_HEAD(vacm_grouplist, vacm_group);
struct vacm_access {
/* The group name is index, not a column in the table */
struct vacm_group *group;
char ctx_prefix[SNMP_ADM_STR32_SIZ];
int32_t sec_model;
int32_t sec_level;
int32_t ctx_match;
struct vacm_view *read_view;
struct vacm_view *write_view;
struct vacm_view *notify_view;
int32_t type;
int32_t status;
TAILQ_ENTRY(vacm_access) vva;
};
TAILQ_HEAD(vacm_accesslist, vacm_access);
struct vacm_view {
char viewname[SNMP_ADM_STR32_SIZ]; /* key */
struct asn_oid subtree; /* key */
uint8_t mask[16];
uint8_t exclude;
int32_t type;
int32_t status;
SLIST_ENTRY(vacm_view) vvl;
};
SLIST_HEAD(vacm_viewlist, vacm_view);
struct vacm_context {
/* The ID of the module that registered this context */
int32_t regid;
char ctxname[SNMP_ADM_STR32_SIZ];
SLIST_ENTRY(vacm_context) vcl;
};
SLIST_HEAD(vacm_contextlist, vacm_context);
void vacm_groups_init(void);
struct vacm_user *vacm_first_user(void);
struct vacm_user *vacm_next_user(struct vacm_user *);
struct vacm_user *vacm_new_user(int32_t, char *);
int vacm_delete_user(struct vacm_user *);
int vacm_user_set_group(struct vacm_user *, u_char *, u_int);
struct vacm_access *vacm_first_access_rule(void);
struct vacm_access *vacm_next_access_rule(struct vacm_access *);
struct vacm_access *vacm_new_access_rule(char *, char *, int32_t, int32_t);
int vacm_delete_access_rule(struct vacm_access *);
struct vacm_view *vacm_first_view(void);
struct vacm_view *vacm_next_view(struct vacm_view *);
struct vacm_view *vacm_new_view(char *, struct asn_oid *);
int vacm_delete_view(struct vacm_view *);
struct vacm_context *vacm_first_context(void);
struct vacm_context *vacm_next_context(struct vacm_context *);
struct vacm_context *vacm_add_context(char *, int32_t);
void vacm_flush_contexts(int32_t);
/*
* Well known OIDs
*/
extern const struct asn_oid oid_zeroDotZero;
/* SNMPv3 Engine Discovery */
extern const struct asn_oid oid_usmUnknownEngineIDs;
extern const struct asn_oid oid_usmNotInTimeWindows;
/*
* Request ID ranges.
*

View File

@ -31,6 +31,7 @@
* Local domain socket transport
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/un.h>
#include <sys/stat.h>

View File

@ -31,6 +31,7 @@
* UDP transport
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <syslog.h>

View File

@ -31,6 +31,7 @@
* TrapSinkTable
*/
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <sys/un.h>
#include <stdio.h>

View File

@ -196,5 +196,15 @@
)
)
)
(10 snmpFrameworkMIB
(2 snmpFrameworkMIBObjects
(1 snmpEngine
(1 snmpEngineID OCTETSTRING | SnmpEngineID op_snmp_engine GET)
(2 snmpEngineBoots INTEGER op_snmp_engine GET)
(3 snmpEngineTime INTEGER op_snmp_engine GET)
(4 snmpEngineMaxMessageSize INTEGER op_snmp_engine GET)
)
)
)
))
)

View File

@ -2,6 +2,8 @@
#
# Author: Harti Brandt <harti@freebsd.org>
.include <bsd.own.mk>
CONTRIB= ${.CURDIR}/../../../contrib/bsnmp/lib
.PATH: ${CONTRIB}
@ -11,8 +13,13 @@ SHLIBDIR?= /lib
CFLAGS+= -I${CONTRIB} -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY
CFLAGS+= -DHAVE_STDINT_H -DHAVE_INTTYPES_H -DQUADFMT='"llu"' -DQUADXFMT='"llx"'
SRCS= asn1.c snmp.c snmpagent.c snmpclient.c support.c
INCS= asn1.h snmp.h snmpagent.h snmpclient.h
.if ${MK_OPENSSL} != "no"
CFLAGS+= -DHAVE_LIBCRYPTO
LDADD+= -lcrypto
.endif
SRCS= asn1.c snmp.c snmpagent.c snmpclient.c snmpcrypto.c support.c
INCS= asn1.h snmp.h snmpagent.h snmpclient.h
MAN= asn1.3 bsnmplib.3 bsnmpclient.3 bsnmpagent.3
.include <bsd.lib.mk>

View File

@ -2,6 +2,8 @@
#
# Author: Harti Brandt <harti@freebsd.org>
.include <bsd.own.mk>
CONTRIB=${.CURDIR}/../../../contrib/bsnmp
.PATH: ${CONTRIB}/snmpd
@ -11,7 +13,7 @@ SRCS+= oid.h tree.c tree.h
XSYM= snmpMIB begemotSnmpdModuleTable begemotSnmpd begemotTrapSinkTable \
sysUpTime snmpTrapOID coldStart authenticationFailure \
begemotSnmpdTransUdp begemotSnmpdTransLsock begemotSnmpdLocalPortTable \
freeBSDVersion
freeBSD freeBSDVersion
CLEANFILES= oid.h tree.c tree.h
MAN= bsnmpd.1 snmpmod.3
NO_WERROR=
@ -31,6 +33,10 @@ LDADD= -lbegemot -lbsnmp -lwrap
LDFLAGS= -export-dynamic
.if ${MK_OPENSSL} != "no"
CFLAGS+= -DHAVE_LIBCRYPTO
.endif
oid.h: tree.def Makefile
gensnmptree -e ${XSYM} < ${.ALLSRC:M*.def} > ${.TARGET}

View File

@ -13,6 +13,8 @@ SUBDIR= ${_snmp_atm} \
snmp_hostres \
snmp_mibII \
snmp_pf \
snmp_usm \
snmp_vacm \
snmp_wlan
.if ${MK_NETGRAPH_SUPPORT} != "no"

View File

@ -26,6 +26,7 @@
* $FreeBSD$
*/
#include <sys/queue.h>
#include <bsnmp/snmpmod.h>
#include <net/pfvar.h>

View File

@ -0,0 +1,22 @@
# $FreeBSD$
#
# Author: Shteryana Shopova <syrinx@freebsd.org>
CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp
.PATH: ${CONTRIB}/snmp_usm
MOD= usm
SRCS= usm_snmp.c
XSYM= snmpUsmMIB usmNoAuthProtocol usmHMACMD5AuthProtocol \
usmHMACSHAAuthProtocol usmNoPrivProtocol usmDESPrivProtocol \
usmAesCfb128Protocol usmUserSecurityName
MAN= snmp_usm.3
CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES
CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H
DEFS= ${MOD}_tree.def
BMIBS=
.include <bsd.snmpmod.mk>

View File

@ -0,0 +1,20 @@
# $FreeBSD$
#
# Author: Shteryana Shopova <syrinx@freebsd.org>
CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp
.PATH: ${CONTRIB}/snmp_vacm
MOD= vacm
SRCS= vacm_snmp.c
XSYM= snmpVacmMIB
MAN= snmp_vacm.3
CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -DSNMPTREE_TYPES
CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H
DEFS= ${MOD}_tree.def
BMIBS=
.include <bsd.snmpmod.mk>