1 //==========================================================================
3 // ./lib/current/src/scapi.c
6 //==========================================================================
7 //####ECOSGPLCOPYRIGHTBEGIN####
8 // -------------------------------------------
9 // This file is part of eCos, the Embedded Configurable Operating System.
10 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // eCos is free software; you can redistribute it and/or modify it under
13 // the terms of the GNU General Public License as published by the Free
14 // Software Foundation; either version 2 or (at your option) any later version.
16 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
17 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 // You should have received a copy of the GNU General Public License along
22 // with eCos; if not, write to the Free Software Foundation, Inc.,
23 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 // As a special exception, if other files instantiate templates or use macros
26 // or inline functions from this file, or you compile this file and link it
27 // with other works to produce a work based on this file, this file does not
28 // by itself cause the resulting work to be covered by the GNU General Public
29 // License. However the source code for this file must still be made available
30 // in accordance with section (3) of the GNU General Public License.
32 // This exception does not invalidate any other reasons why a work based on
33 // this file might be covered by the GNU General Public License.
35 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
36 // at http://sources.redhat.com/ecos/ecos-license/
37 // -------------------------------------------
38 //####ECOSGPLCOPYRIGHTEND####
39 //####UCDSNMPCOPYRIGHTBEGIN####
41 // -------------------------------------------
43 // Portions of this software may have been derived from the UCD-SNMP
44 // project, <http://ucd-snmp.ucdavis.edu/> from the University of
45 // California at Davis, which was originally based on the Carnegie Mellon
46 // University SNMP implementation. Portions of this software are therefore
47 // covered by the appropriate copyright disclaimers included herein.
49 // The release used was version 4.1.2 of May 2000. "ucd-snmp-4.1.2"
50 // -------------------------------------------
52 //####UCDSNMPCOPYRIGHTEND####
53 //==========================================================================
54 //#####DESCRIPTIONBEGIN####
59 // Purpose: Port of UCD-SNMP distribution to eCos.
63 //####DESCRIPTIONEND####
65 //==========================================================================
66 /********************************************************************
67 Copyright 1989, 1991, 1992 by Carnegie Mellon University
70 Copyright 1996, 1998, 1999, 2000 The Regents of the University of California
74 Permission to use, copy, modify and distribute this software and its
75 documentation for any purpose and without fee is hereby granted,
76 provided that the above copyright notice appears in all copies and
77 that both that copyright notice and this permission notice appear in
78 supporting documentation, and that the name of CMU and The Regents of
79 the University of California not be used in advertising or publicity
80 pertaining to distribution of the software without specific written
83 CMU AND THE REGENTS OF THE UNIVERSITY OF CALIFORNIA DISCLAIM ALL
84 WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
85 WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL CMU OR
86 THE REGENTS OF THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY SPECIAL,
87 INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
88 FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
89 CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
90 CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
91 *********************************************************************/
99 #ifdef CYGPKG_SNMPAGENT_V3_SUPPORT
100 #include <sys/types.h>
112 #if TIME_WITH_SYS_TIME
114 # include <sys/timeb.h>
116 # include <sys/time.h>
121 # include <sys/time.h>
126 #ifdef HAVE_NETINET_IN_H
127 #include <netinet/in.h>
130 #ifdef USE_INTERNAL_MD5
135 #include "snmp_api.h"
136 #include "callback.h"
138 #include "keytools.h"
139 #include "snmp_debug.h"
141 #include "snmp_impl.h"
144 #include "transform_oids.h"
147 #include <openssl/hmac.h>
148 #include <openssl/evp.h>
149 #include <openssl/rand.h>
154 #define QUITFUN(e, l) \
155 if (e != SNMPERR_SUCCESS) { \
156 rval = SNMPERR_SC_GENERAL_FAILURE; \
163 sc_get_properlength(oid *hashtype, u_int hashtype_len):
165 Given a hashing type ("hashtype" and its length hashtype_len), return
166 the length of the hash result.
168 Returns either the length or SNMPERR_GENERR for an unknown hashing type.
171 sc_get_properlength(oid *hashtype, u_int hashtype_len)
175 * Determine transform type hash length.
177 if ( ISTRANSFORM(hashtype, HMACMD5Auth)) {
178 return BYTESIZE(SNMP_TRANS_AUTHLEN_HMACMD5);
180 else if ( ISTRANSFORM(hashtype, HMACSHA1Auth) ) {
181 return BYTESIZE(SNMP_TRANS_AUTHLEN_HMACSHA1);
183 return SNMPERR_GENERR;
187 /*******************************************************************-o-******
191 * SNMPERR_SUCCESS Success.
196 int rval = SNMPERR_SUCCESS;
199 #ifdef USE_INTERNAL_MD5
204 gettimeofday(&tv,(struct timezone *)0);
206 srandom(tv.tv_sec ^ tv.tv_usec);
208 rval = SNMPERR_SC_NOT_CONFIGURED;
210 /* XXX ogud: The only reason to do anything here with openssl is to
211 * XXX ogud: seed random number generator
213 #endif /* ifndef USE_OPENSSL */
215 } /* end sc_init() */
217 /*******************************************************************-o-******
221 * *buf Pre-allocated buffer.
222 * *buflen Size of buffer.
225 * SNMPERR_SUCCESS Success.
228 sc_random(u_char *buf, size_t *buflen)
229 #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL)
231 int rval = SNMPERR_SUCCESS;
232 #ifdef USE_INTERNAL_MD5
241 RAND_bytes(buf, *buflen); /* will never fail */
242 #else /* USE_INTERNAL_MD5 */
243 /* fill the buffer with random integers. Note that random()
244 is defined in config.h and may not be truly the random()
245 system call if something better existed */
246 rval = *buflen - *buflen%sizeof(rndval);
247 for(i = 0; i < rval; i += sizeof(rndval)) {
249 memcpy(ucp, &rndval, sizeof(rndval));
250 ucp += sizeof(rndval);
254 memcpy(ucp, &rndval, *buflen%sizeof(rndval));
256 rval = SNMPERR_SUCCESS;
257 #endif /* USE_OPENSSL */
260 } /* end sc_random() */
263 _SCAPI_NOT_CONFIGURED
268 /*******************************************************************-o-******
269 * sc_generate_keyed_hash
272 * authtype Type of authentication transform.
274 * *key Pointer to key (Kul) to use in keyed hash.
275 * keylen Length of key in bytes.
276 * *message Pointer to the message to hash.
277 * msglen Length of the message.
278 * *MAC Will be returned with allocated bytes containg hash.
279 * *maclen Length of the hash buffer in bytes; also indicates
280 * whether the MAC should be truncated.
283 * SNMPERR_SUCCESS Success.
284 * SNMPERR_GENERR All errs
287 * A hash of the first msglen bytes of message using a keyed hash defined
288 * by authtype is created and stored in MAC. MAC is ASSUMED to be a buffer
289 * of at least maclen bytes. If the length of the hash is greater than
290 * maclen, it is truncated to fit the buffer. If the length of the hash is
291 * less than maclen, maclen set to the number of hash bytes generated.
293 * ASSUMED that the number of hash bits is a multiple of 8.
296 sc_generate_keyed_hash( oid *authtype, size_t authtypelen,
297 u_char *key, u_int keylen,
298 u_char *message, u_int msglen,
299 u_char *MAC, size_t *maclen)
300 #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL)
302 int rval = SNMPERR_SUCCESS;
305 u_char buf[SNMP_MAXBUF_SMALL];
306 #if defined(USE_OPENSSL)
307 int buf_len = sizeof(buf);
313 #ifdef SNMP_TESTING_CODE
316 DEBUGMSG(("sc_generate_keyed_hash", "sc_generate_keyed_hash(): key=0x"));
317 for(i=0; i< keylen; i++)
318 DEBUGMSG(("sc_generate_keyed_hash", "%02x", key[i] & 0xff));
319 DEBUGMSG(("sc_generate_keyed_hash"," (%d)\n", keylen));
321 #endif /* SNMP_TESTING_CODE */
326 if ( !authtype || !key || !message || !MAC || !maclen
327 || (keylen<=0) || (msglen<=0) || (*maclen<=0)
328 || (authtypelen != USM_LENGTH_OID_TRANSFORM) )
330 QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit);
333 properlength = sc_get_properlength(authtype, authtypelen);
334 if (properlength == SNMPERR_GENERR)
337 if ( ((int)keylen < properlength) ) {
338 QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit);
344 * Determine transform type.
346 if (ISTRANSFORM(authtype, HMACMD5Auth))
347 HMAC(EVP_md5(), key, keylen, message, msglen,
349 else if (ISTRANSFORM(authtype, HMACSHA1Auth))
350 HMAC(EVP_sha1(), key, keylen, message, msglen,
353 QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit);
355 if (buf_len != properlength) {
356 QUITFUN(rval, sc_generate_keyed_hash_quit);
358 if (*maclen > buf_len)
360 memcpy(MAC, buf, *maclen);
362 if ((int)*maclen > properlength)
363 *maclen = properlength;
364 if (MDsign(message, msglen, MAC, *maclen, key, keylen)) {
365 rval = SNMPERR_GENERR;
366 goto sc_generate_keyed_hash_quit;
368 #endif /* USE_OPENSSL */
370 #ifdef SNMP_TESTING_CODE
373 int len = binary_to_hex(MAC, *maclen, &s);
375 DEBUGMSGTL(("scapi","Full v3 message hash: %s\n", s));
381 sc_generate_keyed_hash_quit:
382 SNMP_ZERO(buf, SNMP_MAXBUF_SMALL);
384 } /* end sc_generate_keyed_hash() */
387 _SCAPI_NOT_CONFIGURED
391 /* sc_hash(): a generic wrapper around whatever hashing package we are using.
394 hashtype - oid pointer to a hash type
395 hashtypelen - length of oid pointer
396 buf - u_char buffer to be hashed
397 buf_len - integer length of buf data
398 MAC_len - length of the passed MAC buffer size.
401 MAC - pre-malloced space to store hash output.
402 MAC_len - length of MAC output to the MAC buffer.
405 SNMPERR_SUCCESS Success.
406 SNMP_SC_GENERAL_FAILURE Any error.
410 sc_hash(oid *hashtype, size_t hashtypelen, u_char *buf, size_t buf_len,
411 u_char *MAC, size_t *MAC_len)
412 #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL)
414 int rval = SNMPERR_SUCCESS;
423 if (hashtype == NULL || hashtypelen < 0 || buf == NULL ||
424 buf_len < 0 || MAC == NULL || MAC_len == NULL ||
425 (int)(*MAC_len) < sc_get_properlength(hashtype, hashtypelen))
426 return (SNMPERR_GENERR);
430 * Determine transform type.
432 c = malloc(sizeof(HMAC_CTX));
434 return (SNMPERR_GENERR);
436 if (ISTRANSFORM(hashtype, HMACMD5Auth)) {
437 EVP_DigestInit(&c->md_ctx, (const EVP_MD *) EVP_md5());
439 else if (ISTRANSFORM(hashtype, HMACSHA1Auth)) {
440 EVP_DigestInit(&c->md_ctx, (const EVP_MD *) EVP_sha1());
443 return(SNMPERR_GENERR);
445 EVP_DigestUpdate(&c->md_ctx, buf, buf_len);
446 EVP_DigestFinal(&(c->md_ctx), MAC, MAC_len);
449 #else /* USE_INTERNAL_MD5 */
451 if (MDchecksum(buf, buf_len, MAC, *MAC_len)) {
452 return SNMPERR_GENERR;
458 #endif /* USE_OPENSSL */
460 #else /* !defined(USE_OPENSSL) && !defined(USE_INTERNAL_MD5) */
461 _SCAPI_NOT_CONFIGURED
462 #endif /* !defined(USE_OPENSSL) && !defined(USE_INTERNAL_MD5) */
466 /*******************************************************************-o-******
467 * sc_check_keyed_hash
470 * authtype Transform type of authentication hash.
471 * *key Key bits in a string of bytes.
472 * keylen Length of key in bytes.
473 * *message Message for which to check the hash.
474 * msglen Length of message.
476 * maclen Length of given hash; indicates truncation if it is
477 * shorter than the normal size of output for
478 * given hash transform.
480 * SNMPERR_SUCCESS Success.
481 * SNMP_SC_GENERAL_FAILURE Any error
484 * Check the hash given in MAC against the hash of message. If the length
485 * of MAC is less than the length of the transform hash output, only maclen
486 * bytes are compared. The length of MAC cannot be greater than the
487 * length of the hash transform output.
490 sc_check_keyed_hash( oid *authtype, size_t authtypelen,
491 u_char *key, u_int keylen,
492 u_char *message, u_int msglen,
493 u_char *MAC, u_int maclen)
494 #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL)
496 int rval = SNMPERR_SUCCESS;
497 size_t buf_len = SNMP_MAXBUF_SMALL;
499 u_char buf[SNMP_MAXBUF_SMALL];
503 #ifdef SNMP_TESTING_CODE
506 DEBUGMSG(("scapi", "sc_check_keyed_hash(): key=0x"));
507 for(i=0; i< keylen; i++)
508 DEBUGMSG(("scapi", "%02x", key[i] & 0xff));
509 DEBUGMSG(("scapi"," (%d)\n", keylen));
511 #endif /* SNMP_TESTING_CODE */
516 if ( !authtype || !key || !message || !MAC
517 || (keylen<=0) || (msglen<=0) || (maclen<=0)
518 || (authtypelen != USM_LENGTH_OID_TRANSFORM) )
520 QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit);
524 if (maclen != USM_MD5_AND_SHA_AUTH_LEN) {
525 QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit);
529 * Generate a full hash of the message, then compare
530 * the result with the given MAC which may shorter than
531 * the full hash length.
533 rval = sc_generate_keyed_hash( authtype, authtypelen,
537 QUITFUN(rval, sc_check_keyed_hash_quit);
539 if (maclen > msglen) {
540 QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit);
542 } else if ( memcmp(buf, MAC, maclen) != 0 ) {
543 QUITFUN(SNMPERR_GENERR, sc_check_keyed_hash_quit);
547 sc_check_keyed_hash_quit:
548 SNMP_ZERO(buf, SNMP_MAXBUF_SMALL);
552 } /* end sc_check_keyed_hash() */
555 _SCAPI_NOT_CONFIGURED
556 #endif /* USE_INTERNAL_MD5 */
560 /*******************************************************************-o-******
564 * privtype Type of privacy cryptographic transform.
565 * *key Key bits for crypting.
566 * keylen Length of key (buffer) in bytes.
567 * *iv IV bits for crypting.
568 * ivlen Length of iv (buffer) in bytes.
569 * *plaintext Plaintext to crypt.
570 * ptlen Length of plaintext.
571 * *ciphertext Ciphertext to crypt.
572 * *ctlen Length of ciphertext.
575 * SNMPERR_SUCCESS Success.
576 * SNMPERR_SC_NOT_CONFIGURED Encryption is not supported.
577 * SNMPERR_SC_GENERAL_FAILURE Any other error
580 * Encrypt plaintext into ciphertext using key and iv.
582 * ctlen contains actual number of crypted bytes in ciphertext upon
586 sc_encrypt( oid *privtype, size_t privtypelen,
587 u_char *key, u_int keylen,
588 u_char *iv, u_int ivlen,
589 u_char *plaintext, u_int ptlen,
590 u_char *ciphertext, size_t *ctlen)
591 #if defined(USE_OPENSSL)
593 int rval = SNMPERR_SUCCESS;
597 u_char pad_block[32]; /* bigger than anything I need */
598 u_char my_iv[32]; /* ditto */
599 int pad, plast, pad_size;
600 des_key_schedule key_sch;
601 des_cblock key_struct;
608 #if !defined(SCAPI_AUTHPRIV)
609 return SNMPERR_SC_NOT_CONFIGURED;
612 if ( !privtype || !key || !iv || !plaintext || !ciphertext || !ctlen
613 || (keylen<=0) || (ivlen<=0) || (ptlen<=0) || (*ctlen<=0)
614 || (privtypelen != USM_LENGTH_OID_TRANSFORM) )
616 QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
618 else if ( ptlen >= *ctlen) {
619 QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
623 #ifdef SNMP_TESTING_CODE
625 char buf[SNMP_MAXBUF];
627 sprint_hexstring(buf, iv, ivlen);
628 DEBUGMSGTL(("scapi", "encrypt: IV: %s/ ", buf));
629 sprint_hexstring(buf, key, keylen);
630 DEBUGMSG(("scapi","%s\n", buf));
632 sprint_hexstring(buf, plaintext, 16);
633 DEBUGMSGTL(("scapi","encrypt: string: %s\n", buf));
635 #endif /* SNMP_TESTING_CODE */
639 * Determine privacy transform.
641 if ( ISTRANSFORM(privtype, DESPriv) ) {
642 properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES);
643 properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV);
644 pad_size = properlength;
646 QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
649 if ( (keylen<properlength) || (ivlen<properlength_iv) ) {
650 QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
653 else if ( (keylen<properlength) || (ivlen<properlength_iv) ) {
654 QUITFUN(SNMPERR_GENERR, sc_encrypt_quit);
657 /* now calculate the padding needed */
658 pad = pad_size - (ptlen % pad_size);
659 if (ptlen + pad > *ctlen) {
660 QUITFUN(SNMPERR_GENERR, sc_encrypt_quit); /* not enough space */
662 memset(pad_block, 0, sizeof(pad_block));
663 plast = (int) ptlen - (pad_size - pad);
664 if (pad > 0) /* copy data into pad block if needed */
665 memcpy( pad_block, plaintext + plast, pad_size - pad);
666 memset(&pad_block[pad_size-pad], pad, pad); /* filling in padblock */
668 memset(my_iv, 0, sizeof(my_iv));
670 if ( ISTRANSFORM(privtype, DESPriv) ) {
671 memcpy(key_struct, key, sizeof(key_struct));
672 (void) des_key_sched(&key_struct, key_sch);
674 memcpy(my_iv, iv, ivlen);
675 /* encrypt the data */
676 des_ncbc_encrypt(plaintext, ciphertext, plast, key_sch,
677 (des_cblock *) &my_iv, DES_ENCRYPT);
678 /* then encrypt the pad block */
679 des_ncbc_encrypt(pad_block, ciphertext+plast, pad_size,
680 key_sch, (des_cblock *)&my_iv, DES_ENCRYPT);
681 *ctlen = plast + pad_size;
684 /* clear memory just in case */
685 memset(my_iv, 0, sizeof(my_iv));
686 memset(pad_block, 0, sizeof(pad_block));
687 memset(key_struct, 0, sizeof(key_struct));
688 memset(key_sch, 0, sizeof(key_sch));
691 } /* end sc_encrypt() */
695 # if USE_INTERNAL_MD5
697 DEBUGMSGTL(("scapi","Encrypt function not defined.\n"));
698 return SNMPERR_SC_GENERAL_FAILURE;
702 _SCAPI_NOT_CONFIGURED
704 # endif /* USE_INTERNAL_MD5 */
710 /*******************************************************************-o-******
725 * SNMPERR_SUCCESS Success.
726 * SNMPERR_SC_NOT_CONFIGURED Encryption is not supported.
727 * SNMPERR_SC_GENERAL_FAILURE Any other error
730 * Decrypt ciphertext into plaintext using key and iv.
732 * ptlen contains actual number of plaintext bytes in plaintext upon
736 sc_decrypt( oid *privtype, size_t privtypelen,
737 u_char *key, u_int keylen,
738 u_char *iv, u_int ivlen,
739 u_char *ciphertext, u_int ctlen,
740 u_char *plaintext, size_t *ptlen)
744 int rval = SNMPERR_SUCCESS;
747 des_key_schedule key_sch;
748 des_cblock key_struct;
754 if ( !privtype || !key || !iv || !plaintext || !ciphertext || !ptlen
755 || (ctlen<=0) || (*ptlen<=0) || (*ptlen < ctlen)
756 || (privtypelen != USM_LENGTH_OID_TRANSFORM) )
758 QUITFUN(SNMPERR_GENERR, sc_decrypt_quit);
762 #ifdef SNMP_TESTING_CODE
764 char buf[SNMP_MAXBUF];
766 sprint_hexstring(buf, iv, ivlen);
767 DEBUGMSGTL(("scapi", "decrypt: IV: %s/ ", buf));
768 sprint_hexstring(buf, key, keylen);
769 DEBUGMSG(("scapi","%s\n", buf));
771 #endif /* SNMP_TESTING_CODE */
774 * Determine privacy transform.
776 if ( ISTRANSFORM(privtype, DESPriv) ) {
777 properlength = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES);
778 properlength_iv = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES_IV);
781 QUITFUN(SNMPERR_GENERR, sc_decrypt_quit);
784 if ( (keylen<properlength) || (ivlen<properlength_iv) ) {
785 QUITFUN(SNMPERR_GENERR, sc_decrypt_quit);
788 memset(my_iv, 0, sizeof(my_iv));
789 if (ISTRANSFORM(privtype, DESPriv)) {
790 memcpy(key_struct, key, sizeof(key_struct));
791 (void) des_key_sched(&key_struct, key_sch);
793 memcpy(my_iv, iv, ivlen);
794 des_cbc_encrypt(ciphertext, plaintext, ctlen, key_sch,
795 (des_cblock *) &my_iv, DES_DECRYPT);
800 memset(key_sch, 0, sizeof(key_sch));
801 memset(key_struct, 0, sizeof(key_struct));
802 memset(my_iv, 0, sizeof(my_iv));
805 #else /* USE OPEN_SSL */
807 #if !defined(SCAPI_AUTHPRIV)
808 return SNMPERR_SC_NOT_CONFIGURED;
810 # if USE_INTERNAL_MD5
812 DEBUGMSGTL(("scapi","Decryption function not defined.\n"));
813 return SNMPERR_SC_GENERAL_FAILURE;
817 _SCAPI_NOT_CONFIGURED
819 # endif /* USE_INTERNAL_MD5 */
822 #endif /* USE_OPENSSL */
824 #endif /* CYGPKG_SNMPAGENT_V3_SUPPORT */