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