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, ×tamp); 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