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