Exemplo n.º 1
0
/*
 * sc_get_properlength(oid *hashtype, u_int hashtype_len):
 * 
 * Given a hashing type ("hashtype" and its length hashtype_len), return
 * the length of the hash result.
 * 
 * Returns either the length or SNMPERR_GENERR for an unknown hashing type.
 */
int
sc_get_properlength(const oid * hashtype, u_int hashtype_len)
{
    DEBUGTRACE;
    /*
     * Determine transform type hash length.
     */
    if (ISTRANSFORM(hashtype, HMACMD5Auth)) {
        return BYTESIZE(SNMP_TRANS_AUTHLEN_HMACMD5);
    } else if (ISTRANSFORM(hashtype, HMACSHA1Auth)) {
        return BYTESIZE(SNMP_TRANS_AUTHLEN_HMACSHA1);
    }
    return SNMPERR_GENERR;
}
int
sc_get_proper_priv_length(const oid * privtype, u_int privtype_len)
{
    int properlength = 0;
#ifndef NETSNMP_DISABLE_DES
    if (ISTRANSFORM(privtype, DESPriv)) {
        properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES);
    }
#endif
#ifdef HAVE_AES
    if (ISTRANSFORM(privtype, AESPriv)) {
        properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_AES);
    }
#endif
    return properlength;
}
Exemplo n.º 3
0
/*******************************************************************-o-******
 * generate_Ku
 *
 * Parameters:
 *	*hashtype	MIB OID for the transform type for hashing.
 *	 hashtype_len	Length of OID value.
 *	*P		Pre-allocated bytes of passpharase.
 *	 pplen		Length of passphrase.
 *	*Ku		Buffer to contain Ku.
 *	*kulen		Length of Ku buffer.
 *      
 * Returns:
 *	SNMPERR_SUCCESS			Success.
 *	SNMPERR_GENERR			All errors.
 *
 *
 * Convert a passphrase into a master user key, Ku, according to the
 * algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM)
 * as follows:
 *
 * Expand the passphrase to fill the passphrase buffer space, if necessary,
 * concatenation as many duplicates as possible of P to itself.  If P is
 * larger than the buffer space, truncate it to fit.
 *
 * Then hash the result with the given hashtype transform.  Return
 * the result as Ku.
 *
 * If successful, kulen contains the size of the hash written to Ku.
 *
 * NOTE  Passphrases less than USM_LENGTH_P_MIN characters in length
 *	 cause an error to be returned.
 *	 (Punt this check to the cmdline apps?  XXX)
 */
int
generate_Ku(const oid * hashtype, u_int hashtype_len,
            const u_char * P, size_t pplen, u_char * Ku, size_t * kulen)
#if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_INTERNAL_CRYPTO)
{
    int             rval = SNMPERR_SUCCESS,
        nbytes = USM_LENGTH_EXPANDED_PASSPHRASE;
#if !defined(NETSNMP_USE_OPENSSL) && \
    defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_INTERNAL_CRYPTO)
    int             ret;
#endif

    u_int           i, pindex = 0;

    u_char          buf[USM_LENGTH_KU_HASHBLOCK], *bufp;

#ifdef NETSNMP_USE_OPENSSL
    EVP_MD_CTX     *ctx = NULL;
#elif NETSNMP_USE_INTERNAL_CRYPTO
    SHA_CTX csha1;
    MD5_CTX cmd5;
    char    cryptotype = 0;
#define TYPE_MD5  1
#define TYPE_SHA1 2
#else
    MDstruct        MD;
#endif
    /*
     * Sanity check.
     */
    if (!hashtype || !P || !Ku || !kulen || (*kulen <= 0)
        || (hashtype_len != USM_LENGTH_OID_TRANSFORM)) {
        QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
    }

    if (pplen < USM_LENGTH_P_MIN) {
        snmp_log(LOG_ERR, "Error: passphrase chosen is below the length "
                 "requirements of the USM (min=%d).\n",USM_LENGTH_P_MIN);
        snmp_set_detail("The supplied password length is too short.");
        QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
    }


    /*
     * Setup for the transform type.
     */
#ifdef NETSNMP_USE_OPENSSL

#ifdef HAVE_EVP_MD_CTX_CREATE
    ctx = EVP_MD_CTX_create();
#else
    ctx = malloc(sizeof(*ctx));
    EVP_MD_CTX_init(ctx);
#endif
#ifndef NETSNMP_DISABLE_MD5
    if (ISTRANSFORM(hashtype, HMACMD5Auth))
        EVP_DigestInit(ctx, EVP_md5());
    else
#endif
        if (ISTRANSFORM(hashtype, HMACSHA1Auth))
        EVP_DigestInit(ctx, EVP_sha1());
    else
        QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
#elif NETSNMP_USE_INTERNAL_CRYPTO
#ifndef NETSNMP_DISABLE_MD5
    if (ISTRANSFORM(hashtype, HMACMD5Auth)) {
        MD5_Init(&cmd5);
        cryptotype = TYPE_MD5;
    } else
