Home | History | Annotate | Line # | Download | only in sntp
crypto.c revision 1.1.1.15
      1 /*	$NetBSD: crypto.c,v 1.1.1.15 2022/10/09 21:00:13 christos Exp $	*/
      2 
      3 /*
      4  * HMS: we need to test:
      5  * - OpenSSL versions, if we are building with them
      6  * - our versions
      7  *
      8  * We may need to test with(out) OPENSSL separately.
      9  */
     10 
     11 #include <config.h>
     12 #include "crypto.h"
     13 #include <ctype.h>
     14 #include "isc/string.h"
     15 #include "ntp_md5.h"
     16 
     17 #ifndef EVP_MAX_MD_SIZE
     18 # define EVP_MAX_MD_SIZE 32
     19 #endif
     20 
     21 struct key *key_ptr;
     22 size_t key_cnt = 0;
     23 
     24 typedef struct key Key_T;
     25 
     26 static u_int
     27 compute_mac(
     28 	u_char		digest[EVP_MAX_MD_SIZE],
     29 	char const *	macname,
     30 	void const *	pkt_data,
     31 	u_int		pkt_size,
     32 	void const *	key_data,
     33 	u_int		key_size
     34 	)
     35 {
     36 	u_int		len  = 0;
     37 #if defined(OPENSSL) && defined(ENABLE_CMAC)
     38 	size_t		slen = 0;
     39 #endif
     40 	int		key_type;
     41 
     42 	INIT_SSL();
     43 	key_type = keytype_from_text(macname, NULL);
     44 
     45 #if defined(OPENSSL) && defined(ENABLE_CMAC)
     46 	/* Check if CMAC key type specific code required */
     47 	if (key_type == NID_cmac) {
     48 		CMAC_CTX *	ctx    = NULL;
     49 		u_char		keybuf[AES_128_KEY_SIZE];
     50 
     51 		/* adjust key size (zero padded buffer) if necessary */
     52 		if (AES_128_KEY_SIZE > key_size) {
     53 			memcpy(keybuf, key_data, key_size);
     54 			memset((keybuf + key_size), 0,
     55 			       (AES_128_KEY_SIZE - key_size));
     56 			key_data = keybuf;
     57 		}
     58 
     59 		if (!(ctx = CMAC_CTX_new())) {
     60 			msyslog(LOG_ERR, "make_mac: CMAC %s CTX new failed.",   CMAC);
     61 		}
     62 		else if (!CMAC_Init(ctx, key_data, AES_128_KEY_SIZE,
     63 				    EVP_aes_128_cbc(), NULL)) {
     64 			msyslog(LOG_ERR, "make_mac: CMAC %s Init failed.",      CMAC);
     65 		}
     66 		else if (!CMAC_Update(ctx, pkt_data, (size_t)pkt_size)) {
     67 			msyslog(LOG_ERR, "make_mac: CMAC %s Update failed.",    CMAC);
     68 		}
     69 		else if (!CMAC_Final(ctx, digest, &slen)) {
     70 			msyslog(LOG_ERR, "make_mac: CMAC %s Final failed.",     CMAC);
     71 			slen = 0;
     72 		}
     73 		len = (u_int)slen;
     74 
     75 		if (ctx)
     76 			CMAC_CTX_free(ctx);
     77 		/* Test our AES-128-CMAC implementation */
     78 
     79 	} else	/* MD5 MAC handling */
     80 #endif
     81 	{
     82 		EVP_MD_CTX *	ctx;
     83 
     84 		if (!(ctx = EVP_MD_CTX_new())) {
     85 			msyslog(LOG_ERR, "make_mac: MAC %s Digest CTX new failed.",
     86 				macname);
     87 			goto mac_fail;
     88 		}
     89 #ifdef OPENSSL	/* OpenSSL 1 supports return codes 0 fail, 1 okay */
     90 #	    ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
     91 		EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
     92 #	    endif
     93 		/* [Bug 3457] DON'T use plain EVP_DigestInit! It would
     94 		 *  kill the flags! */
     95 		if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(key_type), NULL)) {
     96 			msyslog(LOG_ERR, "make_mac: MAC %s Digest Init failed.",
     97 				macname);
     98 			goto mac_fail;
     99 		}
    100 		if (!EVP_DigestUpdate(ctx, key_data, key_size)) {
    101 			msyslog(LOG_ERR, "make_mac: MAC %s Digest Update key failed.",
    102 				macname);
    103 			goto mac_fail;
    104 		}
    105 		if (!EVP_DigestUpdate(ctx, pkt_data, pkt_size)) {
    106 			msyslog(LOG_ERR, "make_mac: MAC %s Digest Update data failed.",
    107 				macname);
    108 			goto mac_fail;
    109 		}
    110 		if (!EVP_DigestFinal(ctx, digest, &len)) {
    111 			msyslog(LOG_ERR, "make_mac: MAC %s Digest Final failed.",
    112 				macname);
    113 			len = 0;
    114 		}
    115 #else /* !OPENSSL */
    116 		EVP_DigestInit(ctx, EVP_get_digestbynid(key_type));
    117 		EVP_DigestUpdate(ctx, key_data, key_size);
    118 		EVP_DigestUpdate(ctx, pkt_data, pkt_size);
    119 		EVP_DigestFinal(ctx, digest, &len);
    120 #endif
    121 	  mac_fail:
    122 		EVP_MD_CTX_free(ctx);
    123 	}
    124 
    125 	return len;
    126 }
    127 
    128 int
    129 make_mac(
    130 	const void *	pkt_data,
    131 	int		pkt_size,
    132 	int		mac_size,
    133 	Key_T const *	cmp_key,
    134 	void * 		digest
    135 	)
    136 {
    137 	u_int		len;
    138 	u_char		dbuf[EVP_MAX_MD_SIZE];
    139 
    140 	if (cmp_key->key_len > 64 || mac_size <= 0)
    141 		return 0;
    142 	if (pkt_size % 4 != 0)
    143 		return 0;
    144 
    145 	len = compute_mac(dbuf, cmp_key->typen,
    146 			  pkt_data, (u_int)pkt_size,
    147 			  cmp_key->key_seq, (u_int)cmp_key->key_len);
    148 
    149 
    150 	if (len) {
    151 		if (len > (u_int)mac_size)
    152 			len = (u_int)mac_size;
    153 		memcpy(digest, dbuf, len);
    154 	}
    155 	return (int)len;
    156 }
    157 
    158 
    159 /* Generates a md5 digest of the key specified in keyid concatenated with the
    160  * ntp packet (exluding the MAC) and compares this digest to the digest in
    161  * the packet's MAC. If they're equal this function returns 1 (packet is
    162  * authentic) or else 0 (not authentic).
    163  */
    164 int
    165 auth_md5(
    166 	void const *	pkt_data,
    167 	int 		pkt_size,
    168 	int		mac_size,
    169 	Key_T const *	cmp_key
    170 	)
    171 {
    172 	u_int		len       = 0;
    173 	u_char const *	pkt_ptr   = pkt_data;
    174 	u_char		dbuf[EVP_MAX_MD_SIZE];
    175 
    176 	if (mac_size <= 0 || (size_t)mac_size > sizeof(dbuf))
    177 		return FALSE;
    178 
    179 	len = compute_mac(dbuf, cmp_key->typen,
    180 			  pkt_ptr, (u_int)pkt_size,
    181 			  cmp_key->key_seq, (u_int)cmp_key->key_len);
    182 
    183 	pkt_ptr += pkt_size + 4;
    184 	if (len > (u_int)mac_size)
    185 		len = (u_int)mac_size;
    186 
    187 	/* isc_tsmemcmp will be better when its easy to link with.  sntp
    188 	 * is a 1-shot program, so snooping for timing attacks is
    189 	 * Harder.
    190 	 */
    191 	return ((u_int)mac_size == len) && !memcmp(dbuf, pkt_ptr, len);
    192 }
    193 
    194 static int
    195 hex_val(
    196 	unsigned char x
    197 	)
    198 {
    199 	int val;
    200 
    201 	if ('0' <= x && x <= '9')
    202 		val = x - '0';
    203 	else if ('a' <= x && x <= 'f')
    204 		val = x - 'a' + 0xa;
    205 	else if ('A' <= x && x <= 'F')
    206 		val = x - 'A' + 0xA;
    207 	else
    208 		val = -1;
    209 
    210 	return val;
    211 }
    212 
    213 /* Load keys from the specified keyfile into the key structures.
    214  * Returns -1 if the reading failed, otherwise it returns the
    215  * number of keys it read
    216  */
    217 int
    218 auth_init(
    219 	const char *keyfile,
    220 	struct key **keys
    221 	)
    222 {
    223 	FILE *keyf = fopen(keyfile, "r");
    224 	struct key *prev = NULL;
    225 	int scan_cnt, line_cnt = 1;
    226 	char kbuf[200];
    227 	char keystring[129];
    228 
    229 	/* HMS: Is it OK to do this later, after we know we have a key file? */
    230 	INIT_SSL();
    231 
    232 	if (keyf == NULL) {
    233 		if (debug)
    234 			printf("sntp auth_init: Couldn't open key file %s for reading!\n", keyfile);
    235 		return -1;
    236 	}
    237 	if (feof(keyf)) {
    238 		if (debug)
    239 			printf("sntp auth_init: Key file %s is empty!\n", keyfile);
    240 		fclose(keyf);
    241 		return -1;
    242 	}
    243 	key_cnt = 0;
    244 	while (!feof(keyf)) {
    245 		char * octothorpe;
    246 		struct key *act;
    247 		int goodline = 0;
    248 
    249 		if (NULL == fgets(kbuf, sizeof(kbuf), keyf))
    250 			continue;
    251 
    252 		kbuf[sizeof(kbuf) - 1] = '\0';
    253 		octothorpe = strchr(kbuf, '#');
    254 		if (octothorpe)
    255 			*octothorpe = '\0';
    256 		act = emalloc(sizeof(*act));
    257 		/* keep width 15 = sizeof struct key.typen - 1 synced */
    258 		scan_cnt = sscanf(kbuf, "%d %15s %128s",
    259 					&act->key_id, act->typen, keystring);
    260 		if (scan_cnt == 3) {
    261 			int len = strlen(keystring);
    262 			goodline = 1;	/* assume best for now */
    263 			if (len <= 20) {
    264 				act->key_len = len;
    265 				memcpy(act->key_seq, keystring, len + 1);
    266 			} else if ((len & 1) != 0) {
    267 				goodline = 0; /* it's bad */
    268 			} else {
    269 				int j;
    270 				act->key_len = len >> 1;
    271 				for (j = 0; j < len; j+=2) {
    272 					int val;
    273 					val = (hex_val(keystring[j]) << 4) |
    274 					       hex_val(keystring[j+1]);
    275 					if (val < 0) {
    276 						goodline = 0; /* it's bad */
    277 						break;
    278 					}
    279 					act->key_seq[j>>1] = (char)val;
    280 				}
    281 			}
    282 			act->typei = keytype_from_text(act->typen, NULL);
    283 			if (0 == act->typei) {
    284 				printf("%s: line %d: key %d, %s not supported - ignoring\n",
    285 					keyfile, line_cnt,
    286 					act->key_id, act->typen);
    287 				goodline = 0; /* it's bad */
    288 			}
    289 		}
    290 		if (goodline) {
    291 			act->next = NULL;
    292 			if (NULL == prev)
    293 				*keys = act;
    294 			else
    295 				prev->next = act;
    296 			prev = act;
    297 			key_cnt++;
    298 		} else {
    299 			if (debug) {
    300 				printf("auth_init: scanf %d items, skipping line %d.",
    301 					scan_cnt, line_cnt);
    302 			}
    303 			free(act);
    304 		}
    305 		line_cnt++;
    306 	}
    307 	fclose(keyf);
    308 
    309 	key_ptr = *keys;
    310 	return key_cnt;
    311 }
    312 
    313 /* Looks for the key with keyid key_id and sets the d_key pointer to the
    314  * address of the key. If no matching key is found the pointer is not touched.
    315  */
    316 void
    317 get_key(
    318 	int key_id,
    319 	struct key **d_key
    320 	)
    321 {
    322 	struct key *itr_key;
    323 
    324 	if (key_cnt == 0)
    325 		return;
    326 	for (itr_key = key_ptr; itr_key; itr_key = itr_key->next) {
    327 		if (itr_key->key_id == key_id) {
    328 			*d_key = itr_key;
    329 			break;
    330 		}
    331 	}
    332 	return;
    333 }
    334