1054 lines
35 KiB
C
1054 lines
35 KiB
C
/*
|
|
* snmpusm.c - send snmp SET requests to a network entity to change the
|
|
* usm user database
|
|
*
|
|
* XXX get engineID dynamically.
|
|
* XXX read passwords from prompts
|
|
* XXX customize responses with user names, etc.
|
|
*/
|
|
/* Portions of this file are subject to the following copyright(s). See
|
|
* the Net-SNMP's COPYING file for more details and other copyrights
|
|
* that may apply:
|
|
*/
|
|
/*
|
|
* Portions of this file are copyrighted by:
|
|
* Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
|
|
* Use is subject to license terms specified in the COPYING file
|
|
* distributed with the Net-SNMP package.
|
|
*/
|
|
#include <net-snmp/net-snmp-config.h>
|
|
|
|
#if HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#else
|
|
#include <strings.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#if HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#if TIME_WITH_SYS_TIME
|
|
# include <sys/time.h>
|
|
# include <time.h>
|
|
#else
|
|
# if HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
# else
|
|
# include <time.h>
|
|
# endif
|
|
#endif
|
|
#if HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
#if HAVE_NETDB_H
|
|
#include <netdb.h>
|
|
#endif
|
|
#if HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#if defined(HAVE_OPENSSL_DH_H) && defined(HAVE_LIBCRYPTO)
|
|
#include <openssl/dh.h>
|
|
#endif /* HAVE_OPENSSL_DH_H && HAVE_LIBCRYPTO */
|
|
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
#include <net-snmp/library/snmp_openssl.h>
|
|
|
|
#define CMD_PASSWD_NAME "passwd"
|
|
#define CMD_PASSWD 1
|
|
#define CMD_CREATE_NAME "create"
|
|
#define CMD_CREATE 2
|
|
#define CMD_DELETE_NAME "delete"
|
|
#define CMD_DELETE 3
|
|
#define CMD_CLONEFROM_NAME "cloneFrom"
|
|
#define CMD_CLONEFROM 4
|
|
#define CMD_ACTIVATE_NAME "activate"
|
|
#define CMD_ACTIVATE 5
|
|
#define CMD_DEACTIVATE_NAME "deactivate"
|
|
#define CMD_DEACTIVATE 6
|
|
#define CMD_CHANGEKEY_NAME "changekey"
|
|
#define CMD_CHANGEKEY 7
|
|
|
|
#define CMD_NUM 7
|
|
|
|
static const char *successNotes[CMD_NUM] = {
|
|
"SNMPv3 Key(s) successfully changed.",
|
|
"User successfully created.",
|
|
"User successfully deleted.",
|
|
"User successfully cloned.",
|
|
"User successfully activated.",
|
|
"User successfully deactivated.",
|
|
"SNMPv3 Key(s) successfully changed."
|
|
};
|
|
|
|
#define USM_OID_LEN 12
|
|
#define DH_USM_OID_LEN 11
|
|
|
|
static oid
|
|
|
|
authKeyOid[MAX_OID_LEN] = { 1, 3, 6, 1, 6, 3, 15, 1, 2, 2, 1, 6 },
|
|
ownAuthKeyOid[MAX_OID_LEN] = {1, 3, 6, 1, 6, 3, 15, 1, 2, 2, 1, 7},
|
|
privKeyOid[MAX_OID_LEN] = {1, 3, 6, 1, 6, 3, 15, 1, 2, 2, 1, 9},
|
|
ownPrivKeyOid[MAX_OID_LEN] = {1, 3, 6, 1, 6, 3, 15, 1, 2, 2, 1, 10},
|
|
usmUserCloneFrom[MAX_OID_LEN] = {1, 3, 6, 1, 6, 3, 15, 1, 2, 2, 1, 4},
|
|
usmUserSecurityName[MAX_OID_LEN] = {1, 3, 6, 1, 6, 3, 15, 1, 2, 2, 1, 3},
|
|
usmUserPublic[MAX_OID_LEN] = {1, 3, 6, 1, 6, 3, 15, 1, 2, 2, 1, 11},
|
|
usmUserStatus[MAX_OID_LEN] = {1, 3, 6, 1, 6, 3, 15, 1, 2, 2, 1, 13},
|
|
/* diffie helman change key objects */
|
|
usmDHUserAuthKeyChange[MAX_OID_LEN] = {1, 3, 6, 1, 3, 101, 1, 1, 2, 1, 1 },
|
|
usmDHUserPrivKeyChange[MAX_OID_LEN] = {1, 3, 6, 1, 3, 101, 1, 1, 2, 1, 3 },
|
|
#if defined(HAVE_OPENSSL_DH_H) && defined(HAVE_LIBCRYPTO)
|
|
usmDHUserOwnAuthKeyChange[MAX_OID_LEN] = {1, 3, 6, 1, 3, 101, 1, 1, 2, 1, 2 },
|
|
usmDHUserOwnPrivKeyChange[MAX_OID_LEN] = {1, 3, 6, 1, 3, 101, 1, 1, 2, 1, 4 },
|
|
#endif /* HAVE_OPENSSL_DH_H && HAVE_LIBCRYPTO */
|
|
usmDHParameters[] = { 1,3,6,1,3,101,1,1,1,0 }
|
|
;
|
|
size_t usmDHParameters_len = OID_LENGTH(usmDHParameters);
|
|
|
|
static
|
|
oid *authKeyChange = authKeyOid, *privKeyChange = privKeyOid;
|
|
oid *dhauthKeyChange = usmDHUserAuthKeyChange,
|
|
*dhprivKeyChange = usmDHUserPrivKeyChange;
|
|
int doauthkey = 0, doprivkey = 0, uselocalizedkey = 0;
|
|
size_t usmUserEngineIDLen = 0;
|
|
u_char *usmUserEngineID = NULL;
|
|
char *usmUserPublic_val = NULL;
|
|
int docreateandwait = 0;
|
|
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr, "Usage: snmpusm ");
|
|
snmp_parse_args_usage(stderr);
|
|
fprintf(stderr, " COMMAND\n\n");
|
|
snmp_parse_args_descriptions(stderr);
|
|
fprintf(stderr, "\nsnmpusm commands:\n");
|
|
fprintf(stderr, " [options] create USER [CLONEFROM-USER]\n");
|
|
fprintf(stderr, " [options] delete USER\n");
|
|
fprintf(stderr, " [options] activate USER\n");
|
|
fprintf(stderr, " [options] deactivate USER\n");
|
|
fprintf(stderr, " [options] [-Cw] cloneFrom USER CLONEFROM-USER\n");
|
|
fprintf(stderr, " [options] [-Ca] [-Cx] changekey [USER]\n");
|
|
fprintf(stderr,
|
|
" [options] [-Ca] [-Cx] passwd OLD-PASSPHRASE NEW-PASSPHRASE [USER]\n");
|
|
fprintf(stderr,
|
|
" [options] (-Ca|-Cx) -Ck passwd OLD-KEY-OR-PASS NEW-KEY-OR-PASS [USER]\n");
|
|
fprintf(stderr, "\nsnmpusm options:\n");
|
|
fprintf(stderr, "\t-CE ENGINE-ID\tSet usmUserEngineID (e.g. 800000020109840301).\n");
|
|
fprintf(stderr, "\t-Cp STRING\tSet usmUserPublic value to STRING.\n");
|
|
fprintf(stderr, "\t-Cw\t\tCreate the user with createAndWait.\n");
|
|
fprintf(stderr, "\t\t\t(it won't be active until you active it)\n");
|
|
fprintf(stderr, "\t-Cx\t\tChange the privacy key.\n");
|
|
fprintf(stderr, "\t-Ca\t\tChange the authentication key.\n");
|
|
fprintf(stderr, "\t-Ck\t\tAllows one to use localized key (must start with 0x)\n");
|
|
fprintf(stderr, "\t\t\tinstead of passphrase.\n");
|
|
}
|
|
|
|
/*
|
|
* setup_oid appends to the oid the index for the engineid/user
|
|
*/
|
|
void
|
|
setup_oid(oid * it, size_t * len, u_char * id, size_t idlen,
|
|
const char *user)
|
|
{
|
|
int i, itIndex = *len;
|
|
|
|
*len = itIndex + 1 + idlen + 1 + strlen(user);
|
|
|
|
it[itIndex++] = idlen;
|
|
for (i = 0; i < (int) idlen; i++) {
|
|
it[itIndex++] = id[i];
|
|
}
|
|
|
|
it[itIndex++] = strlen(user);
|
|
for (i = 0; i < (int) strlen(user); i++) {
|
|
it[itIndex++] = user[i];
|
|
}
|
|
|
|
#ifdef NETSNMP_ENABLE_TESTING_CODE
|
|
fprintf(stdout, "setup_oid: ");
|
|
fprint_objid(stdout, it, *len);
|
|
fprintf(stdout, "\n");
|
|
#endif
|
|
}
|
|
|
|
#if defined(HAVE_OPENSSL_DH_H) && defined(HAVE_LIBCRYPTO)
|
|
int
|
|
get_USM_DH_key(netsnmp_variable_list *vars, netsnmp_variable_list *dhvar,
|
|
size_t outkey_len,
|
|
netsnmp_pdu *pdu, const char *keyname,
|
|
oid *keyoid, size_t keyoid_len) {
|
|
u_char *dhkeychange;
|
|
DH *dh;
|
|
const BIGNUM *p, *g, *pub_key;
|
|
BIGNUM *other_pub;
|
|
u_char *key;
|
|
size_t key_len;
|
|
|
|
dhkeychange = (u_char *) malloc(2 * vars->val_len * sizeof(char));
|
|
if (!dhkeychange)
|
|
return SNMPERR_GENERR;
|
|
|
|
memcpy(dhkeychange, vars->val.string, vars->val_len);
|
|
|
|
{
|
|
const unsigned char *cp = dhvar->val.string;
|
|
dh = d2i_DHparams(NULL, &cp, dhvar->val_len);
|
|
}
|
|
|
|
if (dh)
|
|
DH_get0_pqg(dh, &p, NULL, &g);
|
|
|
|
if (!dh || !g || !p) {
|
|
SNMP_FREE(dhkeychange);
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
if (!DH_generate_key(dh)) {
|
|
SNMP_FREE(dhkeychange);
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
DH_get0_key(dh, &pub_key, NULL);
|
|
if (vars->val_len != (unsigned int)BN_num_bytes(pub_key)) {
|
|
SNMP_FREE(dhkeychange);
|
|
fprintf(stderr,"incorrect diffie-helman lengths (%lu != %d)\n",
|
|
(unsigned long)vars->val_len, BN_num_bytes(pub_key));
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
BN_bn2bin(pub_key, dhkeychange + vars->val_len);
|
|
|
|
key_len = DH_size(dh);
|
|
if (!key_len) {
|
|
SNMP_FREE(dhkeychange);
|
|
return SNMPERR_GENERR;
|
|
}
|
|
key = (u_char *) malloc(key_len * sizeof(u_char));
|
|
|
|
if (!key) {
|
|
SNMP_FREE(dhkeychange);
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
other_pub = BN_bin2bn(vars->val.string, vars->val_len, NULL);
|
|
if (!other_pub) {
|
|
SNMP_FREE(dhkeychange);
|
|
SNMP_FREE(key);
|
|
return SNMPERR_GENERR;
|
|
}
|
|
|
|
if (DH_compute_key(key, other_pub, dh)) {
|
|
u_char *kp;
|
|
|
|
printf("new %s key: 0x", keyname);
|
|
for(kp = key + key_len - outkey_len;
|
|
kp - key < (int)key_len; kp++) {
|
|
printf("%02x", (unsigned char) *kp);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
snmp_pdu_add_variable(pdu, keyoid, keyoid_len,
|
|
ASN_OCTET_STR, dhkeychange,
|
|
2 * vars->val_len);
|
|
|
|
SNMP_FREE(dhkeychange);
|
|
SNMP_FREE(other_pub);
|
|
SNMP_FREE(key);
|
|
|
|
return SNMPERR_SUCCESS;
|
|
}
|
|
#endif /* HAVE_OPENSSL_DH_H */
|
|
|
|
static void
|
|
optProc(int argc, char *const *argv, int opt)
|
|
{
|
|
switch (opt) {
|
|
case 'C':
|
|
while (*optarg) {
|
|
switch (*optarg++) {
|
|
case 'a':
|
|
doauthkey = 1;
|
|
break;
|
|
|
|
case 'x':
|
|
doprivkey = 1;
|
|
break;
|
|
|
|
case 'k':
|
|
uselocalizedkey = 1;
|
|
break;
|
|
|
|
case 'p':
|
|
if (optind < argc) {
|
|
usmUserPublic_val = argv[optind];
|
|
} else {
|
|
fprintf(stderr, "Bad -Cp option: no argument given\n");
|
|
exit(1);
|
|
}
|
|
optind++;
|
|
break;
|
|
|
|
case 'w':
|
|
docreateandwait = 1;
|
|
break;
|
|
|
|
case 'E': {
|
|
size_t ebuf_len = MAX_ENGINEID_LENGTH;
|
|
u_char *ebuf;
|
|
if (optind < argc) {
|
|
if (argv[optind]) {
|
|
ebuf = (u_char *)malloc(ebuf_len);
|
|
if (ebuf == NULL) {
|
|
fprintf(stderr,
|
|
"malloc failure processing -CE option.\n");
|
|
exit(1);
|
|
}
|
|
if (!snmp_hex_to_binary(&ebuf, &ebuf_len,
|
|
&usmUserEngineIDLen, 1, argv[optind])) {
|
|
fprintf(stderr,
|
|
"Bad usmUserEngineID value after -CE option.\n");
|
|
free(ebuf);
|
|
exit(1);
|
|
}
|
|
usmUserEngineID = ebuf;
|
|
DEBUGMSGTL(("snmpusm", "usmUserEngineID set to: "));
|
|
DEBUGMSGHEX(("snmpusm", usmUserEngineID, usmUserEngineIDLen));
|
|
DEBUGMSG(("snmpusm", "\n"));
|
|
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Bad -CE option: no argument given\n");
|
|
exit(1);
|
|
}
|
|
optind++;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
fprintf(stderr, "Unknown flag passed to -C: %c\n",
|
|
optarg[-1]);
|
|
exit(1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
netsnmp_session session, *ss;
|
|
netsnmp_pdu *pdu = NULL, *response = NULL;
|
|
|
|
int arg;
|
|
size_t name_length = USM_OID_LEN;
|
|
size_t name_length2 = USM_OID_LEN;
|
|
int status;
|
|
int exitval = 1;
|
|
int rval;
|
|
int command = 0;
|
|
long longvar;
|
|
|
|
size_t oldKu_len = SNMP_MAXBUF_SMALL,
|
|
newKu_len = SNMP_MAXBUF_SMALL,
|
|
oldkul_len = SNMP_MAXBUF_SMALL,
|
|
oldkulpriv_len = SNMP_MAXBUF_SMALL,
|
|
newkulpriv_len = SNMP_MAXBUF_SMALL,
|
|
newkul_len = SNMP_MAXBUF_SMALL,
|
|
keychange_len = SNMP_MAXBUF_SMALL,
|
|
keychangepriv_len = SNMP_MAXBUF_SMALL;
|
|
|
|
char *newpass = NULL, *oldpass = NULL;
|
|
u_char oldKu[SNMP_MAXBUF_SMALL],
|
|
newKu[SNMP_MAXBUF_SMALL],
|
|
oldkul[SNMP_MAXBUF_SMALL],
|
|
oldkulpriv[SNMP_MAXBUF_SMALL],
|
|
newkulpriv[SNMP_MAXBUF_SMALL],
|
|
newkul[SNMP_MAXBUF_SMALL], keychange[SNMP_MAXBUF_SMALL],
|
|
keychangepriv[SNMP_MAXBUF_SMALL];
|
|
|
|
SOCK_STARTUP;
|
|
|
|
authKeyChange = authKeyOid;
|
|
privKeyChange = privKeyOid;
|
|
|
|
/*
|
|
* get the common command line arguments
|
|
*/
|
|
switch (arg = snmp_parse_args(argc, argv, &session, "C:", optProc)) {
|
|
case NETSNMP_PARSE_ARGS_ERROR:
|
|
goto out;
|
|
case NETSNMP_PARSE_ARGS_SUCCESS_EXIT:
|
|
exitval = 0;
|
|
goto out;
|
|
case NETSNMP_PARSE_ARGS_ERROR_USAGE:
|
|
usage();
|
|
goto out;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (arg >= argc) {
|
|
fprintf(stderr, "Please specify an operation to perform.\n");
|
|
usage();
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* open an SNMP session
|
|
*/
|
|
/*
|
|
* Note: this needs to obtain the engineID used below
|
|
*/
|
|
session.flags &= ~SNMP_FLAGS_DONT_PROBE;
|
|
ss = snmp_open(&session);
|
|
if (ss == NULL) {
|
|
/*
|
|
* diagnose snmp_open errors with the input netsnmp_session pointer
|
|
*/
|
|
snmp_sess_perror("snmpusm", &session);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* set usmUserEngineID from ss->contextEngineID
|
|
* if not already set (via -CE)
|
|
*/
|
|
if (usmUserEngineID == NULL) {
|
|
usmUserEngineID = ss->contextEngineID;
|
|
usmUserEngineIDLen = ss->contextEngineIDLen;
|
|
}
|
|
|
|
/*
|
|
* create PDU for SET request and add object names and values to request
|
|
*/
|
|
pdu = snmp_pdu_create(SNMP_MSG_SET);
|
|
if (!pdu) {
|
|
fprintf(stderr, "Failed to create request\n");
|
|
goto close_session;
|
|
}
|
|
|
|
|
|
if (strcmp(argv[arg], CMD_PASSWD_NAME) == 0) {
|
|
|
|
/*
|
|
* passwd: change a users password.
|
|
*
|
|
* XXX: Uses the auth type of the calling user, a MD5 user can't
|
|
* change a SHA user's key.
|
|
*/
|
|
char *passwd_user;
|
|
|
|
command = CMD_PASSWD;
|
|
oldpass = argv[++arg];
|
|
newpass = argv[++arg];
|
|
passwd_user = argv[++arg];
|
|
|
|
if (doprivkey == 0 && doauthkey == 0)
|
|
doprivkey = doauthkey = 1;
|
|
|
|
if (newpass == NULL || strlen(newpass) < USM_LENGTH_P_MIN) {
|
|
fprintf(stderr,
|
|
"New passphrase must be greater than %d characters in length.\n",
|
|
USM_LENGTH_P_MIN);
|
|
goto close_session;
|
|
}
|
|
|
|
if (oldpass == NULL || strlen(oldpass) < USM_LENGTH_P_MIN) {
|
|
fprintf(stderr,
|
|
"Old passphrase must be greater than %d characters in length.\n",
|
|
USM_LENGTH_P_MIN);
|
|
goto close_session;
|
|
}
|
|
|
|
DEBUGMSGTL(("9:usm:passwd", "oldpass len %" NETSNMP_PRIz "d, newpass len %" NETSNMP_PRIz "d\n",
|
|
strlen(oldpass), strlen(newpass)));
|
|
|
|
/*
|
|
* Change the user supplied on command line.
|
|
*/
|
|
if ((passwd_user != NULL) && (strlen(passwd_user) > 0)) {
|
|
session.securityName = passwd_user;
|
|
} else {
|
|
/*
|
|
* Use own key object if no user was supplied.
|
|
*/
|
|
authKeyChange = ownAuthKeyOid;
|
|
privKeyChange = ownPrivKeyOid;
|
|
}
|
|
|
|
/*
|
|
* do we have a securityName? If not, copy the default
|
|
*/
|
|
if (session.securityName == NULL) {
|
|
session.securityName =
|
|
strdup(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
|
|
NETSNMP_DS_LIB_SECNAME));
|
|
}
|
|
|
|
/*
|
|
* the old Ku is in the session, but we need the new one
|
|
*/
|
|
if (session.securityAuthProto == NULL) {
|
|
/*
|
|
* get .conf set default
|
|
*/
|
|
const oid *def =
|
|
get_default_authtype(&session.securityAuthProtoLen);
|
|
session.securityAuthProto =
|
|
snmp_duplicate_objid(def, session.securityAuthProtoLen);
|
|
}
|
|
if (session.securityAuthProto == NULL) {
|
|
/*
|
|
* assume MD5
|
|
*/
|
|
#ifndef NETSNMP_DISABLE_MD5
|
|
session.securityAuthProtoLen =
|
|
sizeof(usmHMACMD5AuthProtocol) / sizeof(oid);
|
|
session.securityAuthProto =
|
|
snmp_duplicate_objid(usmHMACMD5AuthProtocol,
|
|
session.securityAuthProtoLen);
|
|
#else
|
|
session.securityAuthProtoLen =
|
|
sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid);
|
|
session.securityAuthProto =
|
|
snmp_duplicate_objid(usmHMACSHA1AuthProtocol,
|
|
session.securityAuthProtoLen);
|
|
#endif
|
|
|
|
}
|
|
|
|
if (uselocalizedkey && (strncmp(oldpass, "0x", 2) == 0)) {
|
|
/*
|
|
* use the localized key from the command line
|
|
*/
|
|
u_char *buf;
|
|
size_t buf_len = SNMP_MAXBUF_SMALL;
|
|
buf = (u_char *) malloc (buf_len * sizeof(u_char));
|
|
|
|
oldkul_len = 0; /* initialize the offset */
|
|
if (!snmp_hex_to_binary((u_char **) (&buf), &buf_len, &oldkul_len, 0, oldpass)) {
|
|
snmp_perror(argv[0]);
|
|
fprintf(stderr, "generating the old Kul from localized key failed\n");
|
|
goto close_session;
|
|
}
|
|
|
|
memcpy(oldkul, buf, oldkul_len);
|
|
SNMP_FREE(buf);
|
|
}
|
|
else {
|
|
/*
|
|
* the old Ku is in the session, but we need the new one
|
|
*/
|
|
rval = generate_Ku(session.securityAuthProto,
|
|
session.securityAuthProtoLen,
|
|
(u_char *) oldpass, strlen(oldpass),
|
|
oldKu, &oldKu_len);
|
|
|
|
if (rval != SNMPERR_SUCCESS) {
|
|
snmp_perror(argv[0]);
|
|
fprintf(stderr, "generating the old Ku failed\n");
|
|
goto close_session;
|
|
}
|
|
|
|
/*
|
|
* generate the two Kul's
|
|
*/
|
|
rval = generate_kul(session.securityAuthProto,
|
|
session.securityAuthProtoLen,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
oldKu, oldKu_len, oldkul, &oldkul_len);
|
|
|
|
if (rval != SNMPERR_SUCCESS) {
|
|
snmp_perror(argv[0]);
|
|
fprintf(stderr, "generating the old Kul failed\n");
|
|
goto close_session;
|
|
}
|
|
DEBUGMSGTL(("9:usm:passwd", "oldkul len %" NETSNMP_PRIz "d\n", oldkul_len));
|
|
}
|
|
if (uselocalizedkey && (strncmp(newpass, "0x", 2) == 0)) {
|
|
/*
|
|
* use the localized key from the command line
|
|
*/
|
|
u_char *buf;
|
|
size_t buf_len = SNMP_MAXBUF_SMALL;
|
|
buf = (u_char *) malloc (buf_len * sizeof(u_char));
|
|
|
|
newkul_len = 0; /* initialize the offset */
|
|
if (!snmp_hex_to_binary((u_char **) (&buf), &buf_len, &newkul_len, 0, newpass)) {
|
|
snmp_perror(argv[0]);
|
|
fprintf(stderr, "generating the new Kul from localized key failed\n");
|
|
goto close_session;
|
|
}
|
|
|
|
memcpy(newkul, buf, newkul_len);
|
|
SNMP_FREE(buf);
|
|
} else {
|
|
rval = generate_Ku(session.securityAuthProto,
|
|
session.securityAuthProtoLen,
|
|
(u_char *) newpass, strlen(newpass),
|
|
newKu, &newKu_len);
|
|
|
|
if (rval != SNMPERR_SUCCESS) {
|
|
snmp_perror(argv[0]);
|
|
fprintf(stderr, "generating the new Ku failed\n");
|
|
goto close_session;
|
|
}
|
|
|
|
rval = generate_kul(session.securityAuthProto,
|
|
session.securityAuthProtoLen,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
newKu, newKu_len, newkul, &newkul_len);
|
|
|
|
if (rval != SNMPERR_SUCCESS) {
|
|
snmp_perror(argv[0]);
|
|
fprintf(stderr, "generating the new Kul failed\n");
|
|
goto close_session;
|
|
}
|
|
DEBUGMSGTL(("9:usm:passwd", "newkul len %" NETSNMP_PRIz "d\n", newkul_len));
|
|
}
|
|
|
|
/*
|
|
* for encryption, we may need to truncate the key to the proper length
|
|
* so we need two copies. For simplicity, we always just copy even if
|
|
* they're the same lengths.
|
|
*/
|
|
if (doprivkey) {
|
|
int privtype, properlength;
|
|
u_char *okp = oldkulpriv, *nkp = newkulpriv;
|
|
if (!session.securityPrivProto) {
|
|
snmp_log(LOG_ERR, "no encryption type specified, which I need in order to know to change the key\n");
|
|
goto close_session;
|
|
}
|
|
|
|
privtype = sc_get_privtype(session.securityPrivProto,
|
|
session.securityPrivProtoLen);
|
|
properlength = sc_get_proper_priv_length_bytype(privtype);
|
|
if (USM_CREATE_USER_PRIV_DES == privtype)
|
|
properlength *= 2; /* ?? we store salt with key */
|
|
DEBUGMSGTL(("9:usm:passwd", "proper len %d\n", properlength));
|
|
oldkulpriv_len = oldkul_len;
|
|
newkulpriv_len = newkul_len;
|
|
memcpy(oldkulpriv, oldkul, oldkulpriv_len);
|
|
memcpy(newkulpriv, newkul, newkulpriv_len);
|
|
|
|
if (oldkulpriv_len > properlength) {
|
|
oldkulpriv_len = newkulpriv_len = properlength;
|
|
}
|
|
else if (oldkulpriv_len < properlength) {
|
|
rval = netsnmp_extend_kul(properlength,
|
|
session.securityAuthProto,
|
|
session.securityAuthProtoLen,
|
|
privtype,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
&okp, &oldkulpriv_len,
|
|
sizeof(oldkulpriv));
|
|
rval = netsnmp_extend_kul(properlength,
|
|
session.securityAuthProto,
|
|
session.securityAuthProtoLen,
|
|
privtype,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
&nkp, &newkulpriv_len,
|
|
sizeof(newkulpriv));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* create the keychange string
|
|
*/
|
|
if (doauthkey) {
|
|
rval = encode_keychange(session.securityAuthProto,
|
|
session.securityAuthProtoLen,
|
|
oldkul, oldkul_len,
|
|
newkul, newkul_len,
|
|
keychange, &keychange_len);
|
|
|
|
if (rval != SNMPERR_SUCCESS) {
|
|
snmp_perror(argv[0]);
|
|
fprintf(stderr, "encoding the keychange failed\n");
|
|
usage();
|
|
goto close_session;
|
|
}
|
|
}
|
|
|
|
/* which is slightly different for encryption if lengths are
|
|
different */
|
|
if (doprivkey) {
|
|
DEBUGMSGTL(("9:usm:passwd:encode", "proper len %" NETSNMP_PRIz "d, old_len %" NETSNMP_PRIz "d, new_len %" NETSNMP_PRIz "d\n",
|
|
oldkulpriv_len, oldkulpriv_len, newkulpriv_len));
|
|
rval = encode_keychange(session.securityAuthProto,
|
|
session.securityAuthProtoLen,
|
|
oldkulpriv, oldkulpriv_len,
|
|
newkulpriv, newkulpriv_len,
|
|
keychangepriv, &keychangepriv_len);
|
|
|
|
DEBUGMSGTL(("9:usm:passwd:encode", "keychange len %" NETSNMP_PRIz "d\n",
|
|
keychangepriv_len));
|
|
if (rval != SNMPERR_SUCCESS) {
|
|
snmp_perror(argv[0]);
|
|
fprintf(stderr, "encoding the keychange failed\n");
|
|
usage();
|
|
goto close_session;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* add the keychange string to the outgoing packet
|
|
*/
|
|
if (doauthkey) {
|
|
setup_oid(authKeyChange, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
session.securityName);
|
|
snmp_pdu_add_variable(pdu, authKeyChange, name_length,
|
|
ASN_OCTET_STR, keychange, keychange_len);
|
|
}
|
|
if (doprivkey) {
|
|
setup_oid(privKeyChange, &name_length2,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
session.securityName);
|
|
snmp_pdu_add_variable(pdu, privKeyChange, name_length2,
|
|
ASN_OCTET_STR,
|
|
keychangepriv, keychangepriv_len);
|
|
}
|
|
|
|
} else if (strcmp(argv[arg], CMD_CREATE_NAME) == 0) {
|
|
/*
|
|
* create: create a user
|
|
*
|
|
* create USER [CLONEFROM]
|
|
*/
|
|
if (++arg >= argc) {
|
|
fprintf(stderr, "You must specify the user name to create\n");
|
|
usage();
|
|
goto close_session;
|
|
}
|
|
|
|
command = CMD_CREATE;
|
|
|
|
if (++arg < argc) {
|
|
/*
|
|
* clone the new user from an existing user
|
|
* (and make them active immediately)
|
|
*/
|
|
setup_oid(usmUserStatus, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen, argv[arg-1]);
|
|
if (docreateandwait) {
|
|
longvar = RS_CREATEANDWAIT;
|
|
} else {
|
|
longvar = RS_CREATEANDGO;
|
|
}
|
|
snmp_pdu_add_variable(pdu, usmUserStatus, name_length,
|
|
ASN_INTEGER, (u_char *) & longvar,
|
|
sizeof(longvar));
|
|
|
|
name_length = USM_OID_LEN;
|
|
setup_oid(usmUserCloneFrom, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
argv[arg - 1]);
|
|
setup_oid(usmUserSecurityName, &name_length2,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
argv[arg]);
|
|
snmp_pdu_add_variable(pdu, usmUserCloneFrom, name_length,
|
|
ASN_OBJECT_ID,
|
|
(u_char *) usmUserSecurityName,
|
|
sizeof(oid) * name_length2);
|
|
} else {
|
|
/*
|
|
* create a new (unauthenticated) user from scratch
|
|
* The Net-SNMP agent won't allow such a user to be made active.
|
|
*/
|
|
setup_oid(usmUserStatus, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen, argv[arg-1]);
|
|
longvar = RS_CREATEANDWAIT;
|
|
snmp_pdu_add_variable(pdu, usmUserStatus, name_length,
|
|
ASN_INTEGER, (u_char *) & longvar,
|
|
sizeof(longvar));
|
|
}
|
|
|
|
} else if (strcmp(argv[arg], CMD_CLONEFROM_NAME) == 0) {
|
|
/*
|
|
* create: clone a user from another
|
|
*
|
|
* cloneFrom USER FROM
|
|
*/
|
|
if (++arg >= argc) {
|
|
fprintf(stderr,
|
|
"You must specify the user name to operate on\n");
|
|
usage();
|
|
goto close_session;
|
|
}
|
|
|
|
command = CMD_CLONEFROM;
|
|
setup_oid(usmUserStatus, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen, argv[arg]);
|
|
longvar = RS_ACTIVE;
|
|
snmp_pdu_add_variable(pdu, usmUserStatus, name_length,
|
|
ASN_INTEGER, (u_char *) & longvar,
|
|
sizeof(longvar));
|
|
name_length = USM_OID_LEN;
|
|
setup_oid(usmUserCloneFrom, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen, argv[arg]);
|
|
|
|
if (++arg >= argc) {
|
|
fprintf(stderr,
|
|
"You must specify the user name to clone from\n");
|
|
usage();
|
|
goto close_session;
|
|
}
|
|
|
|
setup_oid(usmUserSecurityName, &name_length2,
|
|
usmUserEngineID, usmUserEngineIDLen, argv[arg]);
|
|
snmp_pdu_add_variable(pdu, usmUserCloneFrom, name_length,
|
|
ASN_OBJECT_ID,
|
|
(u_char *) usmUserSecurityName,
|
|
sizeof(oid) * name_length2);
|
|
|
|
} else if (strcmp(argv[arg], CMD_DELETE_NAME) == 0) {
|
|
/*
|
|
* delete: delete a user
|
|
*
|
|
* delete USER
|
|
*/
|
|
if (++arg >= argc) {
|
|
fprintf(stderr, "You must specify the user name to delete\n");
|
|
goto close_session;
|
|
}
|
|
|
|
command = CMD_DELETE;
|
|
setup_oid(usmUserStatus, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen, argv[arg]);
|
|
longvar = RS_DESTROY;
|
|
snmp_pdu_add_variable(pdu, usmUserStatus, name_length,
|
|
ASN_INTEGER, (u_char *) & longvar,
|
|
sizeof(longvar));
|
|
} else if (strcmp(argv[arg], CMD_ACTIVATE_NAME) == 0) {
|
|
/*
|
|
* activate: activate a user
|
|
*
|
|
* activate USER
|
|
*/
|
|
if (++arg >= argc) {
|
|
fprintf(stderr, "You must specify the user name to activate\n");
|
|
goto close_session;
|
|
}
|
|
|
|
command = CMD_ACTIVATE;
|
|
setup_oid(usmUserStatus, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen, argv[arg]);
|
|
longvar = RS_ACTIVE;
|
|
snmp_pdu_add_variable(pdu, usmUserStatus, name_length,
|
|
ASN_INTEGER, (u_char *) & longvar,
|
|
sizeof(longvar));
|
|
} else if (strcmp(argv[arg], CMD_DEACTIVATE_NAME) == 0) {
|
|
/*
|
|
* deactivate: deactivate a user
|
|
*
|
|
* deactivate USER
|
|
*/
|
|
if (++arg >= argc) {
|
|
fprintf(stderr, "You must specify the user name to deactivate\n");
|
|
goto close_session;
|
|
}
|
|
|
|
command = CMD_DEACTIVATE;
|
|
setup_oid(usmUserStatus, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen, argv[arg]);
|
|
longvar = RS_NOTINSERVICE;
|
|
snmp_pdu_add_variable(pdu, usmUserStatus, name_length,
|
|
ASN_INTEGER, (u_char *) & longvar,
|
|
sizeof(longvar));
|
|
#if defined(HAVE_OPENSSL_DH_H) && defined(HAVE_LIBCRYPTO)
|
|
} else if (strcmp(argv[arg], CMD_CHANGEKEY_NAME) == 0) {
|
|
/*
|
|
* change the key of a user if DH is available
|
|
*/
|
|
|
|
char *passwd_user;
|
|
netsnmp_pdu *dhpdu, *dhresponse = NULL;
|
|
netsnmp_variable_list *vars, *dhvar;
|
|
|
|
command = CMD_CHANGEKEY;
|
|
name_length = DH_USM_OID_LEN;
|
|
name_length2 = DH_USM_OID_LEN;
|
|
|
|
passwd_user = argv[++arg];
|
|
|
|
if (doprivkey == 0 && doauthkey == 0)
|
|
doprivkey = doauthkey = 1;
|
|
|
|
/*
|
|
* Change the user supplied on command line.
|
|
*/
|
|
if ((passwd_user != NULL) && (strlen(passwd_user) > 0)) {
|
|
session.securityName = passwd_user;
|
|
} else {
|
|
/*
|
|
* Use own key object if no user was supplied.
|
|
*/
|
|
dhauthKeyChange = usmDHUserOwnAuthKeyChange;
|
|
dhprivKeyChange = usmDHUserOwnPrivKeyChange;
|
|
}
|
|
|
|
/*
|
|
* do we have a securityName? If not, copy the default
|
|
*/
|
|
if (session.securityName == NULL) {
|
|
session.securityName =
|
|
strdup(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
|
|
NETSNMP_DS_LIB_SECNAME));
|
|
}
|
|
|
|
/* fetch the needed diffie helman parameters */
|
|
dhpdu = snmp_pdu_create(SNMP_MSG_GET);
|
|
if (!dhpdu) {
|
|
fprintf(stderr, "Failed to create DH request\n");
|
|
goto close_session;
|
|
}
|
|
|
|
/* get the current DH parameters */
|
|
snmp_add_null_var(dhpdu, usmDHParameters, usmDHParameters_len);
|
|
|
|
/* maybe the auth key public value */
|
|
if (doauthkey) {
|
|
setup_oid(dhauthKeyChange, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
session.securityName);
|
|
snmp_add_null_var(dhpdu, dhauthKeyChange, name_length);
|
|
}
|
|
|
|
/* maybe the priv key public value */
|
|
if (doprivkey) {
|
|
setup_oid(dhprivKeyChange, &name_length2,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
session.securityName);
|
|
snmp_add_null_var(dhpdu, dhprivKeyChange, name_length2);
|
|
}
|
|
|
|
/* fetch the values */
|
|
status = snmp_synch_response(ss, dhpdu, &dhresponse);
|
|
|
|
if (status != SNMPERR_SUCCESS || dhresponse == NULL ||
|
|
dhresponse->errstat != SNMP_ERR_NOERROR ||
|
|
dhresponse->variables->type != ASN_OCTET_STR) {
|
|
snmp_sess_perror("snmpusm", ss);
|
|
if (dhresponse && dhresponse->variables &&
|
|
dhresponse->variables->type != ASN_OCTET_STR) {
|
|
fprintf(stderr,
|
|
"Can't get diffie-helman exchange from the agent\n");
|
|
fprintf(stderr,
|
|
" (maybe it doesn't support the SNMP-USM-DH-OBJECTS-MIB MIB)\n");
|
|
}
|
|
exitval = 1;
|
|
goto begone;
|
|
}
|
|
|
|
dhvar = dhresponse->variables;
|
|
vars = dhvar->next_variable;
|
|
/* complete the DH equation & print resulting keys */
|
|
if (doauthkey) {
|
|
if (get_USM_DH_key(vars, dhvar,
|
|
sc_get_properlength(ss->securityAuthProto,
|
|
ss->securityAuthProtoLen),
|
|
pdu, "auth",
|
|
dhauthKeyChange, name_length) != SNMPERR_SUCCESS)
|
|
goto begone;
|
|
vars = vars->next_variable;
|
|
}
|
|
if (doprivkey) {
|
|
size_t dhprivKeyLen = 0;
|
|
int privtype = sc_get_privtype(ss->securityPrivProto,
|
|
ss->securityPrivProtoLen);
|
|
dhprivKeyLen = sc_get_proper_priv_length_bytype(privtype);
|
|
if (USM_CREATE_USER_PRIV_DES == privtype)
|
|
dhprivKeyLen *= 2; /* ?? we store salt with key */
|
|
if (get_USM_DH_key(vars, dhvar,
|
|
dhprivKeyLen,
|
|
pdu, "priv",
|
|
dhprivKeyChange, name_length2)
|
|
!= SNMPERR_SUCCESS)
|
|
goto begone;
|
|
vars = vars->next_variable;
|
|
}
|
|
/* snmp_free_pdu(dhresponse); */ /* parts still in use somewhere */
|
|
#endif /* HAVE_OPENSSL_DH_H && HAVE_LIBCRYPTO */
|
|
} else {
|
|
fprintf(stderr, "Unknown command\n");
|
|
usage();
|
|
goto close_session;
|
|
}
|
|
|
|
/*
|
|
* add usmUserPublic if specified (via -Cp)
|
|
*/
|
|
if (usmUserPublic_val) {
|
|
name_length = USM_OID_LEN;
|
|
setup_oid(usmUserPublic, &name_length,
|
|
usmUserEngineID, usmUserEngineIDLen,
|
|
session.securityName);
|
|
snmp_pdu_add_variable(pdu, usmUserPublic, name_length,
|
|
ASN_OCTET_STR, usmUserPublic_val,
|
|
strlen(usmUserPublic_val));
|
|
}
|
|
|
|
/*
|
|
* do the request
|
|
*/
|
|
status = snmp_synch_response(ss, pdu, &response);
|
|
if (status == STAT_SUCCESS) {
|
|
if (response) {
|
|
if (response->errstat == SNMP_ERR_NOERROR) {
|
|
fprintf(stdout, "%s\n", successNotes[command - 1]);
|
|
} else {
|
|
fprintf(stderr, "Error in packet.\nReason: %s\n",
|
|
snmp_errstring(response->errstat));
|
|
if (response->errindex != 0) {
|
|
int count;
|
|
netsnmp_variable_list *vars;
|
|
fprintf(stderr, "Failed object: ");
|
|
for (count = 1, vars = response->variables;
|
|
vars && count != response->errindex;
|
|
vars = vars->next_variable, count++)
|
|
/*EMPTY*/;
|
|
if (vars)
|
|
fprint_objid(stderr, vars->name,
|
|
vars->name_length);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
exitval = 2;
|
|
}
|
|
}
|
|
} else if (status == STAT_TIMEOUT) {
|
|
fprintf(stderr, "Timeout: No Response from %s\n",
|
|
session.peername);
|
|
exitval = 1;
|
|
} else { /* status == STAT_ERROR */
|
|
snmp_sess_perror("snmpset", ss);
|
|
exitval = 1;
|
|
}
|
|
|
|
exitval = 0;
|
|
#if defined(HAVE_OPENSSL_DH_H) && defined(HAVE_LIBCRYPTO)
|
|
begone:
|
|
#endif /* HAVE_OPENSSL_DH_H && HAVE_LIBCRYPTO */
|
|
if (response)
|
|
snmp_free_pdu(response);
|
|
|
|
close_session:
|
|
snmp_close(ss);
|
|
|
|
out:
|
|
SOCK_CLEANUP;
|
|
return exitval;
|
|
}
|