/* * usmUser.c * * 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 (c) 2016 VMware, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. */ #include #include #include #if HAVE_STRING_H #include #else #include #endif #include #include #include "util_funcs/header_generic.h" #include "usmUser.h" #ifndef NETSNMP_NO_WRITE_SUPPORT int usmStatusCheck(struct usmUser *uptr); #endif /* !NETSNMP_NO_WRITE_SUPPORT */ netsnmp_feature_child_of(usmuser_all, libnetsnmpmibs); netsnmp_feature_child_of(init_register_usmuser_context, usmuser_all); netsnmp_feature_require(scapi_get_proper_priv_length); struct variable4 usmUser_variables[] = { {USMUSERSPINLOCK, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, var_usmUser, 1, {1}}, {USMUSERSECURITYNAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY, var_usmUser, 3, {2, 1, 3}}, {USMUSERCLONEFROM, ASN_OBJECT_ID, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 4}}, {USMUSERAUTHPROTOCOL, ASN_OBJECT_ID, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 5}}, {USMUSERAUTHKEYCHANGE, ASN_OCTET_STR, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 6}}, {USMUSEROWNAUTHKEYCHANGE, ASN_OCTET_STR, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 7}}, {USMUSERPRIVPROTOCOL, ASN_OBJECT_ID, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 8}}, {USMUSERPRIVKEYCHANGE, ASN_OCTET_STR, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 9}}, {USMUSEROWNPRIVKEYCHANGE, ASN_OCTET_STR, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 10}}, {USMUSERPUBLIC, ASN_OCTET_STR, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 11}}, {USMUSERSTORAGETYPE, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 12}}, {USMUSERSTATUS, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE, var_usmUser, 3, {2, 1, 13}}, }; oid usmUser_variables_oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 2 }; /* * needed for the write_ functions to find the start of the index */ #define USM_MIB_LENGTH 12 #ifndef NETSNMP_NO_WRITE_SUPPORT static unsigned int usmUserSpinLock = 0; #endif void init_usmUser(void) { REGISTER_MIB("snmpv3/usmUser", usmUser_variables, variable4, usmUser_variables_oid); } #ifndef NETSNMP_FEATURE_REMOVE_INIT_REGISTER_USMUSER_CONTEXT void init_register_usmUser_context(const char *contextName) { register_mib_context("snmpv3/usmUser", (struct variable *) usmUser_variables, sizeof(struct variable4), sizeof(usmUser_variables)/sizeof(struct variable4), usmUser_variables_oid, sizeof(usmUser_variables_oid)/sizeof(oid), DEFAULT_MIB_PRIORITY, 0, 0, NULL, contextName, -1, 0); } #endif /* NETSNMP_FEATURE_REMOVE_INIT_REGISTER_USMUSER_CONTEXT */ /*******************************************************************-o-****** * usm_generate_OID * * Parameters: * *prefix (I) OID prefix to the usmUser table entry. * prefixLen (I) * *uptr (I) Pointer to a user in the user list. * *length (O) Length of generated index OID. * * Returns: * Pointer to the OID index for the user (uptr) -OR- * NULL on failure. * * * Generate the index OID for a given usmUser name. 'length' is set to * the length of the index OID. * * Index OID format is: * * <...prefix>.... */ oid * usm_generate_OID(oid * prefix, size_t prefixLen, struct usmUser *uptr, size_t * length) { oid *indexOid; int i; *length = 2 + uptr->engineIDLen + strlen(uptr->name) + prefixLen; indexOid = (oid *) malloc(*length * sizeof(oid)); if (indexOid) { memmove(indexOid, prefix, prefixLen * sizeof(oid)); indexOid[prefixLen] = uptr->engineIDLen; for (i = 0; i < (int) uptr->engineIDLen; i++) indexOid[prefixLen + 1 + i] = (oid) uptr->engineID[i]; indexOid[prefixLen + uptr->engineIDLen + 1] = strlen(uptr->name); for (i = 0; i < (int) strlen(uptr->name); i++) indexOid[prefixLen + uptr->engineIDLen + 2 + i] = (oid) uptr->name[i]; } return indexOid; } /* end usm_generate_OID() */ /* * usm_parse_oid(): parses an index to the usmTable to break it down into * a engineID component and a name component. The results are stored in: * * **engineID: a newly malloced string. * *engineIDLen: The length of the malloced engineID string above. * **name: a newly malloced string. * *nameLen: The length of the malloced name string above. * * returns 1 if an error is encountered, or 0 if successful. */ int usm_parse_oid(oid * oidIndex, size_t oidLen, unsigned char **engineID, size_t * engineIDLen, unsigned char **name, size_t * nameLen) { int nameL; int engineIDL; int i; /* * first check the validity of the oid */ if ((oidLen <= 0) || (!oidIndex)) { DEBUGMSGTL(("usmUser", "parse_oid: null oid or zero length oid passed in\n")); return 1; } engineIDL = *oidIndex; /* initial engineID length */ if ((int) oidLen < engineIDL + 2) { DEBUGMSGTL(("usmUser", "parse_oid: invalid oid length: less than the engineIDLen\n")); return 1; } nameL = oidIndex[engineIDL + 1]; /* the initial name length */ if ((int) oidLen != engineIDL + nameL + 2) { DEBUGMSGTL(("usmUser", "parse_oid: invalid oid length: length is not exact\n")); return 1; } /* * its valid, malloc the space and store the results */ if (engineID == NULL || name == NULL) { DEBUGMSGTL(("usmUser", "parse_oid: null storage pointer passed in.\n")); return 1; } *engineID = (unsigned char *) malloc(engineIDL); if (*engineID == NULL) { DEBUGMSGTL(("usmUser", "parse_oid: malloc of the engineID failed\n")); return 1; } *engineIDLen = engineIDL; *name = (unsigned char *) malloc(nameL + 1); if (*name == NULL) { DEBUGMSGTL(("usmUser", "parse_oid: malloc of the name failed\n")); free(*engineID); return 1; } *nameLen = nameL; for (i = 0; i < engineIDL; i++) { if (oidIndex[i + 1] > 255) { goto UPO_parse_error; } engineID[0][i] = (unsigned char) oidIndex[i + 1]; } for (i = 0; i < nameL; i++) { if (oidIndex[i + 2 + engineIDL] > 255) { UPO_parse_error: free(*engineID); free(*name); return 1; } name[0][i] = (unsigned char) oidIndex[i + 2 + engineIDL]; } name[0][nameL] = 0; return 0; } /* end usm_parse_oid() */ /*******************************************************************-o-****** * usm_parse_user * * Parameters: * *name Complete OID indexing a given usmUser entry. * name_length * * Returns: * Pointer to a usmUser -OR- * NULL if name does not convert to a usmUser. * * Convert an (full) OID and return a pointer to a matching user in the * user list if one exists. */ struct usmUser * usm_parse_user(oid * name, size_t name_len) { struct usmUser *uptr; char *newName; u_char *engineID; size_t nameLen, engineIDLen; /* * get the name and engineID out of the incoming oid */ if (usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH, &engineID, &engineIDLen, (u_char **) & newName, &nameLen)) return NULL; /* * Now see if a user exists with these index values */ uptr = usm_get_user(engineID, engineIDLen, newName); free(engineID); free(newName); return uptr; } /* end usm_parse_user() */ /*******************************************************************-o-****** * var_usmUser * * Parameters: * *vp (I) Variable-binding associated with this action. * *name (I/O) Input name requested, output name found. * *length (I/O) Length of input and output oid's. * exact (I) TRUE if an exact match was requested. * *var_len (O) Length of variable or 0 if function returned. * (**write_method) Hook to name a write method (UNUSED). * * Returns: * Pointer to (char *) containing related data of length 'length' * (May be NULL.) * * * Call-back function passed to the agent in order to return information * for the USM MIB tree. * * * If this invocation is not for USMUSERSPINLOCK, lookup user name * in the usmUser list. * * If the name does not match any user and the request * is for an exact match, -or- if the usmUser list is empty, create a * new list entry. * * Finally, service the given USMUSER* var-bind. A NULL user generally * results in a NULL return value. */ u_char * var_usmUser(struct variable * vp, oid * name, size_t * length, int exact, size_t * var_len, WriteMethod ** write_method) { struct usmUser *uptr = NULL, *nptr; int i, rtest, result; oid *indexOid; size_t len; /* * variables we may use later */ static long long_ret; #ifndef NETSNMP_NO_WRITE_SUPPORT static u_char string[1]; static oid objid[2]; /* for .0.0 */ #endif if (!vp || !name || !length || !var_len) return NULL; /* assume it isnt writable for the time being */ *write_method = (WriteMethod*)0; /* assume an integer and change later if not */ *var_len = sizeof(long_ret); if (vp->magic != USMUSERSPINLOCK) { oid newname[MAX_OID_LEN]; len = (*length < vp->namelen) ? *length : vp->namelen; rtest = snmp_oid_compare(name, len, vp->name, len); if (rtest > 0 || /* * (rtest == 0 && !exact && (int) vp->namelen+1 < (int) *length) || */ (exact == 1 && rtest != 0)) { if (var_len) *var_len = 0; return NULL; } memset(newname, 0, sizeof(newname)); if (((int) *length) <= (int) vp->namelen || rtest == -1) { /* * oid is not within our range yet */ /* * need to fail if not exact */ uptr = usm_get_userList(); } else { for (nptr = usm_get_userList(), uptr = NULL; nptr != NULL; nptr = nptr->next) { indexOid = usm_generate_OID(vp->name, vp->namelen, nptr, &len); result = snmp_oid_compare(name, *length, indexOid, len); DEBUGMSGTL(("9:usmUser", "Checking user: %s - ", nptr->name)); for (i = 0; i < (int) nptr->engineIDLen; i++) { DEBUGMSG(("9:usmUser", " %x", nptr->engineID[i])); } DEBUGMSG(("9:usmUser", " - %d \n -> OID: ", result)); DEBUGMSGOID(("9:usmUser", indexOid, len)); DEBUGMSG(("9:usmUser", "\n")); free(indexOid); if (exact) { if (result == 0) { uptr = nptr; } } else { if (result == 0) { /* * found an exact match. Need the next one for !exact */ uptr = nptr->next; } else if (result == -1) { uptr = nptr; break; } } } } /* endif -- name <= vp->name */ /* * if uptr is NULL and exact we need to continue for creates */ if (uptr == NULL && !exact) return (NULL); if (uptr) { indexOid = usm_generate_OID(vp->name, vp->namelen, uptr, &len); *length = len; memmove(name, indexOid, len * sizeof(oid)); DEBUGMSGTL(("usmUser", "Found user: %s - ", uptr->name)); for (i = 0; i < (int) uptr->engineIDLen; i++) { DEBUGMSG(("usmUser", " %x", uptr->engineID[i])); } DEBUGMSG(("usmUser", "\n -> OID: ")); DEBUGMSGOID(("usmUser", indexOid, len)); DEBUGMSG(("usmUser", "\n")); free(indexOid); } } else { if (header_generic(vp, name, length, exact, var_len, write_method)) return NULL; } /* endif -- vp->magic != USMUSERSPINLOCK */ switch (vp->magic) { #ifndef NETSNMP_NO_WRITE_SUPPORT case USMUSERSPINLOCK: *write_method = write_usmUserSpinLock; long_ret = usmUserSpinLock; return (unsigned char *) &long_ret; case USMUSERSECURITYNAME: if (uptr) { *var_len = strlen(uptr->secName); return (unsigned char *) uptr->secName; } return NULL; case USMUSERCLONEFROM: *write_method = write_usmUserCloneFrom; if (uptr) { objid[0] = 0; /* "When this object is read, the ZeroDotZero OID */ objid[1] = 0; /* is returned." */ *var_len = sizeof(oid) * 2; return (unsigned char *) objid; } return NULL; case USMUSERAUTHPROTOCOL: *write_method = write_usmUserAuthProtocol; if (uptr) { *var_len = uptr->authProtocolLen * sizeof(oid); return (u_char *) uptr->authProtocol; } return NULL; case USMUSERAUTHKEYCHANGE: case USMUSEROWNAUTHKEYCHANGE: /* * we treat these the same, and let the calling module * distinguish between them */ *write_method = write_usmUserAuthKeyChange; if (uptr) { *string = 0; /* always return a NULL string */ *var_len = 0; return string; } return NULL; case USMUSERPRIVPROTOCOL: *write_method = write_usmUserPrivProtocol; if (uptr) { *var_len = uptr->privProtocolLen * sizeof(oid); return (u_char *) uptr->privProtocol; } return NULL; case USMUSERPRIVKEYCHANGE: case USMUSEROWNPRIVKEYCHANGE: /* * we treat these the same, and let the calling module * distinguish between them */ *write_method = write_usmUserPrivKeyChange; if (uptr) { *string = 0; /* always return a NULL string */ *var_len = 0; return string; } return NULL; case USMUSERPUBLIC: *write_method = write_usmUserPublic; if (uptr) { if (uptr->userPublicString) { *var_len = uptr->userPublicStringLen; return uptr->userPublicString; } *string = 0; *var_len = 0; /* return an empty string if the public * string hasn't been defined yet */ return string; } return NULL; case USMUSERSTORAGETYPE: *write_method = write_usmUserStorageType; if (uptr) { long_ret = uptr->userStorageType; return (unsigned char *) &long_ret; } return NULL; case USMUSERSTATUS: *write_method = write_usmUserStatus; if (uptr) { long_ret = uptr->userStatus; return (unsigned char *) &long_ret; } return NULL; default: DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_usmUser\n", vp->magic)); #else /* !NETSNMP_NO_WRITE_SUPPORT */ default: DEBUGMSGTL(("snmpd", "no write support for var_usmUser\n")); #endif /* !NETSNMP_NO_WRITE_SUPPORT */ } return NULL; } /* end var_usmUser() */ #ifndef NETSNMP_NO_WRITE_SUPPORT /* * write_usmUserSpinLock(): called when a set is performed on the * usmUserSpinLock object */ int write_usmUserSpinLock(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { /* * variables we may use later */ static long long_ret; if (var_val_type != ASN_INTEGER) { DEBUGMSGTL(("usmUser", "write to usmUserSpinLock not ASN_INTEGER\n")); return SNMP_ERR_WRONGTYPE; } if (var_val_len > sizeof(long_ret)) { DEBUGMSGTL(("usmUser", "write to usmUserSpinLock: bad length\n")); return SNMP_ERR_WRONGLENGTH; } long_ret = *((long *) var_val); if (long_ret != (long) usmUserSpinLock) return SNMP_ERR_INCONSISTENTVALUE; if (action == COMMIT) { if (usmUserSpinLock == 2147483647) usmUserSpinLock = 0; else usmUserSpinLock++; } return SNMP_ERR_NOERROR; } /* end write_usmUserSpinLock() */ /*******************************************************************-o-****** * write_usmUserCloneFrom * * Parameters: * action * *var_val * var_val_type * var_val_len * *statP (UNUSED) * *name OID of user to clone from. * name_len * * Returns: * SNMP_ERR_NOERROR On success -OR- If user exists * and has already been cloned. * SNMP_ERR_GENERR Local function call failures. * SNMP_ERR_INCONSISTENTNAME 'name' does not exist in user list * -OR- user to clone from != RS_ACTIVE. * SNMP_ERR_WRONGLENGTH OID length > than local buffer size. * SNMP_ERR_WRONGTYPE ASN_OBJECT_ID is wrong. * * * XXX: should handle action=UNDO's. */ int write_usmUserCloneFrom(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { struct usmUser *uptr, *cloneFrom; if (action == RESERVE1) { if (var_val_type != ASN_OBJECT_ID) { DEBUGMSGTL(("usmUser", "write to usmUserCloneFrom not ASN_OBJECT_ID\n")); return SNMP_ERR_WRONGTYPE; } if (var_val_len > USM_LENGTH_OID_MAX * sizeof(oid) || var_val_len % sizeof(oid) != 0) { DEBUGMSGTL(("usmUser", "write to usmUserCloneFrom: bad length\n")); return SNMP_ERR_WRONGLENGTH; } } else if (action == RESERVE2) { if ((uptr = usm_parse_user(name, name_len)) == NULL) { /* * We don't allow creations here. */ return SNMP_ERR_INCONSISTENTNAME; } /* * Has the user already been cloned? If so, writes to this variable * are defined to have no effect and to produce no error. */ if (uptr->cloneFrom != NULL) { return SNMP_ERR_NOERROR; } cloneFrom = usm_parse_user((oid *) var_val, var_val_len / sizeof(oid)); if (cloneFrom == NULL || cloneFrom->userStatus != SNMP_ROW_ACTIVE) { return SNMP_ERR_INCONSISTENTNAME; } uptr->cloneFrom = snmp_duplicate_objid((oid *) var_val, var_val_len / sizeof(oid)); usm_cloneFrom_user(cloneFrom, uptr); if (usmStatusCheck(uptr) && uptr->userStatus == SNMP_ROW_NOTREADY) { uptr->userStatus = SNMP_ROW_NOTINSERVICE; } } return SNMP_ERR_NOERROR; } /*******************************************************************-o-****** * write_usmUserAuthProtocol * * Parameters: * action * *var_val OID of auth transform to set. * var_val_type * var_val_len * *statP * *name OID of user upon which to perform set operation. * name_len * * Returns: * SNMP_ERR_NOERROR On success. * SNMP_ERR_GENERR * SNMP_ERR_INCONSISTENTVALUE * SNMP_ERR_NOSUCHNAME * SNMP_ERR_WRONGLENGTH * SNMP_ERR_WRONGTYPE */ int write_usmUserAuthProtocol(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { static oid *optr; static size_t olen; static int resetOnFail; struct usmUser *uptr; if (action == RESERVE1) { resetOnFail = 0; if (var_val_type != ASN_OBJECT_ID) { DEBUGMSGTL(("usmUser", "write to usmUserAuthProtocol not ASN_OBJECT_ID\n")); return SNMP_ERR_WRONGTYPE; } if (var_val_len > USM_LENGTH_OID_MAX * sizeof(oid) || var_val_len % sizeof(oid) != 0) { DEBUGMSGTL(("usmUser", "write to usmUserAuthProtocol: bad length\n")); return SNMP_ERR_WRONGLENGTH; } } else if (action == RESERVE2) { if ((uptr = usm_parse_user(name, name_len)) == NULL) { return SNMP_ERR_INCONSISTENTNAME; } if (uptr->userStatus == RS_ACTIVE || uptr->userStatus == RS_NOTREADY || uptr->userStatus == RS_NOTINSERVICE) { /* * The authProtocol is already set. It is only legal to CHANGE it * to usmNoAuthProtocol... */ if (snmp_oid_compare ((oid *) var_val, var_val_len / sizeof(oid), usmNoAuthProtocol, sizeof(usmNoAuthProtocol) / sizeof(oid)) == 0) { /* * ... and then only if the privProtocol is equal to * usmNoPrivProtocol. */ if (snmp_oid_compare (uptr->privProtocol, uptr->privProtocolLen, usmNoPrivProtocol, sizeof(usmNoPrivProtocol) / sizeof(oid)) != 0) { return SNMP_ERR_INCONSISTENTVALUE; } optr = uptr->authProtocol; olen = uptr->authProtocolLen; resetOnFail = 1; uptr->authProtocol = snmp_duplicate_objid((oid *) var_val, var_val_len / sizeof(oid)); if (uptr->authProtocol == NULL) { return SNMP_ERR_RESOURCEUNAVAILABLE; } uptr->authProtocolLen = var_val_len / sizeof(oid); } else if (snmp_oid_compare ((oid *) var_val, var_val_len / sizeof(oid), uptr->authProtocol, uptr->authProtocolLen) == 0) { /* * But it's also okay to set it to the same thing as it * currently is. */ return SNMP_ERR_NOERROR; } else { return SNMP_ERR_INCONSISTENTVALUE; } } else { /* * This row is under creation. It's okay to set * usmUserAuthProtocol to any valid authProtocol but it will be * overwritten when usmUserCloneFrom is set (so don't write it if * that has already been set). */ if (snmp_oid_compare ((oid *) var_val, var_val_len / sizeof(oid), usmNoAuthProtocol, sizeof(usmNoAuthProtocol) / sizeof(oid)) == 0 #ifndef NETSNMP_DISABLE_MD5 || snmp_oid_compare((oid *) var_val, var_val_len / sizeof(oid), usmHMACMD5AuthProtocol, sizeof(usmHMACMD5AuthProtocol) / sizeof(oid)) == 0 #endif || snmp_oid_compare((oid *) var_val, var_val_len / sizeof(oid), usmHMACSHA1AuthProtocol, sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid)) == 0) { if (uptr->cloneFrom != NULL) { optr = uptr->authProtocol; olen = uptr->authProtocolLen; resetOnFail = 1; uptr->authProtocol = snmp_duplicate_objid((oid *) var_val, var_val_len / sizeof(oid)); if (uptr->authProtocol == NULL) { return SNMP_ERR_RESOURCEUNAVAILABLE; } uptr->authProtocolLen = var_val_len / sizeof(oid); } } else { /* * Unknown authentication protocol. */ return SNMP_ERR_WRONGVALUE; } } } else if (action == COMMIT) { SNMP_FREE(optr); } else if (action == FREE || action == UNDO) { if ((uptr = usm_parse_user(name, name_len)) != NULL) { if (resetOnFail) { SNMP_FREE(uptr->authProtocol); uptr->authProtocol = optr; uptr->authProtocolLen = olen; } } } return SNMP_ERR_NOERROR; } /* end write_usmUserAuthProtocol() */ /*******************************************************************-o-****** * write_usmUserAuthKeyChange * * Parameters: * action * *var_val Octet string representing new KeyChange value. * var_val_type * var_val_len * *statP (UNUSED) * *name OID of user upon which to perform set operation. * name_len * * Returns: * SNMP_ERR_NOERR Success. * SNMP_ERR_WRONGTYPE * SNMP_ERR_WRONGLENGTH * SNMP_ERR_NOSUCHNAME * SNMP_ERR_GENERR * * Note: This function handles both the usmUserAuthKeyChange and * usmUserOwnAuthKeyChange objects. We are not passed the name * of the user requseting the keychange, so we leave this to the * calling module to verify when and if we should be called. To * change this would require a change in the mib module API to * pass in the securityName requesting the change. * * XXX: should handle action=UNDO's. */ int write_usmUserAuthKeyChange(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { struct usmUser *uptr; unsigned char buf[SNMP_MAXBUF_SMALL]; size_t buflen = SNMP_MAXBUF_SMALL; const char fnAuthKey[] = "write_usmUserAuthKeyChange"; const char fnOwnAuthKey[] = "write_usmUserOwnAuthKeyChange"; const char *fname; static unsigned char *oldkey; static size_t oldkeylen; static int resetOnFail; if (name[USM_MIB_LENGTH - 1] == 6) { fname = fnAuthKey; } else { fname = fnOwnAuthKey; } if (action == RESERVE1) { resetOnFail = 0; if (var_val_type != ASN_OCTET_STR) { DEBUGMSGTL(("usmUser", "write to %s not ASN_OCTET_STR\n", fname)); return SNMP_ERR_WRONGTYPE; } if (var_val_len == 0) { return SNMP_ERR_WRONGLENGTH; } } else if (action == RESERVE2) { if ((uptr = usm_parse_user(name, name_len)) == NULL) { return SNMP_ERR_INCONSISTENTNAME; } else { #ifndef NETSNMP_DISABLE_MD5 if (snmp_oid_compare(uptr->authProtocol, uptr->authProtocolLen, usmHMACMD5AuthProtocol, sizeof(usmHMACMD5AuthProtocol) / sizeof(oid)) == 0) { if (var_val_len != 0 && var_val_len != 32) { return SNMP_ERR_WRONGLENGTH; } } else #endif if (snmp_oid_compare (uptr->authProtocol, uptr->authProtocolLen, usmHMACSHA1AuthProtocol, sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid)) == 0) { if (var_val_len != 0 && var_val_len != 40) { return SNMP_ERR_WRONGLENGTH; } } } } else if (action == ACTION) { if ((uptr = usm_parse_user(name, name_len)) == NULL) { return SNMP_ERR_INCONSISTENTNAME; } if (uptr->cloneFrom == NULL) { return SNMP_ERR_INCONSISTENTNAME; } if (snmp_oid_compare(uptr->authProtocol, uptr->authProtocolLen, usmNoAuthProtocol, sizeof(usmNoAuthProtocol) / sizeof(oid)) == 0) { /* * "When the value of the corresponding usmUserAuthProtocol is * usmNoAuthProtocol, then a set is successful, but effectively * is a no-op." */ DEBUGMSGTL(("usmUser", "%s: noAuthProtocol keyChange... success!\n", fname)); return SNMP_ERR_NOERROR; } /* * Change the key. */ DEBUGMSGTL(("usmUser", "%s: changing auth key for user %s\n", fname, uptr->secName)); if (decode_keychange(uptr->authProtocol, uptr->authProtocolLen, uptr->authKey, uptr->authKeyLen, var_val, var_val_len, buf, &buflen) != SNMPERR_SUCCESS) { DEBUGMSGTL(("usmUser", "%s: ... failed\n", fname)); return SNMP_ERR_GENERR; } DEBUGMSGTL(("usmUser", "%s: ... succeeded\n", fname)); resetOnFail = 1; oldkey = uptr->authKey; oldkeylen = uptr->authKeyLen; uptr->authKey = netsnmp_memdup(buf, buflen); if (uptr->authKey == NULL) { return SNMP_ERR_RESOURCEUNAVAILABLE; } uptr->authKeyLen = buflen; } else if (action == COMMIT) { SNMP_FREE(oldkey); } else if (action == UNDO) { if ((uptr = usm_parse_user(name, name_len)) != NULL && resetOnFail) { SNMP_FREE(uptr->authKey); uptr->authKey = oldkey; uptr->authKeyLen = oldkeylen; } } return SNMP_ERR_NOERROR; } /* end write_usmUserAuthKeyChange() */ int write_usmUserPrivProtocol(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { static oid *optr; static size_t olen; static int resetOnFail; struct usmUser *uptr; if (action == RESERVE1) { resetOnFail = 0; if (var_val_type != ASN_OBJECT_ID) { DEBUGMSGTL(("usmUser", "write to usmUserPrivProtocol not ASN_OBJECT_ID\n")); return SNMP_ERR_WRONGTYPE; } if (var_val_len > USM_LENGTH_OID_MAX * sizeof(oid) || var_val_len % sizeof(oid) != 0) { DEBUGMSGTL(("usmUser", "write to usmUserPrivProtocol: bad length\n")); return SNMP_ERR_WRONGLENGTH; } } else if (action == RESERVE2) { if ((uptr = usm_parse_user(name, name_len)) == NULL) { DEBUGMSGTL(("usmUser", "usm_parse_user() error\n")); return SNMP_ERR_INCONSISTENTNAME; } if (uptr->userStatus == RS_ACTIVE || uptr->userStatus == RS_NOTREADY || uptr->userStatus == RS_NOTINSERVICE) { /* * The privProtocol is already set. It is only legal to CHANGE it * to usmNoPrivProtocol. */ if (snmp_oid_compare ((oid *) var_val, var_val_len / sizeof(oid), usmNoPrivProtocol, sizeof(usmNoPrivProtocol) / sizeof(oid)) == 0) { resetOnFail = 1; optr = uptr->privProtocol; olen = uptr->privProtocolLen; uptr->privProtocol = snmp_duplicate_objid((oid *) var_val, var_val_len / sizeof(oid)); if (uptr->privProtocol == NULL) { DEBUGMSGTL(("usmUser", "snmp_duplicate_objid() error\n")); return SNMP_ERR_RESOURCEUNAVAILABLE; } uptr->privProtocolLen = var_val_len / sizeof(oid); } else if (snmp_oid_compare ((oid *) var_val, var_val_len / sizeof(oid), uptr->privProtocol, uptr->privProtocolLen) == 0) { /* * But it's also okay to set it to the same thing as it * currently is. */ return SNMP_ERR_NOERROR; } else { DEBUGMSGTL(("usmUser", "inconsistent value error\n")); return SNMP_ERR_INCONSISTENTVALUE; } } else { /* * This row is under creation. It's okay to set * usmUserPrivProtocol to any valid privProtocol with the proviso * that if usmUserAuthProtocol is set to usmNoAuthProtocol, it may * only be set to usmNoPrivProtocol. The value will be overwritten * when usmUserCloneFrom is set (so don't write it if that has * already been set). */ if (snmp_oid_compare(uptr->authProtocol, uptr->authProtocolLen, usmNoAuthProtocol, sizeof(usmNoAuthProtocol) / sizeof(oid)) == 0) { if (snmp_oid_compare ((oid *) var_val, var_val_len / sizeof(oid), usmNoPrivProtocol, sizeof(usmNoPrivProtocol) / sizeof(oid)) != 0) { DEBUGMSGTL(("usmUser", "inconsistent value error\n")); return SNMP_ERR_INCONSISTENTVALUE; } } else { if (snmp_oid_compare ((oid *) var_val, var_val_len / sizeof(oid), usmNoPrivProtocol, sizeof(usmNoPrivProtocol) / sizeof(oid)) != 0 #ifndef NETSNMP_DISABLE_DES && snmp_oid_compare ((oid *) var_val, var_val_len / sizeof(oid), usmDESPrivProtocol, sizeof(usmDESPrivProtocol) / sizeof(oid)) != 0 #endif && snmp_oid_compare ((oid *) var_val, var_val_len / sizeof(oid), usmAESPrivProtocol, sizeof(usmAESPrivProtocol) / sizeof(oid)) != 0) { DEBUGMSGTL(("usmUser", "wrong value error\n")); return SNMP_ERR_WRONGVALUE; } } resetOnFail = 1; optr = uptr->privProtocol; olen = uptr->privProtocolLen; uptr->privProtocol = snmp_duplicate_objid((oid *) var_val, var_val_len / sizeof(oid)); if (uptr->privProtocol == NULL) { DEBUGMSGTL(("usmUser", "resource unavailable error\n")); return SNMP_ERR_RESOURCEUNAVAILABLE; } uptr->privProtocolLen = var_val_len / sizeof(oid); } } else if (action == COMMIT) { SNMP_FREE(optr); } else if (action == FREE || action == UNDO) { if ((uptr = usm_parse_user(name, name_len)) != NULL) { if (resetOnFail) { SNMP_FREE(uptr->privProtocol); uptr->privProtocol = optr; uptr->privProtocolLen = olen; } } } return SNMP_ERR_NOERROR; } /* end write_usmUserPrivProtocol() */ /* * Note: This function handles both the usmUserPrivKeyChange and * usmUserOwnPrivKeyChange objects. We are not passed the name * of the user requseting the keychange, so we leave this to the * calling module to verify when and if we should be called. To * change this would require a change in the mib module API to * pass in the securityName requesting the change. * */ int write_usmUserPrivKeyChange(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { struct usmUser *uptr; unsigned char buf[SNMP_MAXBUF_SMALL], buf2[SNMP_MAXBUF_SMALL]; size_t buflen = sizeof(buf); const char fnPrivKey[] = "write_usmUserPrivKeyChange"; const char fnOwnPrivKey[] = "write_usmUserOwnPrivKeyChange"; const char *fname; static unsigned char *oldkey; static size_t oldkeylen; static int resetOnFail; int plen; if (name[USM_MIB_LENGTH - 1] == 9) { fname = fnPrivKey; } else { fname = fnOwnPrivKey; } if (action == RESERVE1) { resetOnFail = 0; if (var_val_type != ASN_OCTET_STR) { DEBUGMSGTL(("usmUser", "write to %s not ASN_OCTET_STR\n", fname)); return SNMP_ERR_WRONGTYPE; } if (var_val_len == 0) { return SNMP_ERR_WRONGLENGTH; } } else if (action == RESERVE2) { if ((uptr = usm_parse_user(name, name_len)) == NULL) { return SNMP_ERR_INCONSISTENTNAME; } else { const netsnmp_priv_alg_info *pai = sc_get_priv_alg_byoid(uptr->privProtocol, uptr->privProtocolLen); if (NULL == pai) { DEBUGMSGTL(("usmUser", "%s: unknown privProtocol\n", fname)); return SNMP_ERR_GENERR; } plen = pai->proper_length; DEBUGMSGTL(("usmUser", "plen %d\n", plen)); /* * ?? we store salt with key. See also the corresponding statement * in apps/snmpusm.c. */ if (USM_CREATE_USER_PRIV_DES == pai->type) plen *= 2; if (var_val_len != 0 && var_val_len != (2 * plen)) { DEBUGMSGTL(("usmUser", "%s: bad len. %" NETSNMP_PRIz "d != %d\n", fname, var_val_len, 2 * plen)); return SNMP_ERR_WRONGLENGTH; } } } else if (action == ACTION) { if ((uptr = usm_parse_user(name, name_len)) == NULL) { return SNMP_ERR_INCONSISTENTNAME; } if (uptr->cloneFrom == NULL) { return SNMP_ERR_INCONSISTENTNAME; } plen = sc_get_proper_priv_length(uptr->privProtocol, uptr->privProtocolLen); DEBUGMSGTL(("usmUser", "plen %d\n", plen)); if (snmp_oid_compare(uptr->privProtocol, uptr->privProtocolLen, usmNoPrivProtocol, sizeof(usmNoPrivProtocol) / sizeof(oid)) == 0) { /* * "When the value of the corresponding usmUserPrivProtocol is * usmNoPrivProtocol, then a set is successful, but effectively * is a no-op." */ DEBUGMSGTL(("usmUser", "%s: noPrivProtocol keyChange... success!\n", fname)); return SNMP_ERR_NOERROR; } /* * extend key as needed */ DEBUGMSGTL(("9:usmUser", "%s: var_val_len %" NETSNMP_PRIz "d\n", fname, var_val_len)); if (var_val_len < ( 2 * plen )) { struct usmUser dummy; memset(&dummy, 0x0, sizeof(dummy)); dummy.engineID = uptr->engineID; dummy.engineIDLen = uptr->engineIDLen; dummy.authProtocol = uptr->authProtocol; dummy.authProtocolLen = uptr->authProtocolLen; dummy.privProtocol = uptr->privProtocol; dummy.privProtocolLen = uptr->privProtocolLen; memcpy(buf2, var_val, var_val_len); dummy.privKey = buf2; dummy.privKeyLen = var_val_len; if (SNMP_ERR_NOERROR != usm_extend_user_kul(&dummy, sizeof(buf2))) { DEBUGMSGTL(("usmUser", "%s: extend kul failed\n", fname)); return SNMP_ERR_GENERR; } DEBUGMSGTL(("9:usmUser", "%s: extend kul OK\n", fname)); var_val = dummy.privKey; var_val_len = dummy.privKeyLen; /* * make sure no reallocation happened; buf2 must be large enoungh */ netsnmp_assert( dummy.privKey == buf2 ); } /* * Change the key. */ DEBUGMSGTL(("usmUser", "%s: changing priv key for user %s\n", fname, uptr->secName)); if (decode_keychange(uptr->authProtocol, uptr->authProtocolLen, uptr->privKey, uptr->privKeyLen, var_val, var_val_len, buf, &buflen) != SNMPERR_SUCCESS) { DEBUGMSGTL(("usmUser", "%s: ... failed\n", fname)); return SNMP_ERR_GENERR; } DEBUGMSGTL(("usmUser", "%s: ... succeeded\n", fname)); resetOnFail = 1; oldkey = uptr->privKey; oldkeylen = uptr->privKeyLen; uptr->privKey = netsnmp_memdup(buf, buflen); if (uptr->privKey == NULL) { return SNMP_ERR_RESOURCEUNAVAILABLE; } uptr->privKeyLen = buflen; } else if (action == COMMIT) { SNMP_FREE(oldkey); } else if (action == UNDO) { if ((uptr = usm_parse_user(name, name_len)) != NULL && resetOnFail) { SNMP_FREE(uptr->privKey); uptr->privKey = oldkey; uptr->privKeyLen = oldkeylen; } } return SNMP_ERR_NOERROR; } /* end write_usmUserPrivKeyChange() */ int write_usmUserPublic(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { struct usmUser *uptr = NULL; if (var_val_type != ASN_OCTET_STR) { DEBUGMSGTL(("usmUser", "write to usmUserPublic not ASN_OCTET_STR\n")); return SNMP_ERR_WRONGTYPE; } if (var_val_len > 32) { DEBUGMSGTL(("usmUser", "write to usmUserPublic: bad length\n")); return SNMP_ERR_WRONGLENGTH; } if (action == COMMIT) { /* * don't allow creations here */ if ((uptr = usm_parse_user(name, name_len)) == NULL) { return SNMP_ERR_NOSUCHNAME; } if (uptr->userPublicString) free(uptr->userPublicString); uptr->userPublicString = (u_char *) malloc(var_val_len); if (uptr->userPublicString == NULL) { return SNMP_ERR_GENERR; } memcpy(uptr->userPublicString, var_val, var_val_len); uptr->userPublicStringLen = var_val_len; DEBUGMSG(("usmUser", "setting public string: %d - ", (int)var_val_len)); DEBUGMSGHEX(("usmUser", uptr->userPublicString, var_val_len)); DEBUGMSG(("usmUser", "\n")); } return SNMP_ERR_NOERROR; } /* end write_usmUserPublic() */ int write_usmUserStorageType(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { long long_ret = *((long *) var_val); static long oldValue; struct usmUser *uptr; static int resetOnFail; if (action == RESERVE1) { resetOnFail = 0; if (var_val_type != ASN_INTEGER) { DEBUGMSGTL(("usmUser", "write to usmUserStorageType not ASN_INTEGER\n")); return SNMP_ERR_WRONGTYPE; } if (var_val_len != sizeof(long)) { DEBUGMSGTL(("usmUser", "write to usmUserStorageType: bad length\n")); return SNMP_ERR_WRONGLENGTH; } if (long_ret < 1 || long_ret > 5) { return SNMP_ERR_WRONGVALUE; } } else if (action == RESERVE2) { if ((uptr = usm_parse_user(name, name_len)) == NULL) { return SNMP_ERR_INCONSISTENTNAME; } if ((long_ret == ST_VOLATILE || long_ret == ST_NONVOLATILE) && (uptr->userStorageType == ST_VOLATILE || uptr->userStorageType == ST_NONVOLATILE)) { oldValue = uptr->userStorageType; uptr->userStorageType = long_ret; resetOnFail = 1; } else { /* * From RFC2574: * * "Note that any user who employs authentication or privacy must * allow its secret(s) to be updated and thus cannot be 'readOnly'. * * If an initial set operation tries to set the value to 'readOnly' * for a user who employs authentication or privacy, then an * 'inconsistentValue' error must be returned. Note that if the * value has been previously set (implicit or explicit) to any * value, then the rules as defined in the StorageType Textual * Convention apply. */ DEBUGMSGTL(("usmUser", "long_ret %ld uptr->st %d uptr->status %d\n", long_ret, uptr->userStorageType, uptr->userStatus)); if (long_ret == ST_READONLY && uptr->userStorageType != ST_READONLY && (uptr->userStatus == RS_ACTIVE || uptr->userStatus == RS_NOTINSERVICE)) { return SNMP_ERR_WRONGVALUE; } else if (long_ret == ST_READONLY && (snmp_oid_compare (uptr->privProtocol, uptr->privProtocolLen, usmNoPrivProtocol, sizeof(usmNoPrivProtocol) / sizeof(oid)) != 0 || snmp_oid_compare(uptr->authProtocol, uptr->authProtocolLen, usmNoAuthProtocol, sizeof(usmNoAuthProtocol) / sizeof(oid)) != 0)) { return SNMP_ERR_INCONSISTENTVALUE; } else { return SNMP_ERR_WRONGVALUE; } } } else if (action == UNDO || action == FREE) { if ((uptr = usm_parse_user(name, name_len)) != NULL && resetOnFail) { uptr->userStorageType = oldValue; } } return SNMP_ERR_NOERROR; } /* end write_usmUserStorageType() */ /* * Return 1 if enough objects have been set up to transition rowStatus to * notInService(2) or active(1). */ int usmStatusCheck(struct usmUser *uptr) { if (uptr == NULL) { return 0; } else { if (uptr->cloneFrom == NULL) { return 0; } } return 1; } /*******************************************************************-o-****** * write_usmUserStatus * * Parameters: * action * *var_val * var_val_type * var_val_len * *statP * *name * name_len * * Returns: * SNMP_ERR_NOERROR On success. * SNMP_ERR_GENERR * SNMP_ERR_INCONSISTENTNAME * SNMP_ERR_INCONSISTENTVALUE * SNMP_ERR_WRONGLENGTH * SNMP_ERR_WRONGTYPE */ int write_usmUserStatus(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { /* * variables we may use later */ static long long_ret; unsigned char *engineID; size_t engineIDLen; char *newName; size_t nameLen; struct usmUser *uptr = NULL; if (action == RESERVE1) { if (var_val_type != ASN_INTEGER) { DEBUGMSGTL(("usmUser", "write to usmUserStatus not ASN_INTEGER\n")); return SNMP_ERR_WRONGTYPE; } if (var_val_len != sizeof(long_ret)) { DEBUGMSGTL(("usmUser", "write to usmUserStatus: bad length\n")); return SNMP_ERR_WRONGLENGTH; } long_ret = *((long *) var_val); if (long_ret == RS_NOTREADY || long_ret < 1 || long_ret > 6) { return SNMP_ERR_WRONGVALUE; } /* * See if we can parse the oid for engineID/name first. */ if (usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH, &engineID, &engineIDLen, (u_char **) & newName, &nameLen)) { DEBUGMSGTL(("usmUser", "can't parse the OID for engineID or name\n")); return SNMP_ERR_INCONSISTENTNAME; } if (engineIDLen < 5 || engineIDLen > 32 || nameLen < 1 || nameLen > 32) { SNMP_FREE(engineID); SNMP_FREE(newName); return SNMP_ERR_NOCREATION; } /* * Now see if a user already exists with these index values. */ uptr = usm_get_user(engineID, engineIDLen, newName); if (uptr != NULL) { if (long_ret == RS_CREATEANDGO || long_ret == RS_CREATEANDWAIT) { SNMP_FREE(engineID); SNMP_FREE(newName); long_ret = RS_NOTREADY; return SNMP_ERR_INCONSISTENTVALUE; } SNMP_FREE(engineID); SNMP_FREE(newName); } else { if (long_ret == RS_ACTIVE || long_ret == RS_NOTINSERVICE) { SNMP_FREE(engineID); SNMP_FREE(newName); return SNMP_ERR_INCONSISTENTVALUE; } if (long_ret == RS_CREATEANDGO || long_ret == RS_CREATEANDWAIT) { if ((uptr = usm_create_user()) == NULL) { SNMP_FREE(engineID); SNMP_FREE(newName); return SNMP_ERR_RESOURCEUNAVAILABLE; } uptr->engineID = engineID; uptr->name = newName; uptr->secName = strdup(uptr->name); if (uptr->secName == NULL) { usm_free_user(uptr); return SNMP_ERR_RESOURCEUNAVAILABLE; } uptr->engineIDLen = engineIDLen; /* * Set status to createAndGo or createAndWait so we can tell * that this row is under creation. */ uptr->userStatus = long_ret; /* * Add to the list of users (we will take it off again * later if something goes wrong). */ usm_add_user(uptr); } else { SNMP_FREE(engineID); SNMP_FREE(newName); } } } else if (action == ACTION) { usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH, &engineID, &engineIDLen, (u_char **) & newName, &nameLen); uptr = usm_get_user(engineID, engineIDLen, newName); SNMP_FREE(engineID); SNMP_FREE(newName); if (uptr != NULL) { if (long_ret == RS_CREATEANDGO || long_ret == RS_ACTIVE) { if (usmStatusCheck(uptr)) { uptr->userStatus = RS_ACTIVE; } else { SNMP_FREE(engineID); SNMP_FREE(newName); return SNMP_ERR_INCONSISTENTVALUE; } } else if (long_ret == RS_CREATEANDWAIT) { if (usmStatusCheck(uptr)) { uptr->userStatus = RS_NOTINSERVICE; } else { uptr->userStatus = RS_NOTREADY; } } else if (long_ret == RS_NOTINSERVICE) { if (uptr->userStatus == RS_ACTIVE || uptr->userStatus == RS_NOTINSERVICE) { uptr->userStatus = RS_NOTINSERVICE; } else { return SNMP_ERR_INCONSISTENTVALUE; } } } } else if (action == COMMIT) { usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH, &engineID, &engineIDLen, (u_char **) & newName, &nameLen); uptr = usm_get_user(engineID, engineIDLen, newName); SNMP_FREE(engineID); SNMP_FREE(newName); if (uptr != NULL) { if (long_ret == RS_DESTROY) { usm_remove_user(uptr); usm_free_user(uptr); } } } else if (action == UNDO || action == FREE) { if (usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH, &engineID, &engineIDLen, (u_char **) & newName, &nameLen)) { /* Can't extract engine info from the OID - nothing to undo */ return SNMP_ERR_NOERROR; } uptr = usm_get_user(engineID, engineIDLen, newName); SNMP_FREE(engineID); SNMP_FREE(newName); if (long_ret == RS_CREATEANDGO || long_ret == RS_CREATEANDWAIT) { usm_remove_user(uptr); usm_free_user(uptr); } } return SNMP_ERR_NOERROR; } /* write_usmUserStatus */ #endif /* !NETSNMP_NO_WRITE_SUPPORT */ #if 0 /* * see if we can parse the oid for engineID/name first */ if (usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH, &engineID, &engineIDLen, (u_char **) & newName, &nameLen)) return SNMP_ERR_INCONSISTENTNAME; /* * Now see if a user already exists with these index values */ uptr = usm_get_user(engineID, engineIDLen, newName); if (uptr) { /* If so, we set the appropriate value... */ free(engineID); free(newName); if (long_ret == RS_CREATEANDGO || long_ret == RS_CREATEANDWAIT) { return SNMP_ERR_INCONSISTENTVALUE; } if (long_ret == RS_DESTROY) { usm_remove_user(uptr); usm_free_user(uptr); } else { uptr->userStatus = long_ret; } } else { /* ...else we create a new user */ /* * check for a valid status column set */ if (long_ret == RS_ACTIVE || long_ret == RS_NOTINSERVICE) { free(engineID); free(newName); return SNMP_ERR_INCONSISTENTVALUE; } if (long_ret == RS_DESTROY) { /* * destroying a non-existent row is actually legal */ free(engineID); free(newName); return SNMP_ERR_NOERROR; } /* * generate a new user */ if ((uptr = usm_create_user()) == NULL) { free(engineID); free(newName); return SNMP_ERR_GENERR; } /* * copy in the engineID */ uptr->engineID = (unsigned char *) malloc(engineIDLen); if (uptr->engineID == NULL) { free(engineID); free(newName); usm_free_user(uptr); return SNMP_ERR_GENERR; } uptr->engineIDLen = engineIDLen; memcpy(uptr->engineID, engineID, engineIDLen); free(engineID); /* * copy in the name and secname */ if ((uptr->name = strdup(newName)) == NULL) { free(newName); usm_free_user(uptr); return SNMP_ERR_GENERR; } free(newName); if ((uptr->secName = strdup(uptr->name)) == NULL) { usm_free_user(uptr); return SNMP_ERR_GENERR; } /* * set the status of the row based on the request */ if (long_ret == RS_CREATEANDGO) uptr->userStatus = RS_ACTIVE; else if (long_ret == RS_CREATEANDWAIT) uptr->userStatus = RS_NOTINSERVICE; /* * finally, add it to our list of users */ usm_add_user(uptr); } /* endif -- uptr */ } /* endif -- action==COMMIT */ return SNMP_ERR_NOERROR; } /* end write_usmUserStatus() */ #endif