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