xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/rbac/util.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: util.c,v 1.2 2021/08/14 16:14:53 christos Exp $	*/
2 
3 /* util.c - RBAC utility */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  *
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  */
20 
21 #include <sys/cdefs.h>
22 __RCSID("$NetBSD: util.c,v 1.2 2021/08/14 16:14:53 christos Exp $");
23 
24 #include "portable.h"
25 
26 #include <stdio.h>
27 
28 #include <ac/ctype.h>
29 #include <ac/string.h>
30 
31 #include "slap.h"
32 #include "slap-config.h"
33 #include "lutil.h"
34 
35 #include "rbac.h"
36 
37 #define DELIMITER '$'
38 
39 #define SUNDAY 0x01
40 #define MONDAY 0x02
41 #define TUESDAY 0x04
42 #define WEDNESDAY 0x08
43 #define THURSDAY 0x10
44 #define FRIDAY 0x20
45 #define SATURDAY 0x40
46 
47 #define ALL_WEEK "all"
48 
49 void
rbac_free_constraint(rbac_constraint_t * cp)50 rbac_free_constraint( rbac_constraint_t *cp )
51 {
52 	if ( !cp ) return;
53 
54 	if ( !BER_BVISNULL( &cp->name ) ) {
55 		ch_free( cp->name.bv_val );
56 	}
57 
58 	ch_free( cp );
59 }
60 
61 void
rbac_free_constraints(rbac_constraint_t * constraints)62 rbac_free_constraints( rbac_constraint_t *constraints )
63 {
64 	rbac_constraint_t *cp, *tmp;
65 
66 	if ( !constraints ) return;
67 
68 	tmp = constraints;
69 	while ( tmp ) {
70 		cp = tmp->next;
71 		rbac_free_constraint( tmp );
72 		tmp = cp;
73 	}
74 
75 	return;
76 }
77 
78 rbac_constraint_t *
rbac_alloc_constraint()79 rbac_alloc_constraint()
80 {
81 	rbac_constraint_t *cp = NULL;
82 
83 	cp = ch_calloc( 1, sizeof(rbac_constraint_t) );
84 	return cp;
85 }
86 
87 static int
is_well_formed_constraint(struct berval * bv)88 is_well_formed_constraint( struct berval *bv )
89 {
90 	int rc = LDAP_SUCCESS;
91 
92 	/* assume well-formed role/user-constraints, for the moment */
93 
94 	if ( rc != LDAP_SUCCESS ) {
95 		Debug( LDAP_DEBUG_ANY, "is_well_formed_constraint: "
96 				"rbac role/user constraint not well-formed: %s\n",
97 				bv->bv_val );
98 	}
99 
100 	return rc;
101 }
102 
103 /* input contains 4 digits, representing time */
104 /* in hhmm format */
105 static int
constraint_parse_time(char * input)106 constraint_parse_time( char *input )
107 {
108 	int btime;
109 	char *ptr = input;
110 
111 	btime = ( *ptr++ - '0' ) * 12;
112 	btime += ( *ptr++ - '0' );
113 	btime *= 60; /* turning into mins */
114 	btime += ( *ptr++ - '0' ) * 10;
115 	btime += ( *ptr++ - '0' );
116 	btime *= 60; /* turning into secs */
117 
118 	return btime;
119 }
120 
121 /* input contains 4 digits, representing year */
122 /* in yyyy format */
123 static int
constraint_parse_year(char * input)124 constraint_parse_year( char *input )
125 {
126 	int i;
127 	int year = 0;
128 	char *ptr = input;
129 
130 	for ( i = 0; i <= 3; i++, ptr++ ) {
131 		year = year * 10 + *ptr - '0';
132 	}
133 
134 	return year;
135 }
136 
137 /* input contains 2 digits, representing month */
138 /* in mm format */
139 static int
constraint_parse_month(char * input)140 constraint_parse_month( char *input )
141 {
142 	int i;
143 	int month = 0;
144 	char *ptr = input;
145 
146 	for ( i = 0; i < 2; i++, ptr++ ) {
147 		month = month * 10 + *ptr - '0';
148 	}
149 
150 	return month;
151 }
152 
153 /* input contains 2 digits, representing day in month */
154 /* in dd format */
155 static int
constraint_parse_day_in_month(char * input)156 constraint_parse_day_in_month( char *input )
157 {
158 	int i;
159 	int day_in_month = 0;
160 	char *ptr = input;
161 
162 	for ( i = 0; i < 2; i++, ptr++ ) {
163 		day_in_month = day_in_month * 10 + *ptr - '0';
164 	}
165 
166 	return day_in_month;
167 }
168 
169 rbac_constraint_t *
rbac_bv2constraint(struct berval * bv)170 rbac_bv2constraint( struct berval *bv )
171 {
172 	rbac_constraint_t *cp = NULL;
173 	int rc = LDAP_SUCCESS;
174 	char *ptr, *endp = NULL;
175 	int len = 0;
176 	int year, month, mday;
177 
178 	if ( !bv || BER_BVISNULL( bv ) ) goto done;
179 
180 	rc = is_well_formed_constraint( bv );
181 	if ( rc != LDAP_SUCCESS ) {
182 		goto done;
183 	}
184 
185 	cp = rbac_alloc_constraint();
186 	if ( !cp ) {
187 		rc = LDAP_UNWILLING_TO_PERFORM;
188 		goto done;
189 	}
190 
191 	/* constraint name */
192 	ptr = bv->bv_val;
193 	endp = ptr;
194 	while ( *endp != DELIMITER ) {
195 		endp++;
196 		len++;
197 	}
198 
199 	if ( len > 0 ) {
200 		cp->name.bv_val = ch_malloc( len + 1 );
201 		strncpy( cp->name.bv_val, ptr, len );
202 		cp->name.bv_val[len] = '\0';
203 		cp->name.bv_len = len;
204 	} else {
205 		rc = LDAP_OTHER;
206 		goto done;
207 	}
208 
209 	/* allowed inactivity period */
210 	ptr = endp;
211 	endp++;
212 	if ( isdigit( *endp ) ) {
213 		int secs = 0;
214 		while ( isdigit( *endp ) ) {
215 			secs = secs * 10 + *endp - '0';
216 			endp++;
217 		}
218 		cp->allowed_inactivity = secs;
219 	} else if ( *endp != DELIMITER ) {
220 		rc = LDAP_OTHER;
221 		goto done;
222 	}
223 
224 	ptr = endp;
225 	endp = ptr + 1;
226 
227 	/* begin time */
228 	if ( isdigit( *endp ) ) {
229 		cp->begin_time = constraint_parse_time( endp );
230 		while ( isdigit( *endp ) )
231 			endp++;
232 	}
233 
234 	ptr = endp;
235 	while ( *ptr != DELIMITER )
236 		ptr++;
237 	endp = ptr + 1;
238 
239 	/* end time */
240 	if ( isdigit( *endp ) ) {
241 		cp->end_time = constraint_parse_time( endp );
242 		while ( isdigit( *endp ) )
243 			endp++;
244 	}
245 
246 	ptr = endp;
247 	while ( *ptr != DELIMITER )
248 		ptr++;
249 	endp = ptr + 1;
250 
251 	/* begin year/month/day_in_month */
252 	if ( isdigit( *endp ) ) {
253 		lutil_tm tm;
254 		year = constraint_parse_year( endp );
255 		endp += 4;
256 		month = constraint_parse_month( endp );
257 		endp += 2;
258 		mday = constraint_parse_day_in_month( endp );
259 		endp += 2;
260 
261 		tm.tm_year = year - 1900;
262 		tm.tm_mon = month - 1;
263 		tm.tm_mday = mday;
264 		tm.tm_sec = 0;
265 		tm.tm_min = 0;
266 		tm.tm_hour = 0;
267 
268 		lutil_tm2time( &tm, &cp->begin_date );
269 	}
270 
271 	ptr = endp;
272 	while ( *ptr != DELIMITER )
273 		ptr++;
274 	endp = ptr + 1;
275 
276 	/* end year/month/day_in_month */
277 	if ( isdigit( *endp ) ) {
278 		lutil_tm tm;
279 		year = constraint_parse_year( endp );
280 		endp += 4;
281 		month = constraint_parse_month( endp );
282 		endp += 2;
283 		mday = constraint_parse_day_in_month( endp );
284 		endp += 2;
285 
286 		tm.tm_year = year - 1900;
287 		tm.tm_mon = month - 1;
288 		tm.tm_mday = mday;
289 		tm.tm_sec = 0;
290 		tm.tm_min = 0;
291 		tm.tm_hour = 0;
292 
293 		lutil_tm2time( &tm, &cp->end_date );
294 	}
295 
296 	ptr = endp;
297 	while ( *ptr != DELIMITER )
298 		ptr++;
299 	endp = ptr + 1;
300 
301 	/* begin lock year/month/day_in_month */
302 	if ( isdigit( *endp ) ) {
303 		lutil_tm tm;
304 		year = constraint_parse_year( endp );
305 		endp += 4;
306 		month = constraint_parse_month( endp );
307 		endp += 2;
308 		mday = constraint_parse_day_in_month( endp );
309 		endp += 2;
310 
311 		tm.tm_year = year - 1900;
312 		tm.tm_mon = month - 1;
313 		tm.tm_mday = mday;
314 		tm.tm_sec = 0;
315 		tm.tm_min = 0;
316 		tm.tm_hour = 0;
317 
318 		lutil_tm2time( &tm, &cp->begin_lock_date );
319 	}
320 
321 	ptr = endp;
322 	while ( *ptr != DELIMITER )
323 		ptr++;
324 	endp = ptr + 1;
325 
326 	/* end lock year/month/day_in_month */
327 	if ( isdigit( *endp ) ) {
328 		lutil_tm tm;
329 
330 		year = constraint_parse_year( endp );
331 		endp += 4;
332 		month = constraint_parse_month( endp );
333 		endp += 2;
334 		mday = constraint_parse_day_in_month( endp );
335 		endp += 2;
336 
337 		tm.tm_year = year - 1900;
338 		tm.tm_mon = month - 1;
339 		tm.tm_mday = mday;
340 		tm.tm_sec = 0;
341 		tm.tm_min = 0;
342 		tm.tm_hour = 0;
343 
344 		lutil_tm2time( &tm, &cp->end_lock_date );
345 	}
346 
347 	ptr = endp;
348 	while ( *ptr != DELIMITER )
349 		ptr++;
350 	endp = ptr + 1;
351 
352 	/* dayMask */
353 
354 	/* allow "all" to mean the entire week */
355 	if ( strncasecmp( endp, ALL_WEEK, strlen( ALL_WEEK ) ) == 0 ) {
356 		cp->day_mask = SUNDAY | MONDAY | TUESDAY | WEDNESDAY | THURSDAY |
357 				FRIDAY | SATURDAY;
358 	}
359 
360 	while ( *endp && isdigit( *endp ) ) {
361 		switch ( *endp - '0' ) {
362 			case 1:
363 				cp->day_mask |= SUNDAY;
364 				break;
365 			case 2:
366 				cp->day_mask |= MONDAY;
367 				break;
368 			case 3:
369 				cp->day_mask |= TUESDAY;
370 				break;
371 			case 4:
372 				cp->day_mask |= WEDNESDAY;
373 				break;
374 			case 5:
375 				cp->day_mask |= THURSDAY;
376 				break;
377 			case 6:
378 				cp->day_mask |= FRIDAY;
379 				break;
380 			case 7:
381 				cp->day_mask |= SATURDAY;
382 				break;
383 			default:
384 				/* should not be here */
385 				rc = LDAP_OTHER;
386 				goto done;
387 		}
388 		endp++;
389 	}
390 
391 done:;
392 	if ( rc != LDAP_SUCCESS ) {
393 		rbac_free_constraint( cp );
394 		cp = NULL;
395 	}
396 
397 	return cp;
398 }
399 
400 static int
constraint_day_of_week(rbac_constraint_t * cp,int wday)401 constraint_day_of_week( rbac_constraint_t *cp, int wday )
402 {
403 	int rc = LDAP_UNWILLING_TO_PERFORM;
404 
405 	/* assumption: Monday is 1st day of a week */
406 	switch ( wday ) {
407 		case 1:
408 			if ( !(cp->day_mask & MONDAY) ) goto done;
409 			break;
410 		case 2:
411 			if ( !(cp->day_mask & TUESDAY) ) goto done;
412 			break;
413 		case 3:
414 			if ( !(cp->day_mask & WEDNESDAY) ) goto done;
415 			break;
416 		case 4:
417 			if ( !(cp->day_mask & THURSDAY) ) goto done;
418 			break;
419 		case 5:
420 			if ( !(cp->day_mask & FRIDAY) ) goto done;
421 			break;
422 		case 6:
423 			if ( !(cp->day_mask & SATURDAY) ) goto done;
424 			break;
425 		case 0:
426 		case 7:
427 			if ( !(cp->day_mask & SUNDAY) ) goto done;
428 			break;
429 		default:
430 			/* should not be here */
431 			goto done;
432 	}
433 
434 	rc = LDAP_SUCCESS;
435 
436 done:;
437 	return rc;
438 }
439 
440 int
rbac_check_time_constraint(rbac_constraint_t * cp)441 rbac_check_time_constraint( rbac_constraint_t *cp )
442 {
443 	int rc = LDAP_UNWILLING_TO_PERFORM;
444 	time_t now;
445 	struct tm result, *resultp;
446 
447 	now = slap_get_time();
448 
449 	/*
450 	 * does slapd support day-of-week (wday)?
451 	 * using native routine for now.
452 	 * Win32's gmtime call is already thread-safe, to the _r
453 	 * decorator is unneeded.
454 	 */
455 #ifdef _WIN32
456 	resultp = gmtime( &now );
457 #else
458 	resultp = gmtime_r( &now, &result );
459 #endif
460 	if ( !resultp ) goto done;
461 #if 0
462 	timestamp.bv_val = timebuf;
463 	timestamp.bv_len = sizeof(timebuf);
464 	slap_timestamp(&now, &timestamp);
465 	lutil_parsetime(timestamp.bv_val, &now_tm);
466 	lutil_tm2time(&now_tm, &now_tt);
467 #endif
468 
469 	if ( ( cp->begin_date.tt_sec > 0 && cp->begin_date.tt_sec > now ) ||
470 			( cp->end_date.tt_sec > 0 && cp->end_date.tt_sec < now ) ) {
471 		/* not within allowed time period */
472 		goto done;
473 	}
474 
475 	/* allowed time period during a day */
476 	if ( cp->begin_time > 0 && cp->end_time > 0 ) {
477 		int timeofday = ( resultp->tm_hour * 60 + resultp->tm_min ) * 60 +
478 				resultp->tm_sec;
479 		if ( timeofday < cp->begin_time || timeofday > cp->end_time ) {
480 			/* not within allowed time period in a day */
481 			goto done;
482 		}
483 	}
484 
485 	/* allowed day in a week */
486 	if ( cp->day_mask > 0 ) {
487 		rc = constraint_day_of_week( cp, resultp->tm_wday );
488 		if ( rc != LDAP_SUCCESS ) goto done;
489 	}
490 
491 	/* during lock-out period? */
492 	if ( ( cp->begin_lock_date.tt_sec > 0 &&
493 				 cp->begin_lock_date.tt_sec < now ) &&
494 			( cp->end_lock_date.tt_sec > 0 &&
495 					cp->end_lock_date.tt_sec > now ) ) {
496 		/* within locked out period */
497 		rc = LDAP_UNWILLING_TO_PERFORM;
498 		goto done;
499 	}
500 
501 	/* passed all tests */
502 	rc = LDAP_SUCCESS;
503 
504 done:;
505 	return rc;
506 }
507 
508 rbac_constraint_t *
rbac_role2constraint(struct berval * role,rbac_constraint_t * role_constraints)509 rbac_role2constraint( struct berval *role, rbac_constraint_t *role_constraints )
510 {
511 	rbac_constraint_t *cp = NULL;
512 
513 	if ( !role_constraints || !role ) goto done;
514 
515 	cp = role_constraints;
516 	while ( cp ) {
517 		if ( ber_bvstrcasecmp( role, &cp->name ) == 0 ) {
518 			/* found the role constraint */
519 			goto done;
520 		}
521 		cp = cp->next;
522 	}
523 
524 done:;
525 	return cp;
526 }
527 
528 void
rbac_to_lower(struct berval * bv)529 rbac_to_lower( struct berval *bv )
530 {
531 	// convert the berval to lower case:
532 	int i;
533 	for ( i = 0; i < bv->bv_len; i++ ) {
534 		bv->bv_val[i] = tolower( bv->bv_val[i] );
535 	}
536 }
537