xref: /openbsd/usr.sbin/ldapd/syntax.c (revision 7cf58871)
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 
34 SYNTAX_DECL(bit_string);
35 SYNTAX_DECL(boolean);
36 SYNTAX_DECL(country);
37 SYNTAX_DECL(directory_string);
38 SYNTAX_DECL(dn);
39 SYNTAX_DECL(gentime);
40 SYNTAX_DECL(ia5_string);
41 SYNTAX_DECL(integer);
42 SYNTAX_DECL(numeric_string);
43 SYNTAX_DECL(octet_string);
44 SYNTAX_DECL(oid);
45 SYNTAX_DECL(printable_string);
46 SYNTAX_DECL(utctime);
47 SYNTAX_DECL(uuid);
48 
49 static 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 
96 static int
syntax_cmp(const void * k,const void * e)97 syntax_cmp(const void *k, const void *e)
98 {
99 	return (strcmp(k, ((const struct syntax *)e)->oid));
100 }
101 
102 const struct syntax *
syntax_lookup(const char * oid)103 syntax_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  */
113 static int
syntax_is_octet_string(struct schema * schema,char * value,size_t len)114 syntax_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  */
122 static int
syntax_is_directory_string(struct schema * schema,char * value,size_t len)123 syntax_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  */
138 static int
syntax_is_printable_string(struct schema * schema,char * value,size_t len)139 syntax_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  */
157 static int
syntax_is_ia5_string(struct schema * schema,char * value,size_t len)158 syntax_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  */
175 static int
syntax_is_integer(struct schema * schema,char * value,size_t len)176 syntax_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 
188 static int
syntax_is_dn(struct schema * schema,char * value,size_t len)189 syntax_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 
199 static int
syntax_is_oid(struct schema * schema,char * value,size_t len)200 syntax_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 
222 static int
syntax_is_uuid(struct schema * schema,char * value,size_t len)223 syntax_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  */
251 static int
syntax_is_numeric_string(struct schema * schema,char * value,size_t len)252 syntax_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 
263 static int
syntax_is_time(struct schema * schema,char * value,size_t len,int gen)264 syntax_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 
311 static int
syntax_is_gentime(struct schema * schema,char * value,size_t len)312 syntax_is_gentime(struct schema *schema, char *value, size_t len)
313 {
314 	return syntax_is_time(schema, value, len, 1);
315 }
316 
317 static int
syntax_is_utctime(struct schema * schema,char * value,size_t len)318 syntax_is_utctime(struct schema *schema, char *value, size_t len)
319 {
320 	return syntax_is_time(schema, value, len, 0);
321 }
322 
323 static int
syntax_is_country(struct schema * schema,char * value,size_t len)324 syntax_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 
331 static int
syntax_is_bit_string(struct schema * schema,char * value,size_t len)332 syntax_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 
350 static int
syntax_is_boolean(struct schema * schema,char * value,size_t len)351 syntax_is_boolean(struct schema *schema, char *value, size_t len)
352 {
353 	return strcmp(value, "TRUE") == 0 || strcmp(value, "FALSE") == 0;
354 }
355 
356