#endif
           if (ISTRANSFORM(hashtype, HMACSHA1Auth)) {
        SHA1_Init(&csha1);
        cryptotype = TYPE_SHA1;
    } else {
        return (SNMPERR_GENERR);
    }
#else
    MDbegin(&MD);
#endif                          /* NETSNMP_USE_OPENSSL */

    while (nbytes > 0) {
        bufp = buf;
        for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) {
            *bufp++ = P[pindex++ % pplen];
        }
#ifdef NETSNMP_USE_OPENSSL
        EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK);
#elif NETSNMP_USE_INTERNAL_CRYPTO
        if (TYPE_SHA1 == cryptotype) {
            rval = !SHA1_Update(&csha1, buf, USM_LENGTH_KU_HASHBLOCK);
        } else {
            rval = !MD5_Update(&cmd5, buf, USM_LENGTH_KU_HASHBLOCK);
        }
        if (rval != 0) {
            return SNMPERR_USM_ENCRYPTIONERROR;
        }
#elif NETSNMP_USE_INTERNAL_MD5
        if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK * 8)) {
            rval = SNMPERR_USM_ENCRYPTIONERROR;
            goto md5_fin;
        }
#endif                          /* NETSNMP_USE_OPENSSL */
        nbytes -= USM_LENGTH_KU_HASHBLOCK;
    }

#ifdef NETSNMP_USE_OPENSSL
    {
    unsigned int    tmp_len;

    tmp_len = *kulen;
    EVP_DigestFinal(ctx, (unsigned char *) Ku, &tmp_len);
    *kulen = tmp_len;
    /*
     * what about free() 
     */
    }
#elif NETSNMP_USE_INTERNAL_CRYPTO
    if (TYPE_SHA1 == cryptotype) {
        SHA1_Final(Ku, &csha1);
    } else {
        MD5_Final(Ku, &cmd5);
    }
    ret = sc_get_properlength(hashtype, hashtype_len);
    if (ret == SNMPERR_GENERR)
        return SNMPERR_GENERR;
    *kulen = ret;
#elif NETSNMP_USE_INTERNAL_MD5
    if (MDupdate(&MD, buf, 0)) {
        rval = SNMPERR_USM_ENCRYPTIONERROR;
        goto md5_fin;
    }
    ret = sc_get_properlength(hashtype, hashtype_len);
    if (ret == SNMPERR_GENERR)
        return SNMPERR_GENERR;
    *kulen = ret;
    MDget(&MD, Ku, *kulen);
  md5_fin:
    memset(&MD, 0, sizeof(MD));
#endif                          /* NETSNMP_USE_INTERNAL_MD5 */


#ifdef NETSNMP_ENABLE_TESTING_CODE
    DEBUGMSGTL(("generate_Ku", "generating Ku (from %s): ", P));
    for (i = 0; i < *kulen; i++)
        DEBUGMSG(("generate_Ku", "%02x", Ku[i]));
    DEBUGMSG(("generate_Ku", "\n"));
#endif                          /* NETSNMP_ENABLE_TESTING_CODE */


  generate_Ku_quit:
    memset(buf, 0, sizeof(buf));
#ifdef NETSNMP_USE_OPENSSL
    if (ctx) {
#ifdef HAVE_EVP_MD_CTX_DESTROY
        EVP_MD_CTX_destroy(ctx);
#else
        EVP_MD_CTX_cleanup(ctx);
        free(ctx);
#endif
    }
#endif
    return rval;

}                               /* end generate_Ku() */
Exemplo n.º 4
0
_SCAPI_NOT_CONFIGURED
#endif	/* USE_INTERNAL_MD5 */



/*******************************************************************-o-******
 * sc_encrypt
 *
 * Parameters:
 *	 privtype	Type of privacy cryptographic transform.
 *	*key		Key bits for crypting.
 *	 keylen		Length of key (buffer) in bytes.
 *	*iv		IV bits for crypting.
 *	 ivlen		Length of iv (buffer) in bytes.
 *	*plaintext	Plaintext to crypt.
 *	 ptlen		Length of plaintext.
 *	*ciphertext	Ciphertext to crypt.
 *	*ctlen		Length of ciphertext.
 *      
 * Returns:
 *	SNMPERR_SUCCESS			Success.
 *	SNMPERR_SC_NOT_CONFIGURED	Encryption is not supported.
 *	SNMPERR_SC_GENERAL_FAILURE	Any other error
 *
 *
 * Encrypt plaintext into ciphertext using key and iv.
 *
 * ctlen contains actual number of crypted bytes in ciphertext upon
 * successful return.
 */
int
sc_encrypt(	oid    *privtype,	size_t privtypelen,
		u_char *key,		u_int  keylen,
		u_char *iv,		u_int  ivlen,
		u_char *plaintext,	u_int  ptlen,
		u_char *ciphertext,	size_t *ctlen)
