162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* ASN.1 Object identifier (OID) registry 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/oid_registry.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/bug.h> 1462306a36Sopenharmony_ci#include <linux/asn1.h> 1562306a36Sopenharmony_ci#include "oid_registry_data.c" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciMODULE_DESCRIPTION("OID Registry"); 1862306a36Sopenharmony_ciMODULE_AUTHOR("Red Hat, Inc."); 1962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/** 2262306a36Sopenharmony_ci * look_up_OID - Find an OID registration for the specified data 2362306a36Sopenharmony_ci * @data: Binary representation of the OID 2462306a36Sopenharmony_ci * @datasize: Size of the binary representation 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_cienum OID look_up_OID(const void *data, size_t datasize) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci const unsigned char *octets = data; 2962306a36Sopenharmony_ci enum OID oid; 3062306a36Sopenharmony_ci unsigned char xhash; 3162306a36Sopenharmony_ci unsigned i, j, k, hash; 3262306a36Sopenharmony_ci size_t len; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* Hash the OID data */ 3562306a36Sopenharmony_ci hash = datasize - 1; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci for (i = 0; i < datasize; i++) 3862306a36Sopenharmony_ci hash += octets[i] * 33; 3962306a36Sopenharmony_ci hash = (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ hash; 4062306a36Sopenharmony_ci hash &= 0xff; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* Binary search the OID registry. OIDs are stored in ascending order 4362306a36Sopenharmony_ci * of hash value then ascending order of size and then in ascending 4462306a36Sopenharmony_ci * order of reverse value. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci i = 0; 4762306a36Sopenharmony_ci k = OID__NR; 4862306a36Sopenharmony_ci while (i < k) { 4962306a36Sopenharmony_ci j = (i + k) / 2; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci xhash = oid_search_table[j].hash; 5262306a36Sopenharmony_ci if (xhash > hash) { 5362306a36Sopenharmony_ci k = j; 5462306a36Sopenharmony_ci continue; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci if (xhash < hash) { 5762306a36Sopenharmony_ci i = j + 1; 5862306a36Sopenharmony_ci continue; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci oid = oid_search_table[j].oid; 6262306a36Sopenharmony_ci len = oid_index[oid + 1] - oid_index[oid]; 6362306a36Sopenharmony_ci if (len > datasize) { 6462306a36Sopenharmony_ci k = j; 6562306a36Sopenharmony_ci continue; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci if (len < datasize) { 6862306a36Sopenharmony_ci i = j + 1; 6962306a36Sopenharmony_ci continue; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Variation is most likely to be at the tail end of the 7362306a36Sopenharmony_ci * OID, so do the comparison in reverse. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci while (len > 0) { 7662306a36Sopenharmony_ci unsigned char a = oid_data[oid_index[oid] + --len]; 7762306a36Sopenharmony_ci unsigned char b = octets[len]; 7862306a36Sopenharmony_ci if (a > b) { 7962306a36Sopenharmony_ci k = j; 8062306a36Sopenharmony_ci goto next; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci if (a < b) { 8362306a36Sopenharmony_ci i = j + 1; 8462306a36Sopenharmony_ci goto next; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci return oid; 8862306a36Sopenharmony_ci next: 8962306a36Sopenharmony_ci ; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return OID__NR; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(look_up_OID); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * parse_OID - Parse an OID from a bytestream 9862306a36Sopenharmony_ci * @data: Binary representation of the header + OID 9962306a36Sopenharmony_ci * @datasize: Size of the binary representation 10062306a36Sopenharmony_ci * @oid: Pointer to oid to return result 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * Parse an OID from a bytestream that holds the OID in the format 10362306a36Sopenharmony_ci * ASN1_OID | length | oid. The length indicator must equal to datasize - 2. 10462306a36Sopenharmony_ci * -EBADMSG is returned if the bytestream is too short. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_ciint parse_OID(const void *data, size_t datasize, enum OID *oid) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci const unsigned char *v = data; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* we need 2 bytes of header and at least 1 byte for oid */ 11162306a36Sopenharmony_ci if (datasize < 3 || v[0] != ASN1_OID || v[1] != datasize - 2) 11262306a36Sopenharmony_ci return -EBADMSG; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci *oid = look_up_OID(data + 2, datasize - 2); 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(parse_OID); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* 12062306a36Sopenharmony_ci * sprint_OID - Print an Object Identifier into a buffer 12162306a36Sopenharmony_ci * @data: The encoded OID to print 12262306a36Sopenharmony_ci * @datasize: The size of the encoded OID 12362306a36Sopenharmony_ci * @buffer: The buffer to render into 12462306a36Sopenharmony_ci * @bufsize: The size of the buffer 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * The OID is rendered into the buffer in "a.b.c.d" format and the number of 12762306a36Sopenharmony_ci * bytes is returned. -EBADMSG is returned if the data could not be interpreted 12862306a36Sopenharmony_ci * and -ENOBUFS if the buffer was too small. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ciint sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci const unsigned char *v = data, *end = v + datasize; 13362306a36Sopenharmony_ci unsigned long num; 13462306a36Sopenharmony_ci unsigned char n; 13562306a36Sopenharmony_ci size_t ret; 13662306a36Sopenharmony_ci int count; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (v >= end) 13962306a36Sopenharmony_ci goto bad; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci n = *v++; 14262306a36Sopenharmony_ci ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40); 14362306a36Sopenharmony_ci if (count >= bufsize) 14462306a36Sopenharmony_ci return -ENOBUFS; 14562306a36Sopenharmony_ci buffer += count; 14662306a36Sopenharmony_ci bufsize -= count; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci while (v < end) { 14962306a36Sopenharmony_ci n = *v++; 15062306a36Sopenharmony_ci if (!(n & 0x80)) { 15162306a36Sopenharmony_ci num = n; 15262306a36Sopenharmony_ci } else { 15362306a36Sopenharmony_ci num = n & 0x7f; 15462306a36Sopenharmony_ci do { 15562306a36Sopenharmony_ci if (v >= end) 15662306a36Sopenharmony_ci goto bad; 15762306a36Sopenharmony_ci n = *v++; 15862306a36Sopenharmony_ci num <<= 7; 15962306a36Sopenharmony_ci num |= n & 0x7f; 16062306a36Sopenharmony_ci } while (n & 0x80); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci ret += count = snprintf(buffer, bufsize, ".%lu", num); 16362306a36Sopenharmony_ci if (count >= bufsize) 16462306a36Sopenharmony_ci return -ENOBUFS; 16562306a36Sopenharmony_ci buffer += count; 16662306a36Sopenharmony_ci bufsize -= count; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cibad: 17262306a36Sopenharmony_ci snprintf(buffer, bufsize, "(bad)"); 17362306a36Sopenharmony_ci return -EBADMSG; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprint_oid); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/** 17862306a36Sopenharmony_ci * sprint_OID - Print an Object Identifier into a buffer 17962306a36Sopenharmony_ci * @oid: The OID to print 18062306a36Sopenharmony_ci * @buffer: The buffer to render into 18162306a36Sopenharmony_ci * @bufsize: The size of the buffer 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * The OID is rendered into the buffer in "a.b.c.d" format and the number of 18462306a36Sopenharmony_ci * bytes is returned. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ciint sprint_OID(enum OID oid, char *buffer, size_t bufsize) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int ret; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci BUG_ON(oid >= OID__NR); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci ret = sprint_oid(oid_data + oid_index[oid], 19362306a36Sopenharmony_ci oid_index[oid + 1] - oid_index[oid], 19462306a36Sopenharmony_ci buffer, bufsize); 19562306a36Sopenharmony_ci BUG_ON(ret == -EBADMSG); 19662306a36Sopenharmony_ci return ret; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sprint_OID); 199