Home | History | Annotate | Line # | Download | only in rbac
      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
     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
     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 *
     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
     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
    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
    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
    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
    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 *
    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
    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
    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 *
    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
    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