#if defined(USE_OPENSSL) 
{
	int		rval	= SNMPERR_SUCCESS;
	u_int		transform,
			properlength,
			properlength_iv;
	u_char		pad_block[32];  /* bigger than anything I need */
	u_char          my_iv[32];      /* ditto */
	int		pad, plast, pad_size;
	des_key_schedule key_sch;
	des_cblock      key_struct;

        DEBUGTRACE;

	/*
	 * Sanity check.
	 */
#if	!defined(SCAPI_AUTHPRIV)
		return SNMPERR_SC_NOT_CONFIGURED;
#endif

	if ( !privtype || !key || !iv || !plaintext || !ciphertext || !ctlen
		|| (keylen<=0) || (ivlen<=0) || (ptlen<=0) || (*ctlen<=0)
		|| (privtypelen != USM_LENGTH_OID_TRANSFORM) )
	{
		QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
	}
	else if ( ptlen >= *ctlen) { 
		QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
	}


#ifdef SNMP_TESTING_CODE
{
        char buf[SNMP_MAXBUF];

	sprint_hexstring(buf, iv, ivlen);
        DEBUGMSGTL(("scapi", "encrypt: IV: %s/ ", buf));
	sprint_hexstring(buf, key, keylen);
        DEBUGMSG(("scapi","%s\n", buf));

	sprint_hexstring(buf, plaintext, 16);
        DEBUGMSGTL(("scapi","encrypt: string: %s\n", buf));
}
#endif /* SNMP_TESTING_CODE */


	/*
	 * Determine privacy transform.
	 */
	if ( ISTRANSFORM(privtype, DESPriv) ) {
		properlength	= BYTESIZE(SNMP_TRANS_PRIVLEN_1DES);
		properlength_iv	= BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV);
		pad_size = properlength;
	} else {
		QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
	}

	if ( (keylen<properlength) || (ivlen<properlength_iv) ) {
		QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
	}

	else if ( (keylen<properlength) || (ivlen<properlength_iv) ) {
		QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
	}

/* now calculate the padding needed */
	pad = pad_size - (ptlen % pad_size);
	if (ptlen + pad > *ctlen) { 
		QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); /* not enough space */
	}
	memset(pad_block, 0, sizeof(pad_block));
	plast = (int) ptlen - (pad_size - pad);
	if (pad > 0)  /* copy data into pad block if needed */
		memcpy( pad_block, plaintext + plast, pad_size - pad);
	memset(&pad_block[pad_size-pad], pad, pad); /* filling in padblock */

	memset(my_iv, 0, sizeof(my_iv));

	if ( ISTRANSFORM(privtype, DESPriv) ) {
                memcpy(key_struct, key, sizeof(key_struct));
		(void) des_key_sched(&key_struct, key_sch);

		memcpy(my_iv, iv, ivlen);
		/* encrypt the data */
		des_ncbc_encrypt(plaintext, ciphertext, plast, key_sch, 
				 (des_cblock *) &my_iv, DES_ENCRYPT);
		/* then encrypt the pad block */
		des_ncbc_encrypt(pad_block, ciphertext+plast, pad_size, 
				 key_sch, (des_cblock *)&my_iv, DES_ENCRYPT);
		*ctlen = plast + pad_size;
	}
sc_encrypt_quit:
	/* clear memory just in case */
	memset(my_iv, 0, sizeof(my_iv));
	memset(pad_block, 0, sizeof(pad_block));
	memset(key_struct, 0, sizeof(key_struct));
	memset(key_sch, 0, sizeof(key_sch));
	return rval;

}  /* end sc_encrypt() */
Exemplo n.º 5
0
_SCAPI_NOT_CONFIGURED
#endif							/* */


/* sc_hash(): a generic wrapper around whatever hashing package we are using.

   IN:
     hashtype    - oid pointer to a hash type
     hashtypelen - length of oid pointer
     buf         - u_char buffer to be hashed
     buf_len     - integer length of buf data
     MAC_len     - length of the passed MAC buffer size.
    
   OUT:    
     MAC         - pre-malloced space to store hash output.
     MAC_len     - length of MAC output to the MAC buffer.

   Returns:
     SNMPERR_SUCCESS		Success.
     SNMP_SC_GENERAL_FAILURE	Any error.
*/

int
sc_hash(oid *hashtype, size_t hashtypelen, u_char *buf, size_t buf_len,
        u_char *MAC, size_t *MAC_len)
