Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / access.c
1 /* $XConsortium: access.c /main/5 1996/10/08 16:41:05 barstow $ */
2 /*
3  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
4  *  (c) Copyright 1993, 1994 International Business Machines Corp.
5  *  (c) Copyright 1993, 1994 Novell, Inc.
6  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
7  */
8
9
10 #include <EUSCompat.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <pwd.h>
15 #ifdef SunOS
16 #include <sys/systeminfo.h>
17 #endif
18 #include "access.h"
19 #include "appt4.h"
20 #include "utility.h"
21 #include "log.h"
22 #include "lutil.h"
23 #include "laccess.h"
24
25 #define NUM_CACHE               50      /* cache for unix user name */
26
27 extern int debug;
28
29 typedef struct uname_cache {
30         uid_t   uid;                    /* unix user id */
31         char    *name;                  /* user name */
32         struct  uname_cache *next;
33 } Uname_cache;
34
35 static  Uname_cache     *ucache_list = NULL;
36
37 /******************************************************************************
38  * forward declaration of static functions used within the file
39  ******************************************************************************/
40 static Uname_cache * in_u_cache(uid_t uid);
41 static char * get_uname(uid_t uid);
42 static Access_Entry_4 * in_access_list(Access_Entry_4 *l, char *s);
43 static Access_Entry_4 * combine_access_list(Access_Entry_4 *p_list, 
44                                 Access_Entry_4 *p_head, int type, int *p_world);
45 static CSA_return_code _GetV4AccessRights(_DtCmsCalendar *cal, char *target,
46                                 char *sender, uint *access);
47 static CSA_return_code _GetV5AccessRights(_DtCmsCalendar *cal, char *target,
48                                 char *sender, uint *access);
49
50 /*****************************************************************************
51  * extern functions used in the library
52  *****************************************************************************/
53
54 /*
55  * If the requester is the owner, "*sender" will be set to NULL.
56  */
57 extern CSA_return_code
58 _DtCmsV4LoadAndCheckAccess(
59         struct svc_req  *svcrq,
60         char            *target,
61         char            **sender,
62         uint            *access,
63         _DtCmsCalendar  **cal)
64 {
65         CSA_return_code stat;
66
67         if (target == NULL || sender == NULL || cal == NULL)
68                 return (CSA_E_INVALID_PARAMETER);
69
70         if ((stat = _DtCmsGetClientInfo(svcrq, sender)) != CSA_SUCCESS)
71                 return (stat);
72
73         if ((stat = _DtCmsGetCalendarByName(target, B_TRUE, cal))
74             != CSA_SUCCESS)
75                 return (stat);
76
77         if ((*cal)->fversion == _DtCMS_VERSION1)
78                 return (_GetV4AccessRights(*cal, target, *sender, access));
79         else
80                 return (_GetV5AccessRights(*cal, target, *sender, access));
81
82 }
83
84 /*
85  * If the requester is the owner, "*p_src" will be set to NULL.
86  */
87 extern CSA_return_code
88 _DtCmsV5LoadAndCheckAccess(
89         struct svc_req  *svcrq,
90         char            *target,
91         char            **sender,
92         uint            *access,
93         _DtCmsCalendar  **cal)
94 {
95         CSA_return_code         stat;
96         cms_access_entry        *alist;
97         cms_attribute_value     *owner;
98         int                     worldaccess = 0, useraccess = 0;
99         boolean_t               isowner;
100
101
102         if (target == NULL || sender == NULL || cal == NULL)
103                 return (CSA_E_INVALID_PARAMETER);
104
105         if ((stat = _DtCmsGetClientInfo(svcrq, sender)) != CSA_SUCCESS)
106                 return (stat);
107
108         if ((stat = _DtCmsGetCalendarByName(target, B_TRUE, cal))
109             != CSA_SUCCESS)
110                 return (stat);
111
112         if ((*cal)->fversion == _DtCMS_VERSION1)
113                 return (_GetV4AccessRights(*cal, target, *sender, access));
114         else
115                 return (_GetV5AccessRights(*cal, target, *sender, access));
116 }
117
118 extern CSA_return_code
119 _DtCmsGetClientInfo(struct svc_req *svcrq, char **source)
120 {
121         char *name;
122         char *uname;
123         struct authunix_parms *unix_cred;
124
125         if (source == NULL)
126         {
127                 return (CSA_E_INVALID_PARAMETER);
128         }
129
130         switch (svcrq->rq_cred.oa_flavor) {
131         case AUTH_UNIX:
132                 unix_cred = (struct authunix_parms *) svcrq->rq_clntcred;
133                 if (unix_cred == NULL)
134                         return (CSA_E_NO_AUTHORITY);
135                 if ((name = get_uname (unix_cred->aup_uid)) == NULL)
136                         return (CSA_E_INSUFFICIENT_MEMORY);
137                 if ((uname = malloc(strlen(name) +
138                     strlen(unix_cred->aup_machname) + 2)) == NULL)
139                         return (CSA_E_INSUFFICIENT_MEMORY);
140                 else {
141                         sprintf(uname, "%s@%s", name, unix_cred->aup_machname);
142                         *source = uname;
143                         return (CSA_SUCCESS);
144                 }
145
146         case AUTH_NULL:
147         default:
148                 svcerr_weakauth(svcrq->rq_xprt);
149                 return (CSA_E_NO_AUTHORITY);
150         }
151 }
152
153 /*
154  * good format of owner and user assumed:
155  * owner: user[@host[.domain]]
156  * user: user@host[.domain]
157  * target: name@host[.domain]
158  */
159 extern boolean_t
160 _DtCmsIsFileOwner(char *owner, char *user, char *target)
161 {
162         char *ptr1, *ptr2, *ptr3;
163
164         if (debug) {
165                 fprintf(stderr, "rpc.cmsd: %s, %s = %s, %s = %s, %s = %s\n",
166                         "check file owner",
167                         "owner", ((owner == NULL) ? "NULL" : owner),
168                         "user", ((user == NULL) ? "NULL" : user),
169                         "target", ((target == NULL) ? "NULL" : target));
170         }
171
172         if (owner == NULL || user == NULL || target == NULL)
173                 return (B_FALSE);
174
175         ptr1 = _DtCmGetPrefix(owner, '@');
176         ptr2 = _DtCmGetPrefix(user, '@');
177         if (strcmp(ptr1, ptr2)) {
178                 free(ptr1);
179                 free(ptr2);
180                 return(B_FALSE);
181         }
182         free(ptr1);
183         free(ptr2);
184
185         /* check domain if domain info is available */
186
187         ptr1 = strchr(user, '.');
188         ptr2 = strchr(target, '@');
189         if (ptr2)
190                 ptr3 = strchr(ptr2, '.');
191         else
192                 ptr3 = NULL;
193
194         if (ptr1 == NULL || ptr3 == NULL)
195                 /* assume that user is in the local domain */
196                 return B_TRUE;
197         else
198                 return(_DtCmIsSamePath(++ptr1, ++ptr3));
199 }
200
201 extern void
202 _DtCmsShowAccessList(Access_Entry_4 *l)
203 {
204         while (l!=NULL) {
205                 fprintf(stderr, "Access: %s(%c%c%c)\n", l->who,
206                         l->access_type & access_read_4   ? 'r' : '_',
207                         l->access_type & access_write_4  ? 'w' : '_',
208                         l->access_type & access_delete_4 ? 'd' : '_');
209                 l = l->next;
210         }
211 }
212
213 extern Access_Entry_4 *
214 _DtCmsCalendarAccessList(_DtCmsCalendar *cal)
215 {
216         int             world = access_none_4;
217         Access_Entry_4  *a;
218         Access_Entry_4  *l = NULL;
219
220         l = combine_access_list(GET_R_ACCESS(cal), l, access_read_4, &world);
221         l = combine_access_list(GET_W_ACCESS(cal), l, access_write_4, &world);
222         l = combine_access_list(GET_D_ACCESS(cal), l, access_delete_4, &world);
223         l = combine_access_list(GET_X_ACCESS(cal), l, access_exec_4, &world);
224
225         /* WORLD exists in one of the lists, add her to the combined list. */
226         if (world != access_none_4)
227         {
228                 a = _DtCm_make_access_entry4(WORLD, world);
229                 a->next = l;
230                 l = a;
231         }
232         return (l);
233 }
234
235 extern Privacy_Level_4
236 _DtCmCheckPrivacyLevel(char **p_src, Appt_4 *p_appt)
237 {
238         if (*p_src == NULL)
239                 return(public_4);
240
241         if (p_appt != NULL) {
242                 /*
243                  * if p_src is the author of the appointment,
244                  * it should see everything.
245                  */
246                 if (_DtCmIsSameUser(*p_src, p_appt->author)) {
247                         *p_src = NULL;
248                         return(public_4);
249                 } else
250                         return(p_appt->privacy);
251         } else
252                 return(private_4);
253 }
254
255 /*
256  * the user can view the entry if it has OWNER rights,
257  * the appropriate VIEW rights or he is the organizer of
258  * the entry and has ORGANIZER rights.
259  */
260 extern CSA_return_code
261 _DtCmsCheckViewAccess(char *user, uint access, cms_entry *eptr)
262 {
263         uint    need;
264         cms_attribute_value *oval, *sval;
265
266         need = (_DtCmsClassToViewAccess(eptr)) | CSA_OWNER_RIGHTS;
267         if (access & need) {
268                 return (CSA_SUCCESS);
269         } else {
270                 oval = eptr->attrs[CSA_ENTRY_ATTR_ORGANIZER_I].value;
271                 sval = eptr->attrs[CSA_ENTRY_ATTR_SPONSOR_I].value;
272                 if (((access & CSA_ORGANIZER_RIGHTS) &&
273                     _DtCmIsSameUser(user, oval->item.calendar_user_value)) ||
274                     ((access & CSA_SPONSOR_RIGHTS) && sval &&
275                     _DtCmIsSameUser(user, sval->item.calendar_user_value)))
276                         return (CSA_SUCCESS);
277                 else if ( (need & ~CSA_OWNER_RIGHTS) == (CSA_VIEW_CONFIDENTIAL_ENTRIES) ) {
278                         return (CSA_E_TIME_ONLY);
279                 } else
280                         return (CSA_E_NO_AUTHORITY);
281         }
282 }
283
284 extern CSA_return_code
285 _DtCmsCheckChangeAccess(char *user, uint access, cms_entry *eptr)
286 {
287         uint    need;
288         cms_attribute_value *oval, *sval;
289
290         need = (_DtCmsClassToChangeAccess(eptr)) | CSA_OWNER_RIGHTS;
291
292         if (access & need) {
293                 return (CSA_SUCCESS);
294         } else {
295                 oval = eptr->attrs[CSA_ENTRY_ATTR_ORGANIZER_I].value;
296                 sval = eptr->attrs[CSA_ENTRY_ATTR_SPONSOR_I].value;
297                 if (((access & CSA_ORGANIZER_RIGHTS) &&
298                     _DtCmIsSameUser(user, oval->item.calendar_user_value)) ||
299                     ((access & CSA_SPONSOR_RIGHTS) && sval &&
300                     _DtCmIsSameUser(user, sval->item.calendar_user_value)))
301                         return (CSA_SUCCESS);
302                 else
303                         return (CSA_E_NO_AUTHORITY);
304         }
305 }
306
307 extern uint
308 _DtCmsClassToViewAccess(cms_entry *entry)
309 {
310         cms_attribute_value *val;
311
312         val = entry->attrs[CSA_ENTRY_ATTR_CLASSIFICATION_I].value;
313
314         switch (val->item.uint32_value) {
315         case CSA_CLASS_PUBLIC:
316                 return (CSA_VIEW_PUBLIC_ENTRIES);
317         case CSA_CLASS_PRIVATE:
318                 return (CSA_VIEW_PRIVATE_ENTRIES);
319         case CSA_CLASS_CONFIDENTIAL:
320                 return (CSA_VIEW_CONFIDENTIAL_ENTRIES);
321         }
322 }
323
324 extern uint
325 _DtCmsClassToInsertAccess(cms_entry *entry)
326 {
327         cms_attribute_value *val;
328
329         val = entry->attrs[CSA_ENTRY_ATTR_CLASSIFICATION_I].value;
330
331         switch (val->item.uint32_value) {
332         case CSA_CLASS_PUBLIC:
333                 return (CSA_INSERT_PUBLIC_ENTRIES);
334         case CSA_CLASS_PRIVATE:
335                 return (CSA_INSERT_PRIVATE_ENTRIES);
336         case CSA_CLASS_CONFIDENTIAL:
337                 return (CSA_INSERT_CONFIDENTIAL_ENTRIES);
338         }
339 }
340
341 extern uint
342 _DtCmsClassToChangeAccess(cms_entry *entry)
343 {
344         cms_attribute_value *val;
345
346         val = entry->attrs[CSA_ENTRY_ATTR_CLASSIFICATION_I].value;
347
348         switch (val->item.uint32_value) {
349         case CSA_CLASS_PUBLIC:
350                 return (CSA_CHANGE_PUBLIC_ENTRIES);
351         case CSA_CLASS_PRIVATE:
352                 return (CSA_CHANGE_PRIVATE_ENTRIES);
353         case CSA_CLASS_CONFIDENTIAL:
354                 return (CSA_CHANGE_CONFIDENTIAL_ENTRIES);
355         }
356 }
357
358 /*****************************************************************************
359  * static functions used within the file
360  *****************************************************************************/
361
362 static Uname_cache *
363 in_u_cache(uid_t uid)
364 {
365         int     cache = NUM_CACHE;
366         Uname_cache *p_prev;
367         Uname_cache *p_cache;
368
369         p_prev = NULL;
370         p_cache = ucache_list;
371         while (p_cache != NULL)
372         {
373                 if (p_cache->uid == uid)
374                         return (p_cache);
375                 if (--cache < 0)
376                 {
377                         /* Assume that the cache size is at least 1 */
378                         p_prev->next = p_cache->next;
379                         free (p_cache->name);
380                         free (p_cache);
381                         p_cache = p_prev->next;
382                 }
383                 else
384                 {
385                         p_prev = p_cache;
386                         p_cache = p_cache->next;
387                 }
388         }
389         return (NULL);
390 }
391
392 static char *
393 get_uname(uid_t uid)
394 {
395         struct passwd *pw;
396         char buff[16];
397         Uname_cache *ucache, *prev;
398
399         if ((ucache = in_u_cache(uid)) == NULL)
400         {
401                 if ((pw = getpwuid (uid)) == NULL) {
402                         /* Can't map uid to name.  Don't cache the uid. */
403                         sprintf (buff, "%ld", (long)uid);
404                         return (strdup(buff));
405                 }
406
407                 if ((ucache = (Uname_cache *)malloc(sizeof(Uname_cache)))
408                     == NULL)
409                         return (NULL);
410
411                 if ((ucache->name = strdup(pw->pw_name)) == NULL) {
412                         free(ucache);
413                         return (NULL);
414                 }
415                 ucache->uid = uid;
416                 ucache->next = ucache_list;
417                 ucache_list = ucache;
418         }
419
420         return (strdup(ucache->name));
421 }
422
423 static Access_Entry_4 *
424 in_access_list(Access_Entry_4 *l, char *s)
425 {
426         char    *name;
427
428         if (l==NULL || s==NULL) return(NULL);
429         while(l != NULL) {
430                 /* only for combining lists, not for authentication */
431                 if (strcmp(l->who, s) == 0)
432                         break;
433                 l = l->next;
434         }
435         return(l);
436 }
437
438 static Access_Entry_4 *
439 combine_access_list(
440         Access_Entry_4 *p_list, 
441         Access_Entry_4 *p_head, 
442         int type, 
443         int *p_world)
444 {
445         Access_Entry_4  *a;
446         Access_Entry_4  *h = p_head;
447
448         while (p_list != NULL)
449         {
450                 /* Delay to put the WORLD into the combined list because 
451                  * in_access_list() may return wrong result.
452                  */
453                 if (strcmp (p_list->who, WORLD) == 0)
454                         *p_world |= type;
455                 else
456                 {
457                         /* The user is not in the combined list, add to list. */
458                         if ((a = in_access_list (h, p_list->who)) == NULL)
459                         {
460                                 a = _DtCm_make_access_entry4(p_list->who, type);
461                                 a->next = p_head;
462                                 p_head = a;
463                         }
464                         a->access_type |= type;
465                 }
466                 p_list = p_list->next;
467         }
468         return (p_head);
469 }
470
471 static CSA_return_code
472 _GetV4AccessRights(
473         _DtCmsCalendar  *cal,
474         char            *target,
475         char            *sender,
476         uint            *access)
477 {
478         int             worldaccess = 0, useraccess = 0;
479         Access_Entry_4  *alist;
480
481         /* first check to see if the user is the owner of the calendar */
482         if (_DtCmsIsFileOwner(cal->owner, sender, target)) {
483                 *access = CSA_OWNER_RIGHTS;
484                 return (CSA_SUCCESS);
485         }
486
487         for (alist = cal->alist; alist != NULL; alist = alist->next) {
488                 if (strcmp(alist->who, WORLD) == 0)
489                         worldaccess = alist->access_type;
490                 else if (_DtCmIsSameUser(sender, alist->who)) {
491                         useraccess = alist->access_type;
492                         break;
493                 }
494         }
495
496         *access = worldaccess | useraccess;
497         return (CSA_SUCCESS);
498 }
499
500 static CSA_return_code
501 _GetV5AccessRights(
502         _DtCmsCalendar  *cal,
503         char            *target,
504         char            *sender,
505         uint            *access)
506 {
507         cms_access_entry        *alist;
508         cms_attribute_value     *owner;
509         int                     worldaccess = 0, useraccess = 0;
510         boolean_t               isowner;
511
512         /* first check to see if the user is the owner of the calendar */
513         owner = cal->attrs[CSA_CAL_ATTR_CALENDAR_OWNER_I].value;
514         isowner = _DtCmsIsFileOwner(owner->item.calendar_user_value, sender,
515                         target);
516
517         if (isowner && cal->checkowner == B_FALSE) {
518                 *access = CSA_OWNER_RIGHTS;
519                 return (CSA_SUCCESS);
520         }
521
522         alist = cal->attrs[CSA_CAL_ATTR_ACCESS_LIST_I].value->\
523                         item.access_list_value;
524
525         if (alist == NULL) {
526                 *access = worldaccess | useraccess;
527                 return (CSA_E_NO_AUTHORITY);
528         }
529
530         for (; alist != NULL; alist = alist->next) {
531                 if (strcmp(alist->user, WORLD) == 0)
532                         worldaccess = alist->rights;
533                 else if (_DtCmIsSameUser(sender, alist->user)) {
534                         useraccess = alist->rights;
535                         break;
536                 }
537         }
538
539         *access = worldaccess | useraccess;
540         return (CSA_SUCCESS);
541 }
542