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