#if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL)
{
  int   rval       = SNMPERR_SUCCESS;

#ifdef USE_OPENSSL 
  EVP_MD *hash(void);
  HMAC_CTX *c = NULL; 
#endif

  DEBUGTRACE;

  if (hashtype == NULL || hashtypelen < 0 || buf == NULL ||
      buf_len < 0 || MAC == NULL ||  MAC_len == NULL ||
      (int)(*MAC_len) < sc_get_properlength(hashtype, hashtypelen))
    return (SNMPERR_GENERR);

#ifdef USE_OPENSSL 
  /*
   * Determine transform type.
   */
  c = malloc(sizeof(HMAC_CTX));
  if (c == NULL)
    return (SNMPERR_GENERR);

  if (ISTRANSFORM(hashtype, HMACMD5Auth)) {
    EVP_DigestInit(&c->md_ctx, (const EVP_MD *) EVP_md5());
  }
  else if (ISTRANSFORM(hashtype, HMACSHA1Auth)) {
    EVP_DigestInit(&c->md_ctx, (const EVP_MD *) EVP_sha1());
  }
  else {
    return(SNMPERR_GENERR);
  }
  EVP_DigestUpdate(&c->md_ctx, buf, buf_len);
  EVP_DigestFinal(&(c->md_ctx), MAC, MAC_len);
  free(c);
  return (rval);
#else /* USE_INTERNAL_MD5 */

  if (MDchecksum(buf, buf_len, MAC, *MAC_len)) {
    return SNMPERR_GENERR;
  }
  if (*MAC_len > 16)
    *MAC_len = 16;
  return SNMPERR_SUCCESS;

#endif /* USE_OPENSSL */
}
Exemplo n.º 6
0
_SCAPI_NOT_CONFIGURED
#endif							/*  */



/*******************************************************************-o-******
 * sc_generate_keyed_hash
 *
 * Parameters:
 *	 authtype	Type of authentication transform.
 *	 authtypelen
 *	*key		Pointer to key (Kul) to use in keyed hash.
 *	 keylen		Length of key in bytes.
 *	*message	Pointer to the message to hash.
 *	 msglen		Length of the message.
 *	*MAC		Will be returned with allocated bytes containg hash.
 *	*maclen		Length of the hash buffer in bytes; also indicates
 *				whether the MAC should be truncated.
 *      
 * Returns:
 *	SNMPERR_SUCCESS			Success.
 *	SNMPERR_GENERR			All errs
 *
 *
 * A hash of the first msglen bytes of message using a keyed hash defined
 * by authtype is created and stored in MAC.  MAC is ASSUMED to be a buffer
 * of at least maclen bytes.  If the length of the hash is greater than
 * maclen, it is truncated to fit the buffer.  If the length of the hash is
 * less than maclen, maclen set to the number of hash bytes generated.
 *
 * ASSUMED that the number of hash bits is a multiple of 8.
 */
int
sc_generate_keyed_hash(	oid	*authtype,	size_t authtypelen,
			u_char	*key,		u_int  keylen,
			u_char	*message,	u_int  msglen,
			u_char	*MAC,		size_t *maclen)
#if  defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL)
{
  int		 rval	 = SNMPERR_SUCCESS;
  int		 properlength;

  u_char	 buf[SNMP_MAXBUF_SMALL];
#if  defined(USE_OPENSSL)
  int		 buf_len = sizeof(buf);
  u_char	*bufp = buf;
#endif
  
  DEBUGTRACE;

#ifdef SNMP_TESTING_CODE
{
  int i;
  DEBUGMSG(("sc_generate_keyed_hash", "sc_generate_keyed_hash(): key=0x"));
  for(i=0; i< keylen; i++)
    DEBUGMSG(("sc_generate_keyed_hash", "%02x", key[i] & 0xff));
  DEBUGMSG(("sc_generate_keyed_hash"," (%d)\n", keylen));
}
#endif /* SNMP_TESTING_CODE */

/*
 * Sanity check.
 */
 if ( !authtype || !key || !message || !MAC || !maclen
      || (keylen<=0) || (msglen<=0) || (*maclen<=0)
      || (authtypelen != USM_LENGTH_OID_TRANSFORM) )
   {
     QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit);
   }
 
 properlength = sc_get_properlength(authtype, authtypelen);
 if (properlength == SNMPERR_GENERR)
   return properlength;
 
 if ( ((int)keylen < properlength) ) {
   QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit);
 }
 

#ifdef USE_OPENSSL
 /*
  * Determine transform type.
  */
   if (ISTRANSFORM(authtype, HMACMD5Auth))
     HMAC(EVP_md5(), key, keylen, message, msglen,  
	  buf, &buf_len);
   else if (ISTRANSFORM(authtype, HMACSHA1Auth)) 
     HMAC(EVP_sha1(), key, keylen, message, msglen,  
	  buf, &buf_len);
   else {
     QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit);
   }
   if (buf_len != properlength) {
     QUITFUN(rval, sc_generate_keyed_hash_quit);
   }
   if (*maclen > buf_len) 
     *maclen = buf_len;
   memcpy(MAC, buf, *maclen);
#else 
 if ((int)*maclen > properlength)
   *maclen = properlength;
 if (MDsign(message, msglen, MAC, *maclen, key, keylen)) {
   rval = SNMPERR_GENERR;
   goto sc_generate_keyed_hash_quit;
 }
#endif /* USE_OPENSSL */

#ifdef SNMP_TESTING_CODE
 {
   char    *s;
   int      len = binary_to_hex(MAC, *maclen, &s);
   
   DEBUGMSGTL(("scapi","Full v3 message hash: %s\n", s));
   SNMP_ZERO(s, len);
   SNMP_FREE(s);
 }
