Home | History | Annotate | Download | only in wpa_supplicant
      1 /*
      2  * ASN.1 DER parsing
      3  * Copyright (c) 2006, Jouni Malinen <j (at) w1.fi>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License version 2 as
      7  * published by the Free Software Foundation.
      8  *
      9  * Alternatively, this software may be distributed under the terms of BSD
     10  * license.
     11  *
     12  * See README and COPYING for more details.
     13  */
     14 
     15 #include "includes.h"
     16 
     17 #include "common.h"
     18 
     19 #ifdef CONFIG_INTERNAL_X509
     20 
     21 #include "asn1.h"
     22 
     23 int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
     24 {
     25 	const u8 *pos, *end;
     26 	u8 tmp;
     27 
     28 	os_memset(hdr, 0, sizeof(*hdr));
     29 	pos = buf;
     30 	end = buf + len;
     31 
     32 	hdr->identifier = *pos++;
     33 	hdr->class = hdr->identifier >> 6;
     34 	hdr->constructed = !!(hdr->identifier & (1 << 5));
     35 
     36 	if ((hdr->identifier & 0x1f) == 0x1f) {
     37 		hdr->tag = 0;
     38 		do {
     39 			if (pos >= end) {
     40 				wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
     41 					   "underflow");
     42 				return -1;
     43 			}
     44 			tmp = *pos++;
     45 			wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: "
     46 				   "0x%02x", tmp);
     47 			hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
     48 		} while (tmp & 0x80);
     49 	} else
     50 		hdr->tag = hdr->identifier & 0x1f;
     51 
     52 	tmp = *pos++;
     53 	if (tmp & 0x80) {
     54 		if (tmp == 0xff) {
     55 			wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
     56 				   "value 0xff used");
     57 			return -1;
     58 		}
     59 		tmp &= 0x7f; /* number of subsequent octets */
     60 		hdr->length = 0;
     61 		if (tmp > 4) {
     62 			wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
     63 			return -1;
     64 		}
     65 		while (tmp--) {
     66 			if (pos >= end) {
     67 				wpa_printf(MSG_DEBUG, "ASN.1: Length "
     68 					   "underflow");
     69 				return -1;
     70 			}
     71 			hdr->length = (hdr->length << 8) | *pos++;
     72 		}
     73 	} else {
     74 		/* Short form - length 0..127 in one octet */
     75 		hdr->length = tmp;
     76 	}
     77 
     78 	if (end < pos || hdr->length > (unsigned int) (end - pos)) {
     79 		wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
     80 		return -1;
     81 	}
     82 
     83 	hdr->payload = pos;
     84 	return 0;
     85 }
     86 
     87 
     88 int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
     89 		 const u8 **next)
     90 {
     91 	struct asn1_hdr hdr;
     92 	const u8 *pos, *end;
     93 	unsigned long val;
     94 	u8 tmp;
     95 
     96 	os_memset(oid, 0, sizeof(*oid));
     97 
     98 	if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
     99 		return -1;
    100 
    101 	if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
    102 		wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
    103 			   "tag 0x%x", hdr.class, hdr.tag);
    104 		return -1;
    105 	}
    106 
    107 	pos = hdr.payload;
    108 	end = hdr.payload + hdr.length;
    109 	*next = end;
    110 
    111 	while (pos < end) {
    112 		val = 0;
    113 
    114 		do {
    115 			if (pos >= end)
    116 				return -1;
    117 			tmp = *pos++;
    118 			val = (val << 7) | (tmp & 0x7f);
    119 		} while (tmp & 0x80);
    120 
    121 		if (oid->len >= ASN1_MAX_OID_LEN) {
    122 			wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
    123 			return -1;
    124 		}
    125 		if (oid->len == 0) {
    126 			/*
    127 			 * The first octet encodes the first two object
    128 			 * identifier components in (X*40) + Y formula.
    129 			 * X = 0..2.
    130 			 */
    131 			oid->oid[0] = val / 40;
    132 			if (oid->oid[0] > 2)
    133 				oid->oid[0] = 2;
    134 			oid->oid[1] = val - oid->oid[0] * 40;
    135 			oid->len = 2;
    136 		} else
    137 			oid->oid[oid->len++] = val;
    138 	}
    139 
    140 	return 0;
    141 }
    142 
    143 
    144 void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
    145 {
    146 	char *pos = buf;
    147 	size_t i;
    148 	int ret;
    149 
    150 	if (len == 0)
    151 		return;
    152 
    153 	buf[0] = '\0';
    154 
    155 	for (i = 0; i < oid->len; i++) {
    156 		ret = os_snprintf(pos, buf + len - pos,
    157 				  "%s%lu",
    158 				  i == 0 ? "" : ".", oid->oid[i]);
    159 		if (ret < 0 || ret >= buf + len - pos)
    160 			break;
    161 		pos += ret;
    162 	}
    163 	buf[len - 1] = '\0';
    164 }
    165 
    166 
    167 static u8 rotate_bits(u8 octet)
    168 {
    169 	int i;
    170 	u8 res;
    171 
    172 	res = 0;
    173 	for (i = 0; i < 8; i++) {
    174 		res <<= 1;
    175 		if (octet & 1)
    176 			res |= 1;
    177 		octet >>= 1;
    178 	}
    179 
    180 	return res;
    181 }
    182 
    183 
    184 unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
    185 {
    186 	unsigned long val = 0;
    187 	const u8 *pos = buf;
    188 
    189 	/* BER requires that unused bits are zero, so we can ignore the number
    190 	 * of unused bits */
    191 	pos++;
    192 
    193 	if (len >= 2)
    194 		val |= rotate_bits(*pos++);
    195 	if (len >= 3)
    196 		val |= ((unsigned long) rotate_bits(*pos++)) << 8;
    197 	if (len >= 4)
    198 		val |= ((unsigned long) rotate_bits(*pos++)) << 16;
    199 	if (len >= 5)
    200 		val |= ((unsigned long) rotate_bits(*pos++)) << 24;
    201 	if (len >= 6)
    202 		wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
    203 			   "(BIT STRING length %lu)",
    204 			   __func__, (unsigned long) len);
    205 
    206 	return val;
    207 }
    208 
    209 #endif /* CONFIG_INTERNAL_X509 */
    210