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, ×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 *
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