#endif
 
 sc_generate_keyed_hash_quit:
 SNMP_ZERO(buf, SNMP_MAXBUF_SMALL);
 return rval;
}  /* end sc_generate_keyed_hash() */
Exemplo n.º 7
0
/*******************************************************************-o-******
 * generate_Ku
 *
 * Parameters:
 *	*hashtype	MIB OID for the transform type for hashing.
 *	 hashtype_len	Length of OID value.
 *	*P		Pre-allocated bytes of passpharase.
 *	 pplen		Length of passphrase.
 *	*Ku		Buffer to contain Ku.
 *	*kulen		Length of Ku buffer.
 *
 * Returns:
 *	SNMPERR_SUCCESS			Success.
 *	SNMPERR_GENERR			All errors.
 *
 *
 * Convert a passphrase into a master user key, Ku, according to the
 * algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM)
 * as follows:
 *
 * Expand the passphrase to fill the passphrase buffer space, if necessary,
 * concatenation as many duplicates as possible of P to itself.  If P is
 * larger than the buffer space, truncate it to fit.
 *
 * Then hash the result with the given hashtype transform.  Return
 * the result as Ku.
 *
 * If successful, kulen contains the size of the hash written to Ku.
 *
 * NOTE  Passphrases less than USM_LENGTH_P_MIN characters in length
 *	 cause an error to be returned.
 *	 (Punt this check to the cmdline apps?  XXX)
 */
int
generate_Ku(	oid	*hashtype,	u_int  hashtype_len,
                u_char	*P,		size_t  pplen,
                u_char	*Ku,		size_t *kulen)
#if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL)
{
    int		 rval   = SNMPERR_SUCCESS,
             nbytes = USM_LENGTH_EXPANDED_PASSPHRASE;

    u_int            i, pindex = 0;

    u_char		 buf[USM_LENGTH_KU_HASHBLOCK],
                 *bufp;

#ifdef USE_OPENSSL
    EVP_MD_CTX      *ctx = malloc(sizeof(EVP_MD_CTX));
#else
    MDstruct         MD;
#endif
    /*
     * Sanity check.
     */
    if ( !hashtype || !P || !Ku || !kulen
            || (*kulen<=0)
            || (hashtype_len != USM_LENGTH_OID_TRANSFORM) )
    {
        QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
    }

    if (pplen < USM_LENGTH_P_MIN) {
#ifdef SNMP_TESTING_CODE
        snmp_log(LOG_WARNING, "Warning: passphrase chosen is below the length requiremnts of the USM.\n");
#else
        snmp_set_detail("Password length too short.");
        QUITFUN(SNMPERR_GENERR, generate_Ku_quit);
#endif
    }


    /*
     * Setup for the transform type.
     */
#ifdef USE_OPENSSL

    if (ISTRANSFORM(hashtype, HMACMD5Auth))
        EVP_DigestInit(ctx, EVP_md5());
    else if (ISTRANSFORM(hashtype, HMACSHA1Auth))
        EVP_DigestInit(ctx, EVP_sha1());
    else  {
        free(ctx);
        return (SNMPERR_GENERR);
    }
#else
    MDbegin(&MD);
#endif /* USE_OPENSSL */

    while (nbytes > 0) {
        bufp = buf;
        for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++) {
            *bufp++ = P[pindex++ % pplen];
        }
#ifdef USE_OPENSSL
        EVP_DigestUpdate(ctx, buf, USM_LENGTH_KU_HASHBLOCK);
#else
        if (MDupdate(&MD, buf, USM_LENGTH_KU_HASHBLOCK*8)) {
            rval = SNMPERR_USM_ENCRYPTIONERROR;
            goto md5_fin;
        }
#endif /* USE_OPENSSL */

        nbytes -= USM_LENGTH_KU_HASHBLOCK;
    }

#ifdef USE_OPENSSL
    EVP_DigestFinal(ctx, (unsigned char *) Ku, (unsigned int *) kulen);
    /* what about free() */
#else
    if (MDupdate(&MD, buf, 0)) {
        rval = SNMPERR_USM_ENCRYPTIONERROR;
        goto md5_fin;
    }
    *kulen = sc_get_properlength(hashtype, hashtype_len);
    MDget(&MD, Ku, *kulen);
md5_fin:
    memset(&MD, 0, sizeof(MD));
#endif /* USE_OPENSSL */


#ifdef SNMP_TESTING_CODE
    DEBUGMSGTL(("generate_Ku", "generating Ku (from %s): ", P));
    for(i=0; i < *kulen; i++)
        DEBUGMSG(("generate_Ku", "%02x",Ku[i]));
    DEBUGMSG(("generate_Ku","\n"));
#endif /* SNMP_TESTING_CODE */


generate_Ku_quit:
    memset(buf, 0, sizeof(buf));
#ifdef USE_OPENSSL
    free(ctx);
