📄 snmpusm.c
字号:
{
if ( usm_check_and_update_timeliness (
secEngineID, *secEngineIDLen,
boots_uint, time_uint, &error) == -1 )
{
return error;
}
}
#ifdef LCD_TIME_SYNC_OPT
/*
* Cache the unauthenticated time to use in case we don't have
* anything better - this guess will be no worse than (0,0)
* that we normally use.
*/
else
{
set_enginetime(secEngineID, *secEngineIDLen,
boots_uint, time_uint, FALSE);
}
#endif /* LCD_TIME_SYNC_OPT */
/*
* If needed, decrypt the scoped PDU.
*/
if (secLevel == SNMP_SEC_LEVEL_AUTHPRIV)
{
remaining = wholeMsgLen - (data_ptr - wholeMsg);
if ((value_ptr = asn_parse_sequence (data_ptr, &remaining,
&type_value,
(ASN_UNIVERSAL|ASN_PRIMITIVE|ASN_OCTET_STR),
"encrypted sPDU")) == NULL)
{
DEBUGMSGTL(("usm","%s\n",
"Failed while parsing encrypted sPDU."));
if (snmp_increment_statistic
(STAT_SNMPINASNPARSEERRS)==0)
{
DEBUGMSGTL(("usm","%s\n",
"Failed increment statistic."));
}
return SNMPERR_USM_PARSEERROR;
}
end_of_overhead = value_ptr;
/*
* XOR the salt with the last (iv_length) bytes
* of the priv_key to obtain the IV.
*/
for (i = 0; i < (int)iv_length; i++)
iv[i] = salt[i] ^ user->privKey[iv_length + i];
if (sc_decrypt (
user->privProtocol, user->privProtocolLen,
user->privKey, user->privKeyLen,
iv, iv_length,
value_ptr, remaining,
*scopedPdu, scopedPduLen)
!= SNMP_ERR_NOERROR)
{
DEBUGMSGTL(("usm","%s\n", "Failed decryption."));
if (snmp_increment_statistic
(STAT_USMSTATSDECRYPTIONERRORS)==0)
{
DEBUGMSGTL(("usm","%s\n",
"Failed increment statistic."));
}
return SNMPERR_USM_DECRYPTIONERROR;
}
#ifdef SNMP_TESTING_CODE
if ( debug_is_token_registered("usm/dump") == SNMPERR_SUCCESS) {
dump_chunk("usm/dump", "Decrypted chunk:",
*scopedPdu, *scopedPduLen);
dump_chunk("usm/dump", "IV + Encrypted form:",
salt, salt_length);
dump_chunk("usm/dump", NULL,
value_ptr, remaining);
}
#endif
}
/*
* sPDU is plaintext.
*/
else
{
*scopedPdu = data_ptr;
*scopedPduLen = wholeMsgLen - (data_ptr - wholeMsg);
end_of_overhead = data_ptr;
} /* endif -- PDU decryption */
/*
* Calculate the biggest sPDU for the response (i.e., whole - ovrhd).
*
* FIX Correct?
*/
*maxSizeResponse = maxMsgSize - (int)
((u_long)end_of_overhead - (u_long)wholeMsg);
DEBUGMSGTL(("usm","USM processing completed.\n"));
return SNMPERR_SUCCESS;
} /* end usm_process_in_msg() */
void
init_usm(void) {
snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_POST_READ_CONFIG,
init_usm_post_config, NULL);
}
/*
* initializations for the USM.
*
* Should be called after the configuration files have been read.
*
* Set "arbitrary" portion of salt to a random number.
*/
int
init_usm_post_config(int majorid, int minorid, void *serverarg,
void *clientarg) {
size_t salt_integer_len = sizeof(salt_integer);
initialUser = usm_create_initial_user("initial", usmHMACMD5AuthProtocol,
USM_LENGTH_OID_TRANSFORM,
usmDESPrivProtocol,
USM_LENGTH_OID_TRANSFORM);
SNMP_FREE(initialUser->engineID);
initialUser->engineIDLen = 0;
if ( sc_random((u_char *) &salt_integer, &salt_integer_len) != SNMPERR_SUCCESS )
{
DEBUGMSGTL(("usm","sc_random() failed: using time() as salt.\n"));
salt_integer = (u_int) time(NULL);
salt_integer_len = sizeof(salt_integer);
}
noNameUser = usm_create_initial_user("", usmHMACMD5AuthProtocol,
USM_LENGTH_OID_TRANSFORM,
usmDESPrivProtocol,
USM_LENGTH_OID_TRANSFORM);
SNMP_FREE(noNameUser->engineID);
noNameUser->engineIDLen = 0;
return SNMPERR_SUCCESS;
} /* end init_usm_post_config() */
/*
* Local storage (LCD) of the default user list.
*/
static struct usmUser *userList=NULL;
struct usmUser *
usm_get_userList(void)
{
return userList;
}
/*******************************************************************-o-******
* usm_check_secLevel
*
* Parameters:
* level
* *user
*
* Returns:
* 0 On success,
* -1 Otherwise.
*
* Checks that a given security level is valid for a given user.
*/
int
usm_check_secLevel(int level, struct usmUser *user)
{
if ( level == SNMP_SEC_LEVEL_AUTHPRIV
&& (snmp_oid_compare(user->privProtocol, user->privProtocolLen,
usmNoPrivProtocol, sizeof(usmNoPrivProtocol)/sizeof(oid))==0) )
{
return 1;
}
if ( (level == SNMP_SEC_LEVEL_AUTHPRIV || level == SNMP_SEC_LEVEL_AUTHNOPRIV)
&& (snmp_oid_compare(user->authProtocol, user->authProtocolLen,
usmNoAuthProtocol, sizeof(usmNoAuthProtocol)/sizeof(oid))==0) )
{
return 1;
}
return 0;
} /* end usm_check_secLevel() */
/*******************************************************************-o-******
* usm_check_secLevel_vs_protocols
*
* Parameters:
* level
* *authProtocol
* authProtocolLen
* *privProtocol
* privProtocolLen
*
* Returns:
* 0 On success,
* -1 Otherwise.
*
* Same as above but with explicitly named transform types instead of taking
* from the usmUser structure.
*/
int
usm_check_secLevel_vs_protocols(int level,
oid *authProtocol, u_int authProtocolLen,
oid *privProtocol, u_int privProtocolLen)
{
if ( level == SNMP_SEC_LEVEL_AUTHPRIV
&& (snmp_oid_compare(privProtocol, privProtocolLen, usmNoPrivProtocol,
sizeof(usmNoPrivProtocol)/sizeof(oid))==0) )
{
return 1;
}
if ( (level == SNMP_SEC_LEVEL_AUTHPRIV || level == SNMP_SEC_LEVEL_AUTHNOPRIV)
&& (snmp_oid_compare(authProtocol, authProtocolLen, usmNoAuthProtocol,
sizeof(usmNoAuthProtocol)/sizeof(oid))==0) )
{
return 1;
}
return 0;
} /* end usm_check_secLevel_vs_protocols() */
/* usm_update_engine_time(): Updates engine_time for all registered users.
* This function would be useful for systems that start up with default time
* settings and then update their timing reference using NTP at a later stage
*/
void usm_update_engine_time(void) {
struct usmUser *ptr;
long boots_long;
long time_long;
boots_long = snmpv3_local_snmpEngineBoots();
time_long = snmpv3_local_snmpEngineTime();
for (ptr = userList; ptr != NULL; ptr = ptr->next) {
set_enginetime( ptr->engineID, ptr->engineIDLen,
boots_long, time_long, TRUE );
}
}
/* usm_get_user(): Returns a user from userList based on the engineID,
engineIDLen and name of the requested user. */
struct usmUser *
usm_get_user(u_char *engineID, size_t engineIDLen, char *name)
{
DEBUGMSGTL(("usm","getting user %s\n", name));
return usm_get_user_from_list(engineID, engineIDLen, name, userList, 1);
}
struct usmUser *
usm_get_user_from_list(u_char *engineID, size_t engineIDLen,
char *name, struct usmUser *puserList, int use_default)
{
struct usmUser *ptr;
char noName[] = "";
if (name == NULL)
name = noName;
for (ptr = puserList; ptr != NULL; ptr = ptr->next) {
if (!strcmp(ptr->name, name) &&
ptr->engineIDLen == engineIDLen &&
((ptr->engineID == NULL && engineID == NULL) ||
(ptr->engineID != NULL && engineID != NULL &&
memcmp(ptr->engineID, engineID, engineIDLen) == 0)))
return ptr;
}
/* return "" user used to facilitate engineID discovery */
if (use_default && !strcmp(name, "")) return noNameUser;
/* this next line may be vestigial from when the draft used 'initial'
to discover engineID, also did not remove creation if 'inital' user
-gsm 2/6/99 */
if (use_default && !strcmp(name, "initial")) return initialUser;
return NULL;
}
/* usm_add_user(): Add's a user to the userList, sorted by the
engineIDLength then the engineID then the name length then the name
to facilitate getNext calls on a usmUser table which is indexed by
these values.
Note: userList must not be NULL (obviously), as thats a rather trivial
addition and is left to the API user.
returns the head of the list (which could change due to this add).
*/
struct usmUser *
usm_add_user(struct usmUser *user)
{
struct usmUser *uptr;
uptr = usm_add_user_to_list(user, userList);
if (uptr != NULL)
userList = uptr;
return uptr;
}
struct usmUser *
usm_add_user_to_list(struct usmUser *user,
struct usmUser *puserList)
{
struct usmUser *nptr, *pptr;
/* loop through puserList till we find the proper, sorted place to
insert the new user */
for (nptr = puserList, pptr = NULL; nptr != NULL;
pptr = nptr, nptr = nptr->next) {
if (nptr->engineIDLen > user->engineIDLen)
break;
if (user->engineID == NULL && nptr->engineID != NULL)
break;
if (nptr->engineIDLen == user->engineIDLen &&
(nptr->engineID != NULL && user->engineID != NULL &&
memcmp(nptr->engineID, user->engineID, user->engineIDLen) > 0))
break;
if (!(nptr->engineID == NULL && user->engineID != NULL)) {
if (nptr->engineIDLen == user->engineIDLen &&
((nptr->engineID == NULL && user->engineID == NULL) ||
memcmp(nptr->engineID, user->engineID, user->engineIDLen) == 0) &&
strlen(nptr->name) > strlen(user->name))
break;
if (nptr->engineIDLen == user->engineIDLen &&
((nptr->engineID == NULL && user->engineID == NULL) ||
memcmp(nptr->engineID, user->engineID, user->engineIDLen) == 0) &&
strlen(nptr->name) == strlen(user->name) &&
strcmp(nptr->name, user->name) > 0)
break;
if (nptr->engineIDLen == user->engineIDLen &&
((nptr->engineID == NULL && user->engineID == NULL) ||
memcmp(nptr->engineID, user->engineID, user->engineIDLen) == 0) &&
strlen(nptr->name) == strlen(user->name) &&
strcmp(nptr->name, user->name) == 0)
/* the user is an exact match of a previous entry. Bail */
return NULL;
}
}
/* nptr should now point to the user that we need to add ourselves
in front of, and pptr should be our new 'prev'. */
/* change our pointers */
user->prev = pptr;
user->next = nptr;
/* change the next's prev pointer */
if (user->next)
user->next->prev = user;
/* change the prev's next pointer */
if (user->prev)
user->prev->next = user;
/* rewind to the head of the list and return it (since the new head
could be us, we need to notify the above routine who the head now is. */
for(pptr = user; pptr->prev != NULL; pptr = pptr->prev);
return pptr;
}
/* usm_remove_user(): finds and removes a user from a list */
struct usmUser *
usm_remove_user(struct usmUser *user)
{
return usm_remove_user_from_list(user, &userList);
}
struct usmUser *
usm_remove_user_from_list(struct usmUser *user,
struct usmUser **ppuserList)
{
struct usmUser *nptr, *pptr;
/* NULL pointers aren't allowed */
if (ppuserList == NULL)
return NULL;
/* find the user in the list */
for (nptr = *ppuserList, pptr = NULL; nptr != NULL;
pptr = nptr, nptr = nptr->next) {
if (nptr == user)
break;
}
if (nptr) {
/* remove the user from the linked list */
if (pptr) {
pptr->next = nptr->next;
}
if (nptr->next) {
nptr->next->prev = pptr;
}
} else {
/* user didn't exit */
return NULL;
}
if (nptr == *ppuserList) /* we're the head of the list, need to change
the head to the next user */
*ppuserList = nptr->next;
return *ppuserList;
} /* end usm_remove_user_from_list() */
/* usm_free_user(): calls free() on all needed parts of struct usmUser and
the user himself.
Note: This should *not* be called on an object in a list (IE,
remove it from the list first, and set next and prev to NULL), but
will try to reconnect the list pieces again if it is called this
way. If called on the head of the list, the entire list will be
lost. */
struct usmUser *
usm_free_user(struct usmUser *user)
{
if (user == NULL)
return NULL;
SNMP_FREE(user->engineID);
SNMP_FREE(user->name);
SNMP_FREE(user->secName);
SNMP_FREE(user->cloneFrom);
SNMP_FREE(user->userPublicString);
SNMP_FREE(user->authProtocol);
SNMP_FREE(user->privProtocol);
if (user->authKey != NULL) {
SNMP_ZERO(user->authKey, user->authKeyLen);
SNMP_FREE(user->authKey);
}
if (user->privKey != NULL) {
SNMP_ZERO(user->privKey, user->privKeyLen);
SNMP_FREE(user->privKey);
}
/* FIX Why not put this check *first?*
*/
if (user->prev != NULL) { /* ack, this shouldn't happen */
user->prev->next = user->next;
}
if (user->next != NULL) {
user->next->prev = user->prev;
if (user->prev != NULL) /* ack this is really bad, because it means
we'll loose the head of some structure tree */
DEBUGMSGTL(("usm","Severe: Asked to free
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -