1/*	$OpenBSD: syntax.c,v 1.5 2017/05/28 15:48:49 jmatthew Exp $ */
2
3/*
4 * Copyright (c) 2010 Martin Hedenfalk <martin@bzero.se>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/queue.h>
21#include <sys/tree.h>
22
23#include <ctype.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "schema.h"
29#include "uuid.h"
30
31#define SYNTAX_DECL(TYPE) \
32	static int syntax_is_##TYPE(struct schema *schema, char *value, size_t len)
33
34SYNTAX_DECL(bit_string);
35SYNTAX_DECL(boolean);
36SYNTAX_DECL(country);
37SYNTAX_DECL(directory_string);
38SYNTAX_DECL(dn);
39SYNTAX_DECL(gentime);
40SYNTAX_DECL(ia5_string);
41SYNTAX_DECL(integer);
42SYNTAX_DECL(numeric_string);
43SYNTAX_DECL(octet_string);
44SYNTAX_DECL(oid);
45SYNTAX_DECL(printable_string);
46SYNTAX_DECL(utctime);
47SYNTAX_DECL(uuid);
48
49static struct syntax syntaxes[] = {
50	/*
51	 * Keep these sorted.
52	 */
53	{ "1.3.6.1.1.1.0.0", "NIS netgroup triple", NULL },
54	{ "1.3.6.1.1.1.0.1", "Boot parameter", NULL },
55	{ "1.3.6.1.1.16.1", "UUID", syntax_is_uuid },
56	{ "1.3.6.1.4.1.1466.115.121.1.11", "Country String", syntax_is_country },
57	{ "1.3.6.1.4.1.1466.115.121.1.12", "DN", syntax_is_dn },
58	{ "1.3.6.1.4.1.1466.115.121.1.14", "Delivery Method", NULL },
59	{ "1.3.6.1.4.1.1466.115.121.1.15", "Directory String", syntax_is_directory_string },
60	{ "1.3.6.1.4.1.1466.115.121.1.16", "DIT Content Rule Description", NULL },
61	{ "1.3.6.1.4.1.1466.115.121.1.17", "DIT Structure Rule Description", NULL },
62	{ "1.3.6.1.4.1.1466.115.121.1.21", "Enhanced Guide", NULL },
63	{ "1.3.6.1.4.1.1466.115.121.1.22", "Facsimile Telephone Number", NULL },
64	{ "1.3.6.1.4.1.1466.115.121.1.23", "Fax", NULL },
65	{ "1.3.6.1.4.1.1466.115.121.1.24", "Generalized Time", syntax_is_gentime },
66	{ "1.3.6.1.4.1.1466.115.121.1.25", "Guide", NULL },
67	{ "1.3.6.1.4.1.1466.115.121.1.26", "IA5 String", syntax_is_ia5_string },
68	{ "1.3.6.1.4.1.1466.115.121.1.27", "INTEGER", syntax_is_integer },
69	{ "1.3.6.1.4.1.1466.115.121.1.28", "JPEG", NULL },
70	{ "1.3.6.1.4.1.1466.115.121.1.3",  "Attribute Type Description", NULL },
71	{ "1.3.6.1.4.1.1466.115.121.1.30", "Matching Rule Description", NULL },
72	{ "1.3.6.1.4.1.1466.115.121.1.31", "Matching Rule Use Description", NULL },
73	{ "1.3.6.1.4.1.1466.115.121.1.34", "Name And Optional UID", NULL },
74	{ "1.3.6.1.4.1.1466.115.121.1.35", "Name Form Description", NULL },
75	{ "1.3.6.1.4.1.1466.115.121.1.36", "Numeric String", syntax_is_numeric_string },
76	{ "1.3.6.1.4.1.1466.115.121.1.37", "Object Class Description", NULL },
77	{ "1.3.6.1.4.1.1466.115.121.1.38", "OID", syntax_is_oid },
78	{ "1.3.6.1.4.1.1466.115.121.1.39", "Other Mailbox", syntax_is_ia5_string },
79	{ "1.3.6.1.4.1.1466.115.121.1.40", "Octet String", syntax_is_octet_string },
80	{ "1.3.6.1.4.1.1466.115.121.1.41", "Postal Address", syntax_is_directory_string },
81	{ "1.3.6.1.4.1.1466.115.121.1.44", "Printable String", syntax_is_printable_string },
82	{ "1.3.6.1.4.1.1466.115.121.1.45", "Subtree Specification", NULL },
83	{ "1.3.6.1.4.1.1466.115.121.1.5",  "Binary", NULL },
84	{ "1.3.6.1.4.1.1466.115.121.1.50", "Telephone Number", syntax_is_printable_string },
85	{ "1.3.6.1.4.1.1466.115.121.1.51", "Teletex Terminal Identifier", NULL },
86	{ "1.3.6.1.4.1.1466.115.121.1.52", "Telex Number", NULL },
87	{ "1.3.6.1.4.1.1466.115.121.1.53", "UTC Time", syntax_is_utctime },
88	{ "1.3.6.1.4.1.1466.115.121.1.54", "LDAP Syntax Description", NULL },
89	{ "1.3.6.1.4.1.1466.115.121.1.58", "Substring Assertion", NULL },
90	{ "1.3.6.1.4.1.1466.115.121.1.6",  "Bit String", syntax_is_bit_string },
91	{ "1.3.6.1.4.1.1466.115.121.1.7",  "Boolean", syntax_is_boolean },
92	{ "1.3.6.1.4.1.1466.115.121.1.8",  "Certificate", NULL },
93
94};
95
96static int
97syntax_cmp(const void *k, const void *e)
98{
99	return (strcmp(k, ((const struct syntax *)e)->oid));
100}
101
102const struct syntax *
103syntax_lookup(const char *oid)
104{
105	return bsearch(oid, syntaxes, sizeof(syntaxes)/sizeof(syntaxes[0]),
106	    sizeof(syntaxes[0]), syntax_cmp);
107}
108
109/*
110 * A value of the Octet String syntax is a sequence of zero, one, or
111 * more arbitrary octets.
112 */
113static int
114syntax_is_octet_string(struct schema *schema, char *value, size_t len)
115{
116	return 1;
117}
118
119/*
120 * A string of one or more arbitrary UTF-8 characters.
121 */
122static int
123syntax_is_directory_string(struct schema *schema, char *value, size_t len)
124{
125	/* FIXME: validate UTF-8 characters. */
126	return len >= 1 && *value != '\0';
127}
128
129/*
130 * A value of the Printable String syntax is a string of one or more
131 * latin alphabetic, numeric, and selected punctuation characters as
132 * specified by the <PrintableCharacter> rule in Section 3.2.
133 *
134 *    PrintableCharacter = ALPHA / DIGIT / SQUOTE / LPAREN / RPAREN /
135 *                         PLUS / COMMA / HYPHEN / DOT / EQUALS /
136 *                         SLASH / COLON / QUESTION / SPACE
137 */
138static int
139syntax_is_printable_string(struct schema *schema, char *value, size_t len)
140{
141	static char	*special = "'()+,-.=/:? ";
142	char		*p;
143
144	for (p = value; len > 0 && *p != '\0'; p++, len--) {
145		if (!isalnum((unsigned char)*p) && strchr(special, *p) == NULL)
146			return 0;
147	}
148
149	return (p != value);
150}
151
152/*
153 * A value of the IA5 String syntax is a string of zero, one, or more
154 * characters from International Alphabet 5 (IA5).
155 *   IA5String          = *(%x00-7F)
156 */
157static int
158syntax_is_ia5_string(struct schema *schema, char *value, size_t len)
159{
160	char		*p;
161
162	for (p = value; *p != '\0'; p++) {
163		if ((unsigned char)*p > 0x7F)
164			return 0;
165	}
166
167	return 1;
168}
169
170/*
171 * A value of the Integer syntax is a whole number of unlimited magnitude.
172 *   Integer = ( HYPHEN LDIGIT *DIGIT ) / number
173 *   number  = DIGIT / ( LDIGIT 1*DIGIT )
174 */
175static int
176syntax_is_integer(struct schema *schema, char *value, size_t len)
177{
178	if (*value == '-')
179		value++;
180	if (*value == '0')
181		return value[1] == '\0';
182	for (value++; *value != '\0'; value++)
183		if (!isdigit((unsigned char)*value))
184			return 0;
185	return 1;
186}
187
188static int
189syntax_is_dn(struct schema *schema, char *value, size_t len)
190{
191	if (!syntax_is_directory_string(schema, value, len))
192		return 0;
193
194	/* FIXME: DN syntax not implemented */
195
196	return 1;
197}
198
199static int
200syntax_is_oid(struct schema *schema, char *value, size_t len)
201{
202	char	*symoid = NULL;
203
204	if (len == 0 || *value == '\0')
205		return 0;
206	if (is_oidstr(value))
207		return 1;
208
209	/*
210	 * Check for a symbolic OID: object class, attribute type or symoid.
211	 */
212	if (lookup_object_by_name(schema, value) != NULL ||
213	    lookup_attribute_by_name(schema, value) != NULL ||
214	    (symoid = lookup_symbolic_oid(schema, value)) != NULL) {
215		free(symoid);
216		return 1;
217	}
218
219	return 0;
220}
221
222static int
223syntax_is_uuid(struct schema *schema, char *value, size_t len)
224{
225	int	 i;
226
227	if (len != 36)
228		return 0;
229
230#define IS_XDIGITS(n, c)				\
231	do {						\
232		for (i = 0; i < (n); i++)		\
233			if (!isxdigit(*value++))	\
234				return 0;		\
235		if (*value++ != (c))			\
236			return 0;			\
237	} while(0)
238
239	IS_XDIGITS(8, '-');
240	IS_XDIGITS(4, '-');
241	IS_XDIGITS(4, '-');
242	IS_XDIGITS(4, '-');
243	IS_XDIGITS(12, '\0');
244
245	return 1;
246}
247
248/*
249 * NumericString = 1*(DIGIT / SPACE)
250 */
251static int
252syntax_is_numeric_string(struct schema *schema, char *value, size_t len)
253{
254	char	*p;
255
256	for (p = value; *p != '\0'; p++)
257		if (!isdigit((unsigned char)*p) || *p != ' ')
258			return 0;
259
260	return p != value;
261}
262
263static int
264syntax_is_time(struct schema *schema, char *value, size_t len, int gen)
265{
266	int	 n;
267	char	*p = value;
268
269#define CHECK_RANGE(min, max) \
270	do {						\
271		if (!isdigit((unsigned char)p[0]) ||	\
272		    !isdigit((unsigned char)p[1]))	\
273			return 0;			\
274		n = (p[0] - '0') * 10 + (p[1] - '0');	\
275		if (n < min || n > max)			\
276			return 0;			\
277		p += 2;					\
278	} while (0)
279
280	if (gen)
281		CHECK_RANGE(0, 99);		/* century */
282	CHECK_RANGE(0, 99);			/* year */
283	CHECK_RANGE(1, 12);			/* month */
284	CHECK_RANGE(1, 31);			/* day */
285	/* FIXME: should check number of days in month */
286	CHECK_RANGE(0, 23);			/* hour */
287
288	if (!gen || isdigit((unsigned char)*p)) {
289		CHECK_RANGE(0, 59);		/* minute */
290		if (isdigit((unsigned char)*p))
291			CHECK_RANGE(0, 59+gen);	/* second or leap-second */
292		if (!gen && *p == '\0')
293			return 1;
294	}
295						/* fraction */
296	if (!gen && ((*p == ',' || *p == '.') &&
297	    !isdigit((unsigned char)*++p)))
298		return 0;
299
300	if (*p == '-' || *p == '+') {
301		++p;
302		CHECK_RANGE(0, 23);		/* hour */
303		if (!gen || isdigit((unsigned char)*p))
304			CHECK_RANGE(0, 59);	/* minute */
305	} else if (*p++ != 'Z')
306		return 0;
307
308	return *p == '\0';
309}
310
311static int
312syntax_is_gentime(struct schema *schema, char *value, size_t len)
313{
314	return syntax_is_time(schema, value, len, 1);
315}
316
317static int
318syntax_is_utctime(struct schema *schema, char *value, size_t len)
319{
320	return syntax_is_time(schema, value, len, 0);
321}
322
323static int
324syntax_is_country(struct schema *schema, char *value, size_t len)
325{
326	if (len != 2)
327		return 0;
328	return syntax_is_printable_string(schema, value, len);
329}
330
331static int
332syntax_is_bit_string(struct schema *schema, char *value, size_t len)
333{
334	if (*value++ != '\'')
335		return 0;
336
337	for (; *value != '\0'; value++) {
338		if (*value == '\'')
339			break;
340		if (*value != '0' && *value != '1')
341			return 0;
342	}
343
344	if (++*value != 'B')
345		return 0;
346
347	return *value == '\0';
348}
349
350static int
351syntax_is_boolean(struct schema *schema, char *value, size_t len)
352{
353	return strcmp(value, "TRUE") == 0 || strcmp(value, "FALSE") == 0;
354}
355
356