#endif
    return rval;

}  /* end generate_Ku() */
_SCAPI_NOT_CONFIGURED
#endif                          /* NETSNMP_USE_INTERNAL_MD5 */
/*******************************************************************-o-******
 * sc_encrypt
 *
 * Parameters:
 *	 privtype	Type of privacy cryptographic transform.
 *	*key		Key bits for crypting.
 *	 keylen		Length of key (buffer) in bytes.
 *	*iv		IV bits for crypting.
 *	 ivlen		Length of iv (buffer) in bytes.
 *	*plaintext	Plaintext to crypt.
 *	 ptlen		Length of plaintext.
 *	*ciphertext	Ciphertext to crypt.
 *	*ctlen		Length of ciphertext.
 *      
 * Returns:
 *	SNMPERR_SUCCESS			Success.
 *	SNMPERR_SC_NOT_CONFIGURED	Encryption is not supported.
 *	SNMPERR_SC_GENERAL_FAILURE	Any other error
 *
 *
 * Encrypt plaintext into ciphertext using key and iv.
 *
 * ctlen contains actual number of crypted bytes in ciphertext upon
 * successful return.
 */
int
sc_encrypt(const oid * privtype, size_t privtypelen,
           u_char * key, u_int keylen,
           u_char * iv, u_int ivlen,
           u_char * plaintext, u_int ptlen,
           u_char * ciphertext, size_t * ctlen)
#if defined(NETSNMP_USE_OPENSSL)
{
    int             rval = SNMPERR_SUCCESS;
    u_int           properlength = 0, properlength_iv = 0;
    u_char          pad_block[128];      /* bigger than anything I need */
    u_char          my_iv[128];  /* ditto */
    int             pad, plast, pad_size = 0;
    int             have_trans;
#ifndef NETSNMP_DISABLE_DES
#ifdef OLD_DES
    DES_key_schedule key_sch;
#else
    DES_key_schedule key_sched_store;
    DES_key_schedule *key_sch = &key_sched_store;
#endif
    DES_cblock       key_struct;
#endif
#ifdef HAVE_AES
    AES_KEY aes_key;
    int new_ivlen = 0;
#endif

    DEBUGTRACE;

    /*
     * Sanity check.
     */
#if	!defined(NETSNMP_ENABLE_SCAPI_AUTHPRIV)
    snmp_log(LOG_ERR, "Encryption support not enabled.\n");
    return SNMPERR_SC_NOT_CONFIGURED;
#endif

    if (!privtype || !key || !iv || !plaintext || !ciphertext || !ctlen
        || (keylen <= 0) || (ivlen <= 0) || (ptlen <= 0) || (*ctlen <= 0)
        || (privtypelen != USM_LENGTH_OID_TRANSFORM)) {
        QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
    } else if (ptlen > *ctlen) {
        QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
    }
#ifdef NETSNMP_ENABLE_TESTING_CODE
    {
        size_t          buf_len = 128, out_len = 0;
        u_char         *buf = (u_char *) malloc(buf_len);

        if (buf != NULL) {
            if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1,
                                         iv, ivlen)) {
                DEBUGMSGTL(("scapi", "encrypt: IV: %s/", buf));
            } else {
                DEBUGMSGTL(("scapi", "encrypt: IV: %s [TRUNCATED]/", buf));
            }
            out_len = 0;
            if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1,
                                         key, keylen)) {
                DEBUGMSG(("scapi", "%s\n", buf));
            } else {
                DEBUGMSG(("scapi", "%s [TRUNCATED]\n", buf));
            }
            out_len = 0;
            if (sprint_realloc_hexstring(&buf, &buf_len, &out_len, 1,
                                         plaintext, 16)) {
                DEBUGMSGTL(("scapi", "encrypt: string: %s\n", buf));
            } else {
                DEBUGMSGTL(("scapi", "encrypt: string: %s [TRUNCATED]\n",
                            buf));
            }
            free(buf);
        } else {
            DEBUGMSGTL(("scapi",
                        "encrypt: malloc fail for debug output\n"));
        }
    }
#endif                          /* NETSNMP_ENABLE_TESTING_CODE */


    /*
     * Determine privacy transform.
     */
    have_trans = 0;
#ifndef NETSNMP_DISABLE_DES
    if (ISTRANSFORM(privtype, DESPriv)) {
        properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES);
        properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV);
        pad_size = properlength;
        have_trans = 1;
    }
#endif
#ifdef HAVE_AES
    if (ISTRANSFORM(privtype, AESPriv)) {
        properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_AES);
        properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_AES_IV);
        have_trans = 1;
    }
#endif
    if (!have_trans) {
        QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
    }

    if ((keylen < properlength) || (ivlen < properlength_iv)) {
        QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
    }

    memset(my_iv, 0, sizeof(my_iv));

#ifndef NETSNMP_DISABLE_DES
    if (ISTRANSFORM(privtype, DESPriv)) {

        /*
         * now calculate the padding needed 
         */
        pad = pad_size - (ptlen % pad_size);
        plast = (int) ptlen - (pad_size - pad);
        if (pad == pad_size)
            pad = 0;
        if (ptlen + pad > *ctlen) {
            QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);    /* not enough space */
        }
        if (pad > 0) {              /* copy data into pad block if needed */
            memcpy(pad_block, plaintext + plast, pad_size - pad);
            memset(&pad_block[pad_size - pad], pad, pad);   /* filling in padblock */
        }

        memcpy(key_struct, key, sizeof(key_struct));
        (void) DES_key_sched(&key_struct, key_sch);

        memcpy(my_iv, iv, ivlen);
        /*
         * encrypt the data 
         */
        DES_ncbc_encrypt(plaintext, ciphertext, plast, key_sch,
                         (DES_cblock *) my_iv, DES_ENCRYPT);
        if (pad > 0) {
            /*
             * then encrypt the pad block 
             */
            DES_ncbc_encrypt(pad_block, ciphertext + plast, pad_size,
                             key_sch, (DES_cblock *) my_iv, DES_ENCRYPT);
            *ctlen = plast + pad_size;
        } else {
            *ctlen = plast;
        }
    }
#endif
#ifdef HAVE_AES
    if (ISTRANSFORM(privtype, AESPriv)) {
        (void) AES_set_encrypt_key(key, properlength*8, &aes_key);

        memcpy(my_iv, iv, ivlen);
        /*
         * encrypt the data 
         */
        AES_cfb128_encrypt(plaintext, ciphertext, ptlen,
                           &aes_key, my_iv, &new_ivlen, AES_ENCRYPT);
        *ctlen = ptlen;
    }
#endif
  sc_encrypt_quit:
    /*
     * clear memory just in case 
     */
    memset(my_iv, 0, sizeof(my_iv));
    memset(pad_block, 0, sizeof(pad_block));
#ifndef NETSNMP_DISABLE_DES
    memset(key_struct, 0, sizeof(key_struct));
#ifdef OLD_DES
    memset(&key_sch, 0, sizeof(key_sch));
#else
    memset(&key_sched_store, 0, sizeof(key_sched_store));
#endif
#endif
#ifdef HAVE_AES
    memset(&aes_key,0,sizeof(aes_key));
#endif
    return rval;

}                               /* end sc_encrypt() */
                _SCAPI_NOT_CONFIGURED
#endif                          /* */
/*
 * sc_hash(): a generic wrapper around whatever hashing package we are using.
 * 
 * IN:
 * hashtype    - oid pointer to a hash type
 * hashtypelen - length of oid pointer
 * buf         - u_char buffer to be hashed
 * buf_len     - integer length of buf data
 * MAC_len     - length of the passed MAC buffer size.
 * 
 * OUT:    
 * MAC         - pre-malloced space to store hash output.
 * MAC_len     - length of MAC output to the MAC buffer.
 * 
 * Returns:
 * SNMPERR_SUCCESS              Success.
 * SNMP_SC_GENERAL_FAILURE      Any error.
 */
int
sc_hash(const oid * hashtype, size_t hashtypelen, u_char * buf,
        size_t buf_len, u_char * MAC, size_t * MAC_len)
#if defined(NETSNMP_USE_INTERNAL_MD5) || defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_PKCS11)
{
#if defined(NETSNMP_USE_OPENSSL) || defined(NETSNMP_USE_PKCS11)
    int            rval = SNMPERR_SUCCESS;
#endif
    int            ret;

#ifdef NETSNMP_USE_OPENSSL
    const EVP_MD   *hashfn;
    EVP_MD_CTX     ctx, *cptr;
    unsigned int   tmp_len;
#endif

    DEBUGTRACE;

    if (hashtype == NULL || hashtypelen < 0 || buf == NULL ||
        buf_len <= 0 || MAC == NULL || MAC_len == NULL )
        return (SNMPERR_GENERR);
    ret = sc_get_properlength(hashtype, hashtypelen);
    if (( ret < 0 ) || (*MAC_len < ret ))
        return (SNMPERR_GENERR);

#ifdef NETSNMP_USE_OPENSSL
    /*
     * Determine transform type.
     */
#ifndef NETSNMP_DISABLE_MD5
    if (ISTRANSFORM(hashtype, HMACMD5Auth)) {
        hashfn = (const EVP_MD *) EVP_md5();
    } else
#endif
        if (ISTRANSFORM(hashtype, HMACSHA1Auth)) {
        hashfn = (const EVP_MD *) EVP_sha1();
    } else {
        return (SNMPERR_GENERR);
    }

/** initialize the pointer */
    memset(&ctx, 0, sizeof(ctx));
    cptr = &ctx;
#if defined(OLD_DES)
    EVP_DigestInit(cptr, hashfn);
#else /* !OLD_DES */
    /* this is needed if the runtime library is different than the compiled
       library since the openssl versions are very different. */
    if (SSLeay() < 0x907000) {
        /* the old version of the struct was bigger and thus more
           memory is needed. should be 152, but we use 256 for safety. */
        cptr = (EVP_MD_CTX *)malloc(256);
        EVP_DigestInit(cptr, hashfn);
    } else {
        EVP_MD_CTX_init(cptr);
        EVP_DigestInit(cptr, hashfn);
    }
#endif

/** pass the data */
    EVP_DigestUpdate(cptr, buf, buf_len);

/** do the final pass */
#if defined(OLD_DES)
    EVP_DigestFinal(cptr, MAC, &tmp_len);
    *MAC_len = tmp_len;
#else /* !OLD_DES */
    if (SSLeay() < 0x907000) {
        EVP_DigestFinal(cptr, MAC, &tmp_len);
        *MAC_len = tmp_len;
        free(cptr);
    } else {
        EVP_DigestFinal_ex(cptr, MAC, &tmp_len);
        *MAC_len = tmp_len;
        EVP_MD_CTX_cleanup(cptr);
    }
#endif                          /* OLD_DES */
    return (rval);
#elif NETSNMP_USE_PKCS11                  /* NETSNMP_USE_PKCS11 */

#ifndef NETSNMP_DISABLE_MD5
    if (ISTRANSFORM(hashtype, HMACMD5Auth)) {
	rval = pkcs_digest(CKM_MD5, buf, buf_len, MAC, &tmp_len);
        *MAC_len = tmp_len;
    } else
#endif
        if (ISTRANSFORM(hashtype, HMACSHA1Auth)) {
	rval = pkcs_digest(CKM_SHA_1, buf, buf_len, MAC, &tmp_len);
        *MAC_len = tmp_len;
    } else {
        return (SNMPERR_GENERR);
    }

     return (rval);

#else                           /* NETSNMP_USE_INTERNAL_MD5 */

    if (MDchecksum(buf, buf_len, MAC, *MAC_len)) {
        return SNMPERR_GENERR;
    }
    if (*MAC_len > 16)
        *MAC_len = 16;
    return SNMPERR_SUCCESS;

#endif                          /* NETSNMP_USE_OPENSSL */
}
Exemplo n.º 10
0
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 = 0;
    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];

    authKeyChange = authKeyOid;
    privKeyChange = privKeyOid;

    /*
     * get the common command line arguments 
     */
    switch (arg = snmp_parse_args(argc, argv, &session, "C:", optProc)) {
    case -2:
        exit(0);
    case -1:
        usage();
        exit(1);
    default:
        break;
    }

    if (arg >= argc) {
        fprintf(stderr, "Please specify an operation to perform.\n");
        usage();
        exit(1);
    }

    SOCK_STARTUP;

    /*
     * 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);
        exit(1);
    }

    /*
     * 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");
        exit(1);
    }


    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);
            exit(1);
        }

        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);
            exit(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.
             */
            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");
	      exit(1);
	    }
	    
	    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");
	        exit(1);
	    }

	    /*
	     * 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");
		exit(1);
	    }
	}
	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");
	      exit(1);
	    }
	    
	    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");
                exit(1);
            }

	    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");
		exit(1);
	    }
	}

        /*
         * 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) {
            if (!session.securityPrivProto) {
                snmp_log(LOG_ERR, "no encryption type specified, which I need in order to know to change the key\n");
                exit(1);
            }
                
#ifndef NETSNMP_DISABLE_DES
            if (ISTRANSFORM(session.securityPrivProto, DESPriv)) {
                /* DES uses a 128 bit key, 64 bits of which is a salt */
                oldkulpriv_len = newkulpriv_len = 16;
            }
#endif
#ifdef HAVE_AES
            if (ISTRANSFORM(session.securityPrivProto, AESPriv)) {
                oldkulpriv_len = newkulpriv_len = 16;
            }
#endif
            memcpy(oldkulpriv, oldkul, oldkulpriv_len);
            memcpy(newkulpriv, newkul, newkulpriv_len);
        }
            

        /*
         * 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();
            exit(1);
	  }
	}

        /* which is slightly different for encryption if lengths are
           different */
	if (doprivkey) {
	  rval = encode_keychange(session.securityAuthProto,
                                session.securityAuthProtoLen,
                                oldkulpriv, oldkulpriv_len,
                                newkulpriv, newkulpriv_len,
                                keychangepriv, &keychangepriv_len);

	  if (rval != SNMPERR_SUCCESS) {
            snmp_perror(argv[0]);
            fprintf(stderr, "encoding the keychange failed\n");
            usage();
            exit(1);
	  }
	}

        /*
         * 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();
            exit(1);
        }

        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]);
            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();
            exit(1);
        }

        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();
            exit(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 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");
            exit(1);
        }

        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");
            exit(1);
        }

        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");
            exit(1);
        }

        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");
            exit(1);
        }

        /* 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;
#ifndef NETSNMP_DISABLE_DES
	    if (ISTRANSFORM(ss->securityPrivProto, DESPriv)) {
                /* DES uses a 128 bit key, 64 bits of which is a salt */
	        dhprivKeyLen = 16;
	    }
#endif
#ifdef HAVE_AES
	    if (ISTRANSFORM(ss->securityPrivProto, AESPriv)) {
	        dhprivKeyLen = 16;
	    }
#endif
            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 */
    } else {
        fprintf(stderr, "Unknown command\n");
        usage();
        exit(1);
    }

    /*
     * 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;
    }

  begone:
    if (response)
        snmp_free_pdu(response);
    snmp_close(ss);
    SOCK_CLEANUP;
    return exitval;
}