xref: /netbsd-src/external/gpl2/xcvs/dist/src/acl.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*
2  * Copyright (C) 2006 The Free Software Foundation, Inc.
3  *
4  * Portions Copyright (C) 2006, Baris Sahin <sbaris at users.sourceforge.net>
5  *                                          <http://cvsacl.sourceforge.net>
6  *
7  *
8  * You may distribute under the terms of the GNU General Public License as
9  * specified in the README file that comes with the CVS source distribution.
10  *
11  *
12  *
13  * CVS ACCESS CONTROL LIST EXTENSION
14  *
15  * It provides advanced access control definitions per modules,
16  * directories, and files on branch/tag for remote cvs repository
17  * connections.Execution of all CVS subcommands can be controlled
18  * with eight different permissions.
19  *
20  * Permission Types:
21  * - no permission      (n) (1)
22  * - all permissions    (a) (2)
23  * - write permission   (w) (3)
24  * - tag permission     (t) (4)
25  * - read permission    (r) (5)
26  * - add permission     (c) (6)
27  * - remove permission  (d) (7)
28  * - permission	change  (p) (8)
29  *
30  */
31 #include "cvs.h"
32 #include "getline.h"
33 #include <pwd.h>
34 #include <grp.h>
35 
36 static int acl_fileproc (void *callerdat, struct file_info *finfo);
37 
38 static Dtype acl_dirproc (void *callerdat, const char *dir, const char *repos,
39 			  const char *update_dir, List *entries);
40 
41 static int acllist_fileproc (void *callerdat, struct file_info *finfo);
42 static Dtype acllist_dirproc (void *callerdat, const char *dir,
43 			      const char *repos, const char *update_dir,
44 			      List *entries);
45 
46 static void acllist_print (char *line, const char *obj);
47 
48 static int racl_proc (int argc, char **argv, char *xwhere,
49 		      char *mwhere, char *mfile, int shorten,
50 		      int local_specified, char *mname, char *msg);
51 
52 static FILE *open_accessfile (char *xmode, const char *repos, char **fname);
53 static FILE *open_groupfile (char *xmode);
54 
55 static char *get_perms (const char *xperms);
56 static char *make_perms (char *xperms, char *xfounduserpart, char **xerrmsg);
57 
58 static char *findusername (const char *string1, const char *string2);
59 static char *findgroupname (const char *string1, const char *string2);
60 static int valid_tag (const char *part_tag, const char *tag);
61 static int valid_perm (const char *part_perms, int perm);
62 static int write_perms (const char *user, const char *perms,
63 			const char *founduserpart, int foundline,
64 			char *otheruserparts, const char *part_type,
65 			const char *part_object, const char *part_tag, int pos,
66 			const char *arepos);
67 
68 static char *cache_repository;
69 static int cache_retval;
70 static int founddeniedfile;
71 static int cache_perm;
72 
73 static int is_racl;
74 static int debug = 0;
75 
76 int use_cvs_acl = 0;
77 char *cvs_acl_default_permissions;
78 int use_cvs_groups = 0;
79 int use_system_groups = 0;
80 int use_separate_acl_file_for_each_dir = 0;
81 char *cvs_acl_file_location = NULL;
82 char *cvs_groups_file_location = NULL;
83 char *cvs_server_run_as = NULL;
84 int stop_at_first_permission_denied = 0;
85 
86 char *tag = NULL;
87 
88 char *muser;
89 char *mperms;
90 static int defaultperms;
91 
92 static char *default_perms_object;
93 char *default_part_perms_accessfile;
94 int aclconfig_default_used;
95 
96 int acldir = 0;
97 int aclfile = 0;
98 int listacl = 0;
99 
100 int userfound = 0;
101 int groupfound = 0;
102 
103 /* directory depth ... */
104 char *dirs[255];
105 
106 static const char *const acl_usage[] =
107         {
108                 "Usage: %s %s [user||group:permissions] [-Rl] [-r tag] [directories...] [files...]\n",
109                 "\t-R\tProcess directories recursively.\n",
110                 "\t-r rev\tExisting revision/tag.\n",
111                 "\t-l\tList defined ACLs.\n",
112                 "(Specify the --help global option for a list of other help options)\n",
113                 NULL
114         };
115 
116 static const char *const racl_usage[] =
117 {
118     "Usage: %s %s [user||group:permissions] [-Rl] [-r tag] [directories...]"
119     " [files...]\n",
120     "\t-R\tProcess directories recursively.\n",
121     "\t-r rev\tExisting revision/tag.\n",
122     "\t-l\tList defined ACLs.\n",
123     "(Specify the --help global option for a list of other help options)\n",
124     NULL
125 };
126 
127 
128 int
129 access_allowed (const char *file, const char *repos, const char *tag,
130 		int perm, char **mline, int *mpos, int usecache)
131 {
132     int retval = 0;
133     int foundline = 0;
134     FILE *accessfp;
135 
136     int flag = 1;
137 
138     char *iline;
139     char *tempv;
140     char *tempc;
141     size_t tempsize;
142 
143     int intcount;
144     int accessfilecount;
145     int signlevel = -1;
146     int dadmin = 0;
147 
148     const char *repository;
149     char *filefullname = NULL;
150     userfound = 0;
151     groupfound = 0;
152 
153     if (defaultperms)
154     {
155 	repository = xstrdup ("ALL");
156     }
157     else {
158 	if (strlen(repository = Short_Repository (repos)) == 0)
159 	{
160 	    repository = xstrdup (".");
161 	}
162     }
163 
164     /* cache */
165     if (usecache && cache_repository != NULL &&
166 	strcmp (cache_repository, repository) == 0 && !founddeniedfile
167 	&& perm == cache_perm)
168 	return (cache_retval);
169     else
170     {
171 	free (cache_repository);
172 	cache_repository = xstrdup (repository);
173 	cache_perm = perm;
174     }
175 
176     iline = xstrdup(repository);
177 
178     tempv = strtok(iline, "/\t");
179     tempc = xstrdup(tempv);
180     tempsize = ( tempc != NULL ) ? strlen(tempc) : 0;
181 
182     intcount = 0;
183     /* store paths from object to cvsroot */
184     dirs[intcount] = xstrdup(tempc);
185     while ((tempv = strtok(NULL, "/\t")) != NULL)
186     {
187 	intcount++;
188 
189 	xrealloc_and_strcat(&tempc, &tempsize, "/");
190 	xrealloc_and_strcat(&tempc, &tempsize, tempv);
191 
192 	dirs[intcount] = xstrdup(tempc);
193     }
194 
195     /* free not needed variables here */
196     free (tempv);
197     free (tempc);
198     free (iline);
199 
200     /* accessfilecount will used
201      * if UseSeparateACLFile keyword is set to yes*/
202     accessfilecount = intcount;
203 
204     /* if file is not null add it to dirs array */
205     if (file != NULL)
206     {
207 	filefullname = Xasprintf("%s/%s", repository, file);
208 	intcount++;
209 	dirs[intcount] = xstrdup(filefullname);
210     }
211 
212     for (; accessfilecount >= 0 && flag; accessfilecount--)
213     {
214 	if (!use_separate_acl_file_for_each_dir) {
215 	    flag = 0;
216 	    accessfp = open_accessfile ("r", repository, NULL);
217 	}
218 	else
219 	{
220 	    flag = 1;
221 	    accessfp = open_accessfile ("r", dirs[accessfilecount], NULL);
222 	}
223 
224 	if (accessfp != NULL)
225 	{
226 	    char *line = NULL;
227 	    size_t line_allocated = 0;
228 
229 	    char *xline;
230 	    char *part_type = NULL;
231 	    char *part_object = NULL;
232 	    char *part_tag = NULL;
233 	    char *part_perms = NULL;
234 
235 	    int x;
236 
237 	    while (getline (&line, &line_allocated, accessfp) >= 0)
238 	    {
239 
240 		if (line[0] == '#' || line[0] == '\0' || line[0] == '\n')
241 			continue;
242 
243 		xline = xstrdup (line);
244 		part_type = strtok (line, ":\t");
245 		part_object = strtok (NULL, ":\t");
246 		part_tag = strtok (NULL, ":\t");
247 		part_perms = strtok (NULL, ":\t");
248 
249 		if (part_type == NULL || part_object == NULL ||
250 		    part_tag == NULL || part_perms == NULL)
251 		{
252 		    free (line);
253 		    error(1, 0, "access file is corrupted or has invalid"
254 				" format");
255 		}
256 
257 		if (debug) fprintf (stderr, "type %s object %s tag %s perms"
258 				    "%s\n", part_type, part_object, part_tag,
259 				    part_perms);
260 		for (x = intcount; x >= signlevel && x != -1; x--)
261 		{
262 		    if (debug) fprintf (stderr, "dirs[%d] = %s, part_object="
263 					"%s\n", x, dirs[x], part_object);
264 		    if (strcmp (dirs[x], part_object) == 0)
265 		    {
266 			if (debug) fprintf(stderr, "tag %s \n", tag);
267 			if (valid_tag (part_tag, tag))
268 			{
269 			    foundline  = 1;
270 			    if (debug) fprintf(stderr, "foundline\n");
271 
272 			    if (listacl || ((acldir || aclfile) &&
273 					    x == intcount) &&
274 				strcmp(part_tag, tag) == 0)
275 			    {
276 				*mline = xstrdup (xline);
277 				*mpos = ftell (accessfp);
278 			    }
279 
280 			    if (debug) fprintf(stderr, "perm %d\n", perm);
281 			    if (valid_perm (part_perms, perm))
282 			    {
283 				if (debug) fprintf(stderr, "signlevel=%d "
284 				    " x=%d, aclconfig_default_used=%d\n",
285 				    signlevel, x, aclconfig_default_used);
286 				if (signlevel == x)
287 				{
288 				    if (strcmp(part_tag, "ALL") != 0 &&
289 					!aclconfig_default_used) {
290 					retval = 1;
291 					if (debug) fprintf(stderr,
292 					    "%s, %d: %d\n", __FILE__, __LINE__,
293 					    retval);
294 				    }
295 				}
296 				else if (!aclconfig_default_used)
297 				{
298 				    signlevel = x;
299 				    retval = 1;
300 				    if (debug) fprintf(stderr,
301 					"%s, %d: %d\n", __FILE__, __LINE__,
302 					retval);
303 				}
304 				else {
305 				    /* nothing... */
306 				}
307 			    }
308 			    else
309 			    {
310 				if (signlevel == x)
311 				{
312 				    if (strcmp(part_tag, "ALL") != 0 &&
313 					!aclconfig_default_used) {
314 					retval = 0;
315 					if (debug) fprintf(stderr,
316 					    "%s, %d: %d\n", __FILE__, __LINE__,
317 					    retval);
318 				    }
319 				}
320 				else if (!aclconfig_default_used)
321 				{
322 				    signlevel = x;
323 				    retval = 0;
324 				    if (debug) fprintf(stderr,
325 					"%s, %d: %d\n", __FILE__, __LINE__,
326 					retval);
327 
328 				    if (strncmp (part_type, "f", 1) == 0)
329 					founddeniedfile = 1;
330 				}
331 				else {
332 				}
333 			    }
334 			}
335 		    }
336 		}
337 
338 		if (debug) fprintf (stderr, "xline tag = %s %d %d\n", xline,
339 				    groupfound, userfound);
340 		if (strncmp (xline, "d:ALL:", 6) == 0 &&
341 		    ((!groupfound && !userfound) || listacl))
342 		{
343 		    if (debug) fprintf (stderr, "ALL tag = %s\n", tag);
344 		    /* a default found */
345 		    if (valid_tag (part_tag, tag) > 0)
346 		    {
347 			foundline = 1;
348 
349 			default_part_perms_accessfile = xstrdup (part_perms);
350 
351 			if (debug) fprintf (stderr, "valid perm = %d\n", perm);
352 			if (valid_perm (part_perms, perm))
353 			{
354 			    retval = 1;
355 			    if (debug) fprintf(stderr,
356 				"%s, %d: %d\n", __FILE__, __LINE__,
357 				retval);
358 			    if (perm == 8)
359 				dadmin = 1;
360 			}
361 			else {
362 			    retval = 0;
363 			    if (debug) fprintf(stderr,
364 				"%s, %d: %d\n", __FILE__, __LINE__,
365 				retval);
366 			}
367 		    }
368 		}
369 
370 	    }
371 
372 	    if (fclose (accessfp) == EOF)
373 		error (1, errno, "cannot close 'access' file");
374 	}
375     }
376 
377     if (!foundline)
378     {
379 	if (debug) fprintf(stderr, "not found line\n");
380 	/* DEFAULT */
381 	if (valid_perm (NULL, perm)) {
382 	    retval = 1;
383 	    if (debug) fprintf(stderr,
384 		"%s, %d: %d\n", __FILE__, __LINE__,
385 		retval);
386 	} else {
387 	    retval = 0;
388 	    if (debug) fprintf(stderr,
389 		"%s, %d: %d\n", __FILE__, __LINE__,
390 		retval);
391 	}
392     }
393 
394     /* acl admin rigths 'p' */
395     if (dadmin)
396     {
397 	retval = dadmin;
398     }
399 
400     cache_retval = retval;
401 
402     free (filefullname);
403     /* free directories array */
404     while (intcount >= 0)
405     {
406 	free (dirs[intcount]);
407 	intcount--;
408     }
409 
410     return retval;
411 }
412 
413 /* Returns 1 if tag is valid, 0 if not */
414 static int
415 valid_tag (const char *part_tag, const char *tag)
416 {
417     int retval;
418 
419     if (tag == NULL)
420 	tag = "HEAD";
421 
422     if (strcmp (tag, part_tag) == 0 || strcmp (part_tag, "ALL") == 0)
423 	retval = 1;
424     else
425 	retval = 0;
426 
427     return retval;
428 }
429 
430 /* Returns 1 if successful, 0 if not. */
431 static int
432 valid_perm (const char *part_perms, int perm)
433 {
434     char *perms;
435     int retval = 0;
436 
437     perms = get_perms (part_perms);
438 
439     /* Allow, if nothing found. */
440     if (perms[0] == '\0')
441 	return (1);
442 
443     /* no access allowed, exit */
444     if (strstr (perms, "n"))
445 	retval = 0;
446 
447     if (strstr (perms, "p"))
448 	/* admin rights */
449 	retval = 1;
450     else if (strstr (perms, "a") && perm != 8)
451 	/* all access allowed, exit */
452 	retval = 1;
453     else
454 	switch (perm)
455 	{
456 	case 3:/* write permission */
457 	    if (strstr (perms, "w"))
458 		retval = 1;
459 	    break;
460 	case 4:/* tag permission */
461 	    if (strstr (perms, "t"))
462 		retval = 1;
463 	    break;
464 	case 5:/* read permission */
465 	    if (strstr (perms, "w") || strstr (perms, "t") ||
466 		strstr (perms, "c") || strstr (perms, "d") ||
467 		strstr (perms, "r"))
468 		retval = 1;
469 	    break;
470 	case 6:/* create permission */
471 	    if (strstr (perms, "c"))
472 		retval = 1;
473 	    break;
474 	case 7:/* delete permission */
475 	    if (strstr (perms, "d"))
476 		retval = 1;
477 	    break;
478 	case 8:/* permission change */
479 	    if (strstr (perms, "p"))
480 		retval = 1;
481 	    break;
482 	default:/* never reached */
483 	    retval = 0;
484 	    break;
485 	}
486 
487     free (perms);
488 
489     return (retval);
490 }
491 
492 /* returns permissions found */
493 char *
494 get_perms (const char *part_perms)
495 {
496     char *username;
497     char *xperms;
498     size_t xperms_len = 1;
499 
500     FILE *groupfp;
501 
502     char *founduser = NULL;
503     char *foundall = NULL;
504     int default_checked = 0;
505 
506     if (debug) fprintf (stderr, "get_perms %s...", part_perms);
507     aclconfig_default_used = 0;
508 
509     xperms = xmalloc (xperms_len);
510     xperms[0] = '\0';
511 
512     /* use CVS_Username if set */
513     if (CVS_Username == NULL)
514 	username = getcaller ();
515     else
516 	username = CVS_Username;
517 
518     /* no defined acl, no default acl in access file,
519      * or no access file at all */
520     if (part_perms == NULL) {
521 	if (cvs_acl_default_permissions)
522 	{
523 	    aclconfig_default_used = 1;
524 	    if (debug) fprintf (stderr, "default %s\n",
525 			        cvs_acl_default_permissions);
526 	    return xstrdup(cvs_acl_default_permissions);
527 	}
528 	else {
529 	    if (debug) fprintf (stderr, "early %s\n", xperms);
530 	    return xperms;
531 	}
532     }
533 
534 check_default:
535     founduser = findusername (part_perms, username);
536     foundall = strstr (part_perms, "ALL!");
537 
538     if (debug) fprintf (stderr, "founduser=%s foundALL=%s\n",
539 		        founduser, foundall);
540     if (founduser)
541     {
542 	char *usr;
543 	char *per;
544 
545 	usr = strtok (founduser, "!\t");
546 	per = strtok (NULL, ",\t");
547 
548 	free(xperms);
549 	xperms = xstrdup (per);
550 	xperms_len = strlen (xperms);
551 
552 	userfound = 1;
553 	free (founduser);
554     }
555     else
556     {
557 	if (debug) fprintf (stderr, "usesystemgroups=%d\n", use_system_groups);
558 	if (use_system_groups) {
559 	    struct group *griter;
560 	    struct passwd *pwd;
561 	    gid_t gid = (pwd = getpwnam(username)) != NULL ? pwd->pw_gid : -1;
562 	    setgrent ();
563 	    while (griter = getgrent ())
564 	    {
565 		char *userchk;
566 		if (gid == griter->gr_gid) {
567 		    userchk = username;
568 		} else  {
569 		    char **users = griter->gr_mem;
570 		    int index = 0;
571 		    userchk = users [index++];
572 		    while(userchk != NULL) {
573 			if(strcmp (userchk, username) == 0)
574 			    break;
575 			userchk = users[index++];
576 		    }
577 		}
578 		if (userchk != NULL) {
579 		    char *grp;
580 		    if ((grp = findusername (part_perms, griter->gr_name)))
581 		    {
582 			char *gperm = strtok (grp, "!\t");
583 			if (debug) fprintf (stderr, "usercheck=%s, grp=%s\n",
584 					    userchk, grp);
585 			gperm = strtok (NULL, ",\t");
586 			xrealloc_and_strcat (&xperms, &xperms_len, gperm);
587 
588 			groupfound = 1;
589 			free (grp);
590 		    }
591 		}
592 	    }
593 	    endgrent ();
594 	}
595 	else if (use_cvs_groups) {
596 	    groupfp = open_groupfile ("r");
597 	    if (groupfp != NULL)
598 	    {
599 		char *line = NULL;
600 		char *grp;
601 		char *gperm;
602 		int read;
603 
604 		size_t line_allocated = 0;
605 
606 		while ((read = getline (&line, &line_allocated, groupfp)) >= 0)
607 		{
608 		    char *user;
609 		    if (line[0] == '#' || line[0] == '\0' || line[0] == '\n')
610 			continue;
611 
612 		    if (line[read - 1] == '\n')
613 			line[--read] = '\0';
614 
615 		    if ((grp = findgroupname (line, username)) &&
616 			(user = findusername (part_perms, grp)))
617 
618 		    {
619 			gperm = strtok (user, "!\t");
620 			gperm = strtok (NULL, ",\t");
621 			xrealloc_and_strcat (&xperms, &xperms_len, gperm);
622 			groupfound = 1;
623 			free (grp);
624 			free (user);
625 		    }
626 		}
627 
628 		free (line);
629 
630 		if (fclose (groupfp) == EOF)
631 		    error (1, errno, "cannot close 'group' file");
632 	    }
633 	}
634     }
635 
636     if (foundall)
637     {
638 	char *usr;
639 	char *per;
640 
641 	usr = strtok (strstr (part_perms, "ALL!"), "!\t");
642 	per = strtok (NULL, ",\t");
643 
644 	if (!default_checked)
645 	    default_perms_object = xstrdup (per);
646 
647 	if (xperms[0] == '\0')
648 	{
649 	    xperms = xstrdup (per);
650 	    xperms_len = strlen (xperms);
651 	}
652 
653 	/* You don't free pointers from strtok()! */
654 	//free(usr);
655 	//free(per);
656     }
657 
658     if (xperms[0] == '\0' && !default_checked && default_part_perms_accessfile)
659     {
660 	part_perms = xstrdup (default_part_perms_accessfile);
661 	default_checked = 1;
662 
663 	goto check_default;
664     }
665 
666     if (xperms[0] != '\0' && strcmp (xperms, "x") == 0)
667     {
668 	if (default_perms_object)
669 	    xperms = xstrdup (default_perms_object);
670 	else if (default_part_perms_accessfile)
671 	{
672 	    part_perms = default_part_perms_accessfile;
673 	    default_checked = 1;
674 	    goto check_default;
675 	}
676 	else if (cvs_acl_default_permissions)
677 	{
678 	    aclconfig_default_used = 1;
679 	    xperms = xstrdup (cvs_acl_default_permissions);
680 	}
681     }
682 
683     if (xperms[0] == '\0' && cvs_acl_default_permissions)
684     {
685 	aclconfig_default_used = 1;
686 	xperms = xstrdup (cvs_acl_default_permissions);
687     }
688 
689     if (debug) fprintf (stderr, "late %s\n", xperms);
690     return xperms;
691 }
692 
693 
694 int
695 cvsacl (int argc, char **argv)
696 {
697     char *chdirrepository;
698     int c;
699     int err = 0;
700     int usetag = 0;
701     int recursive = 0;
702 
703     int which;
704     char *where;
705 
706     is_racl = (strcmp (cvs_cmd_name, "racl") == 0);
707 
708     if (argc == -1)
709 	usage (is_racl ? racl_usage : acl_usage);
710 
711     /* parse the args */
712     optind = 0;
713 
714     while ((c = getopt (argc, argv, "dRr:l")) != -1)
715     {
716 	switch (c)
717 	{
718 	case 'd':
719 	    debug++;
720 	    break;
721 	case 'R':
722 	    recursive = 1;
723 	    break;
724 	case 'r': // baris
725 	    tag = xstrdup (optarg);
726 	    break;
727 	case 'l':
728 	    listacl = 1;
729 	    break;
730 	case '?':
731 	default:
732 	    usage (is_racl ? racl_usage : acl_usage);
733 	    break;
734 	}
735     }
736 
737     argc -= optind;
738     argv += optind;
739 
740     if (argc < (is_racl ? 1 : 1))
741 	usage (is_racl ? racl_usage : acl_usage);
742     if (listacl) {
743 	if (strstr (argv[0], ":"))
744 	    usage (is_racl ? racl_usage : acl_usage);
745     } else {
746 	if (!strstr (argv[0], ":"))
747 	    usage (is_racl ? racl_usage : acl_usage);
748     }
749 
750 
751 #ifdef CLIENT_SUPPORT
752 
753     if (current_parsed_root->isremote)
754     {
755 	start_server ();
756 	ign_setup ();
757 
758 	if(recursive)
759 	    send_arg ("-R");
760 
761 	if (listacl)
762 	    send_arg ("-l");
763 
764 	if(tag)
765 	{
766 	    option_with_arg ("-r", tag);
767 	}
768 
769 	send_arg ("--");
770 
771 	if (!listacl)
772 	{
773 	    send_arg (argv[0]);
774 
775 	    argc--;
776 	    argv++;
777 	}
778 
779 	if (is_racl)
780 	{
781 	    int i;
782 	    for (i = 0; i < argc; ++i)
783 		send_arg (argv[i]);
784 
785 	    send_to_server ("racl\012",0);
786 	}
787 	else
788 	{
789 	    send_files (argc, argv, recursive, 0, SEND_NO_CONTENTS);
790 	    send_file_names (argc, argv, SEND_EXPAND_WILD);
791 	    send_to_server ("acl\012", 0);
792 	}
793 
794 	return get_responses_and_close ();
795     }
796 #endif
797 
798 #ifdef SERVER_SUPPORT
799 
800     if (!listacl)
801     {
802 	muser = strtok (argv[0], ":\t");
803 	mperms = strtok (NULL, ":\t");
804 
805 	/* if set to 'default' */
806 	if ((strlen (mperms) == 7) && (strncmp (mperms, "default", 7) == 0))
807 	    mperms = xstrdup ("x");
808 
809 	/* Check that the given permissions are valid. */
810 	if (!given_perms_valid (mperms))
811 	    error (1,0,"Invalid permissions: `%s'", mperms);
812 
813 	argc--;
814 	argv++;
815     }
816 
817 
818     if (!tag)
819 	tag = xstrdup ("HEAD");
820 
821     if (!strcasecmp (argv[0], "ALL"))
822     {
823 	argv[0] = xstrdup (".");
824 	defaultperms = 1;
825 	if (!use_separate_acl_file_for_each_dir)
826 	{
827 	    recursive = 0;
828 	}
829 
830     }
831 
832     if (is_racl)
833     {
834 	DBM *db;
835 	int i;
836 	db = open_module ();
837 	for (i = 0; i < argc; i++)
838 	{
839 	    err += do_module (db, argv[i], MISC, "ACL ing: ",
840 			      racl_proc, NULL, 0, !recursive, 0,
841 			      0, NULL);
842 	}
843 	close_module (db);
844     }
845     else
846     {
847 	err = racl_proc (argc + 1, argv - 1, NULL, NULL, NULL, 0, !recursive,
848 			 NULL, NULL);
849     }
850 
851     return err;
852 
853 #endif
854 }
855 
856 static int
857 racl_proc (int argc, char **argv, char *xwhere, char *mwhere,
858 	   char *mfile, int shorten, int local, char *mname, char *msg)
859 {
860     char *myargv[2];
861     int err = 0;
862     int which;
863     char *repository;
864     char *where;
865     char *obj;
866     size_t objlen = 0;
867 
868     if (!use_cvs_acl)
869     {
870 	error(1, 0, "CVSACL extension is not enabled, set `UseCVSACL=yes'"
871 	      " in aclconfig file");
872     }
873 
874     if (is_racl)
875     {
876 	char *v;
877 	repository = Xasprintf ("%s/%s", current_parsed_root->directory,
878 				argv[0]);
879 	where = xstrdup (argv[0]);
880 
881 	/* if mfile isn't null, we need to set up to do only part of the
882 	 * module */
883 	if (mfile != NULL)
884 	{
885 	    char *cp;
886 	    char *path;
887 
888 	    /* if the portion of the module is a path, put the dir part on
889 	     * repos */
890 	    if ((cp = strrchr (mfile, '/')) != NULL)
891 	    {
892 		*cp = '\0';
893 		v = Xasprintf ("%s/%s", repository, mfile);
894 		free (repository);
895 		repository = v;
896 		v = Xasprintf ("%s/%s", where, mfile);
897 		free(where);
898 		where = v;
899 		mfile = cp + 1;
900 	    }
901 
902 	    /* take care of the rest */
903 	    path = Xasprintf ("%s/%s", repository, mfile);
904 	    if (isdir (path))
905 	    {
906 		/* directory means repository gets the dir tacked on */
907 		free(repository);
908 		repository = path;
909 		v = Xasprintf ("%s/%s", where, mfile);
910 		free(where);
911 		where = v;
912 	    }
913 	    else
914 	    {
915 		free (path);
916 		myargv[0] = argv[0];
917 		myargv[1] = mfile;
918 		argc = 2;
919 		argv = myargv;
920 	    }
921 	}
922 
923 	/* cd to the starting repository */
924 	if ( CVS_CHDIR (repository) < 0)
925 	{
926 	    error (0, errno, "cannot chdir to %s", repository);
927 	    free (repository);
928 	    free (where);
929 	    return 1;
930 	}
931 
932 	/* End section which is identical to patch_proc.  */
933 
934 	which = W_REPOS | W_ATTIC;
935 
936 	if (argc > 1)
937 	{
938 		obj = Xasprintf ("%s/%s", repository, argv[1]);
939 	}
940 	else
941 	{
942 		obj = xstrdup(repository);
943 	}
944     }
945     else
946     {
947 	where = NULL;
948 	repository = NULL;
949 	which = W_LOCAL | W_REPOS | W_ATTIC;
950 
951 	obj = xstrdup (argv[1]);
952     }
953 
954     if (isdir (obj))
955 	acldir = 1;
956     else if (isfile (obj))
957 	aclfile = 1;
958     else
959 	error(1, 0, "no such file or directory");
960 
961     free (obj);
962 
963     if (listacl)
964 	err = start_recursion (acllist_fileproc, NULL, acllist_dirproc, NULL,
965 			       NULL, argc - 1, argv + 1, local, which, 0, 0,
966 			       where, 1, repository);
967     else
968 	err = start_recursion (acl_fileproc, NULL, acl_dirproc, NULL, NULL,
969 			       argc - 1, argv + 1, local, which, 0, 0,
970 			       where, 1, repository);
971 
972     if (repository != NULL)
973 	free (repository);
974     if (where != NULL)
975 	free (where);
976 
977     return err;
978 }
979 
980 
981 static int
982 acl_fileproc (void *callerdat, struct file_info *finfo)
983 {
984     char *filefullname;
985     char *founduserpart = NULL;
986     char *otheruserparts = NULL;
987     size_t otherslen = 0;
988 
989     const char *frepository;
990     int foundline = 0;
991 
992     char *line = NULL;
993     size_t line_allocated = 0;
994     int linelen;
995 
996     char *wperms;
997     char *errmsg;
998 
999     int pos;
1000 
1001     if (!aclfile)
1002 	return 0;
1003 
1004     frepository = Short_Repository (finfo->repository);
1005 
1006     filefullname = Xasprintf("%s/%s", frepository, finfo->file);
1007 
1008 
1009     if (!access_allowed (finfo->file, finfo->repository, tag, 8, &line, &pos,
1010 			 0))
1011 	error (1, 0, "You do not have acl admin rights on '%s'", frepository);
1012 
1013     if (line != NULL)
1014     {
1015 	char *part_type = NULL;
1016 	char *part_object = NULL;
1017 	char *part_tag = NULL;
1018 	char *part_perms = NULL;
1019 	char *userpart;
1020 
1021 	part_type = strtok (line, ":\t");
1022 	part_object = strtok (NULL, ":\t");
1023 	part_tag = strtok (NULL, ":\t");
1024 	part_perms = strtok (NULL, ":\t");
1025 
1026 	foundline = 1;
1027 	userpart = strtok (part_perms, ",\t");
1028 
1029 	do
1030 	{
1031 	    if (strncmp (userpart, muser, strlen (muser)) == 0)
1032 		founduserpart = xstrdup (userpart);
1033 	    else
1034 	    {
1035 		if (otheruserparts != NULL)
1036 		{
1037 		    xrealloc_and_strcat (&otheruserparts, &otherslen, ",");
1038 		    xrealloc_and_strcat (&otheruserparts, &otherslen, userpart);
1039 		}
1040 		else
1041 		{
1042 		    otheruserparts = xstrdup (userpart);
1043 		    otherslen = strlen (otheruserparts);
1044 		}
1045 	    }
1046 	} while ((userpart = strtok (NULL, ",\t")) != NULL);
1047 
1048 	free (userpart);
1049     }
1050 
1051     wperms = make_perms (mperms, founduserpart, &errmsg);
1052     if (wperms == NULL)
1053     {
1054 	if (errmsg)
1055 	    error (0, 0, "`%s' %s", filefullname, errmsg);
1056     }
1057     else
1058     {
1059 	cvs_output ("X ", 0);
1060 	cvs_output (filefullname, 0);
1061 	cvs_output ("\n", 0);
1062 
1063 	write_perms (muser, wperms, founduserpart, foundline,
1064 		     otheruserparts, "f", filefullname, tag, pos,
1065 		     Short_Repository(finfo->repository));
1066     }
1067 
1068     free (line);
1069     free (founduserpart);
1070     free (otheruserparts);
1071     free (wperms);
1072     free (filefullname);
1073 
1074     return 0;
1075 }
1076 
1077 static Dtype
1078 acl_dirproc (void *callerdat, const char *dir, const char *repos,
1079 	     const char *update_dir, List *entries)
1080 {
1081     const char *drepository;
1082     char *founduserpart = NULL;
1083     char *otheruserparts = NULL;
1084     size_t otherslen = 0;
1085     int foundline = 0;
1086 
1087     char *line = NULL;
1088     size_t line_allocated = 0;
1089     int linelen;
1090 
1091     int pos;
1092 
1093     char *wperms;
1094     char *errmsg;
1095 
1096     if (!acldir)
1097 	return 0;
1098 
1099     if (repos[0] == '\0')
1100 	repos = Name_Repository (dir, NULL);
1101 
1102     if (!access_allowed (NULL, repos, tag, 8, &line, &pos, 0))
1103 	error (1, 0, "You do not have admin rights on '%s'",
1104 	       Short_Repository (repos));
1105 
1106     drepository = Short_Repository (repos);
1107 
1108     if (line != NULL)
1109     {
1110 	char *part_type = NULL;
1111 	char *part_object = NULL;
1112 	char *part_tag = NULL;
1113 	char *part_perms = NULL;
1114 	char *userpart;
1115 
1116 	part_type = strtok (line, ":\t");
1117 	part_object = strtok (NULL, ":\t");
1118 	part_tag = strtok (NULL, ":\t");
1119 	part_perms = strtok (NULL, ":\t");
1120 
1121 	foundline = 1;
1122 	userpart = strtok (part_perms, ",\t");
1123 
1124 	do
1125 	{
1126 	    if (strncmp (userpart, muser, strlen (muser)) == 0)
1127 		founduserpart = xstrdup (userpart);
1128 	    else
1129 	    {
1130 		if (otheruserparts != NULL)
1131 		{
1132 		    xrealloc_and_strcat (&otheruserparts, &otherslen, ",");
1133 		    xrealloc_and_strcat (&otheruserparts, &otherslen, userpart);
1134 		}
1135 		else
1136 		{
1137 		    otheruserparts = xstrdup (userpart);
1138 		    otherslen = strlen (otheruserparts);
1139 		}
1140 	    }
1141 	} while ((userpart = strtok (NULL, ",\t")) != NULL);
1142     }
1143 
1144     wperms = make_perms (mperms, founduserpart, &errmsg);
1145     if (wperms == NULL)
1146     {
1147 	if (errmsg)
1148 	    error (0, 0, "`%s' %s", drepository, errmsg);
1149     }
1150     else
1151     {
1152 	if (defaultperms)
1153 	{
1154 	    cvs_output ("X ", 0);
1155 	    cvs_output ("ALL", 0);
1156 	    cvs_output ("\n", 0);
1157 	    write_perms (muser, wperms, founduserpart, foundline,
1158 			 otheruserparts, "d", "ALL", tag, pos, drepository);
1159 
1160 	}
1161 	else
1162 	{
1163 	    cvs_output ("X ", 0);
1164 	    cvs_output (drepository, 0);
1165 	    cvs_output ("\n", 0);
1166 	    write_perms (muser, wperms, founduserpart, foundline,
1167 			 otheruserparts, "d", drepository, tag, pos,
1168 			 drepository);
1169 	}
1170     }
1171 
1172     free (line);
1173     free (founduserpart);
1174     free (otheruserparts);
1175     free (wperms);
1176 
1177     return 0;
1178 }
1179 
1180 /* Open CVSROOT/access or defined CVSACLFileLocation file
1181  * Open access file In each directory if UseSeparateACLFileForEachDir=yes
1182  * returns file pointer to access file or NULL if access file not found */
1183 FILE *
1184 open_accessfile (char *fmode, const char *adir, char **fname)
1185 {
1186     char *accessfile = NULL;
1187     FILE *accessfp;
1188 
1189     if (!use_separate_acl_file_for_each_dir)
1190     {
1191 	if (cvs_acl_file_location == NULL)
1192 	{
1193 	    accessfile = Xasprintf("%s/%s/%s", current_parsed_root->directory,
1194 				   CVSROOTADM, CVSROOTADM_ACCESS);
1195 	}
1196 	else
1197 	{
1198 	    accessfile = xstrdup(cvs_acl_file_location);
1199 	}
1200     }
1201     else
1202     {
1203 	size_t accessfilelen = 0;
1204 	xrealloc_and_strcat (&accessfile, &accessfilelen,
1205 			     current_parsed_root->directory);
1206 	xrealloc_and_strcat (&accessfile, &accessfilelen, "/");
1207 	xrealloc_and_strcat (&accessfile, &accessfilelen, adir);
1208 	xrealloc_and_strcat (&accessfile, &accessfilelen, "/access");
1209     }
1210 
1211     accessfp = CVS_FOPEN (accessfile, fmode);
1212 
1213     if (fname != NULL)
1214 	*fname = xstrdup (accessfile);
1215 
1216     free (accessfile);
1217 
1218     return accessfp;
1219 }
1220 
1221 /* Open /etc/group file if UseSystemGroups=yes in config file
1222  * Open CVSROOT/group file if UseCVSGroups=yes in config file
1223  * Open group file if specified in CVSGroupsFileLocation
1224  * returns group file pointer if UseSystemGroups=yes
1225  * returns NULL if UseSystemGroups=no or group file not found */
1226 FILE *
1227 open_groupfile (char *fmode)
1228 {
1229     char *groupfile;
1230     FILE *groupfp;
1231 
1232     if (use_cvs_groups)
1233     {
1234 	if (cvs_groups_file_location != NULL)
1235 	{
1236 	    groupfile = xstrdup (cvs_groups_file_location);
1237 	}
1238 	else
1239 	{
1240 	    groupfile = Xasprintf("%s/%s/%s", current_parsed_root->directory,
1241 				  CVSROOTADM, CVSROOTADM_GROUP);
1242 	}
1243     }
1244     else
1245     {
1246 	    return NULL;
1247     }
1248 
1249     groupfp = CVS_FOPEN (groupfile, "r");
1250 
1251     if (groupfp == NULL)
1252 	error (0, 0, "cannot open file: %s", groupfile);
1253 
1254     free (groupfile);
1255 
1256     return groupfp;
1257 }
1258 
1259 
1260 /* Check whether given permissions are valid or not
1261  * Returns 1 if permissions are valid
1262  * Returns 0 if permissions are NOT valid */
1263 int
1264 given_perms_valid (const char *cperms)
1265 {
1266     int cperms_len;
1267     int retval;
1268     int index, i;
1269 
1270     if (cperms[0] == '+' || cperms[0] == '-')
1271 	index = 1;
1272     else
1273 	index = 0;
1274 
1275     cperms_len = strlen (cperms);
1276 
1277     switch (cperms[index])
1278     {
1279     case 'x':
1280 	if ((cperms_len - index) == 1 && cperms_len == 1)
1281 	    retval = 1;
1282 	else
1283 	    retval = 0;
1284 	break;
1285     case 'n':
1286 	if ((cperms_len - index) == 1 && cperms_len == 1)
1287 	    retval = 1;
1288 	else
1289 	    retval = 0;
1290 	break;
1291     case 'p':
1292 	if ((cperms_len - index) == 1)
1293 	    retval = 1;
1294 	else
1295 	    retval = 0;
1296 	break;
1297     case 'a':
1298 	if ((cperms_len - index) == 1)
1299 	    retval = 1;
1300 	else
1301 	    retval = 0;
1302 	break;
1303     case 'r':
1304 	if ((cperms_len - index) == 1)
1305 	    retval = 1;
1306 	else
1307 	    retval = 0;
1308 	break;
1309     case 'w':
1310 	if ((cperms_len - index) == 1)
1311 		retval = 1;
1312 	else
1313 	    for (i = index + 1; i < cperms_len; i++)
1314 		if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'd')
1315 		    retval = 1;
1316 		else
1317 		    retval = 0;
1318 	break;
1319     case 't':
1320 	if ((cperms_len - index) == 1)
1321 	    retval = 1;
1322 	else
1323 	    for (i = index + 1; i < cperms_len; i++)
1324 		if (cperms[i] == 'w' || cperms[i] == 'c' || cperms[i] == 'd')
1325 		    retval = 1;
1326 		else
1327 		    retval = 0;
1328 	break;
1329     case 'c':
1330 	if ((cperms_len - index) == 1)
1331 	    retval = 1;
1332 	else
1333 	    for (i = index + 1; i < cperms_len; i++)
1334 		if (cperms[i] == 't' || cperms[i] == 'w' || cperms[i] == 'd')
1335 		    retval = 1;
1336 		else
1337 		    retval = 0;
1338 	break;
1339     case 'd':
1340 	if ((cperms_len - index) == 1)
1341 	    retval = 1;
1342 	else
1343 	    for (i = index + 1; i < cperms_len; i++)
1344 		if (cperms[i] == 't' || cperms[i] == 'c' || cperms[i] == 'w')
1345 		    retval = 1;
1346 		else
1347 		    retval = 0;
1348 	break;
1349     default:
1350 	retval = 0;
1351 	break;
1352     }
1353 
1354     return retval;
1355 }
1356 
1357 /* prepare permsissions string to be written to access file
1358  * returns permissions or NULL if */
1359 char *
1360 make_perms (char *perms, char *founduserpart, char **xerrmsg)
1361 {
1362     char *fperms = NULL;
1363     size_t perms_len;
1364     size_t fperms_len;
1365 
1366     int i, j;
1367     int err = 0;
1368     char *errmsg = NULL;
1369 
1370     char *retperms;
1371     size_t retperms_len;
1372 
1373     perms_len = strlen (perms);
1374     if (perms[0] == '+' || perms[0] == '-')
1375     {
1376 	retperms = xmalloc (retperms_len);
1377 	retperms[0] = '\0';
1378 	retperms_len = 1;
1379 
1380 	if (founduserpart)
1381 	{
1382 	    char *tempfperms;
1383 	    size_t tempfperms_len;
1384 
1385 	    char *temp;
1386 	    int per = 0;
1387 	    temp = strtok (founduserpart, "!\t");
1388 	    fperms = strtok (NULL, "!\t");
1389 	    fperms_len = strlen (fperms);
1390 
1391 	    if (strncmp (fperms, "x", 1) == 0)
1392 	    {
1393 		err = 1;
1394 		if (perms[0] == '+')
1395 		    *xerrmsg = xstrdup ("cannot add default permission 'x'");
1396 		else
1397 		    *xerrmsg = xstrdup ("cannot remove default permission 'x'");
1398 	    }
1399 
1400 	    /* go through perms */
1401 	    for (i = 1; i < perms_len && !err; i++)
1402 	    {
1403 		switch (perms[i])
1404 		{
1405 		case 'n':
1406 		    err = 1;
1407 		    break;
1408 		case 'p':
1409 		    if (perms[0] == '+')
1410 			fperms = xstrdup ("p");
1411 		    else if (perms[0] == '-')
1412 		    {
1413 			fperms_len = 1;
1414 			fperms = xmalloc (fperms_len);
1415 			fperms[0] = '\0';
1416 		    }
1417 		    break;
1418 		case 'a':
1419 		    for (j = 0; j < fperms_len; j++)
1420 		    {
1421 			if (fperms[j] == 'p')
1422 			{
1423 			    err = 1;
1424 			    *xerrmsg = xstrdup ("user have admin rights,"
1425 						" cannot use +/- permissions");
1426 			}
1427 			else if (fperms[j] == 'a' && perms[0] == '+')
1428 			{
1429 			    err = 1;
1430 			    *xerrmsg = xstrdup ("user already has all ('a')"
1431 						" permission");
1432 			}
1433 			else if (fperms[j] != 'a' && perms[0] == '-')
1434 			{
1435 			    err = 1;
1436 			    *xerrmsg = xstrdup ("user does not have all "
1437 						"('a') permission");
1438 			}
1439 		    }
1440 		    if (perms[0] == '+')
1441 		    {
1442 			fperms = xstrdup ("a");
1443 			fperms_len = strlen (fperms);
1444 		    }
1445 		    else if (perms[0] == '-')
1446 		    {
1447 			fperms_len = 1;
1448 			fperms = xmalloc (fperms_len);
1449 			fperms[0] = '\0';
1450 		    }
1451 		    break;
1452 		case 'r':
1453 		    for (i = 0; i < fperms_len; i++)
1454 		    {
1455 			if (fperms[i] == 'n' && perms[0] == '+')
1456 			{
1457 			    fperms = xstrdup ("r");
1458 			    fperms_len = strlen (fperms);
1459 			}
1460 			else if (fperms[i] == 'r' && perms[0] == '-')
1461 			{
1462 			    fperms_len = 1;
1463 			    fperms = xmalloc (fperms_len);
1464 			    fperms[0] = '\0';
1465 			}
1466 			else if (perms[0] == '-')
1467 			{
1468 			    err = 1;
1469 			    *xerrmsg = xstrdup ("user has other permissions,"
1470 						" cannot remove read ('r')"
1471 						" permission");
1472 			}
1473 			else
1474 			{
1475 			    err = 1;
1476 			    *xerrmsg = xstrdup ("user has other permissions,"
1477 						" cannot remove read ('r')"
1478 						" permission");
1479 			}
1480 		    }
1481 		    break;
1482 		case 'w':
1483 		    {
1484 			tempfperms_len = 1;
1485 
1486 			tempfperms = xmalloc (tempfperms_len);
1487 			tempfperms[0] = '\0';
1488 
1489 			for (j = 0; j < fperms_len; j++)
1490 			{
1491 			    if (fperms[j] == 't' || fperms[j] == 'c' ||
1492 				fperms[j] == 'd')
1493 			    {
1494 				char *temp;
1495 				temp = xmalloc (2);
1496 				temp[0] = fperms[j];
1497 				temp[1] = '\0';
1498 
1499 				xrealloc_and_strcat (&tempfperms,
1500 						     &tempfperms_len, temp);
1501 				free (temp);
1502 			    }
1503 			    else if (fperms[j] == 'a' || fperms[j] == 'p')
1504 			    {
1505 				err = 1;
1506 				*xerrmsg = xstrdup ("user has higher"
1507 						    " permissions, cannot use"
1508 						    " +/- write permissions");
1509 			    }
1510 			    else if (fperms[j] == 'n' || fperms[j] == 'r')
1511 			    {
1512 				if (perms[0] == '-')
1513 				{
1514 				    err = 1;
1515 				    *xerrmsg = xstrdup ("user does not have"
1516 							" write ('w')"
1517 							" permission");
1518 				}
1519 			    }
1520 			    else if (fperms[j] == 'w')
1521 			    {
1522 				per = 1;
1523 				if (perms[0] == '+') {
1524 				    err = 1;
1525 				    *xerrmsg = xstrdup ("user already have"
1526 							" write ('w')"
1527 							"permission");
1528 				}
1529 			    }
1530 			}
1531 
1532 			fperms = tempfperms;
1533 			fperms_len = strlen (fperms);
1534 
1535 			if (!per && !err && (perms[0] == '-')) {
1536 			    err = 1;
1537 			    *xerrmsg = xstrdup ("user does not have write"
1538 						" ('w') permission");
1539 			}
1540 
1541 			if (perms[0] == '+')
1542 			{
1543 			    xrealloc_and_strcat (&fperms, &fperms_len, "w");
1544 			}
1545 		    }
1546 		    break;
1547 		case 't':
1548 		    {
1549 			tempfperms_len = 1;
1550 
1551 			tempfperms = xmalloc (tempfperms_len);
1552 			tempfperms[0] = '\0';
1553 
1554 			for (j = 0; j < fperms_len; j++)
1555 			{
1556 			    if (fperms[j] == 'w' || fperms[j] == 'c' ||
1557 				fperms[j] == 'd')
1558 			    {
1559 				char *temp;
1560 				temp = xmalloc (2);
1561 				temp[0] = fperms[j];
1562 				temp[1] = '\0';
1563 
1564 				xrealloc_and_strcat (&tempfperms,
1565 						     &tempfperms_len, temp);
1566 				free (temp);
1567 			    }
1568 			    else if (fperms[j] == 'a' || fperms[j] == 'p')
1569 			    {
1570 				err = 1;
1571 				*xerrmsg = xstrdup ("user has higher"
1572 						    " permissions, cannot use"
1573 						    " +/- tag permissions");
1574 			    }
1575 			    else if (fperms[j] == 'n' || fperms[i] == 'r')
1576 			    {
1577 				if (perms[0] == '-')
1578 				    *xerrmsg = xstrdup ("user does not have tag"
1579 							" ('t') permission");
1580 			    }
1581 			    else if (fperms[j] == 't')
1582 			    {
1583 				per = 1;
1584 				if (perms[0] == '+')
1585 				{
1586 				    err = 1;
1587 				    *xerrmsg = xstrdup ("user already have tag"
1588 							" ('t') permission");
1589 				}
1590 			    }
1591 			}
1592 
1593 			fperms = tempfperms;
1594 			fperms_len = strlen (fperms);
1595 
1596 			if (!per && !err && (perms[0] == '-'))
1597 			{
1598 			    err = 1;
1599 			    *xerrmsg = xstrdup ("user does not have tag ('t')"
1600 						" permission");
1601 			}
1602 
1603 			if (perms[0] == '+')
1604 			{
1605 			    xrealloc_and_strcat (&fperms, &fperms_len, "t");
1606 			}
1607 		    }
1608 		    break;
1609 		case 'c':
1610 		    {
1611 			tempfperms_len = 1;
1612 
1613 			tempfperms = xmalloc (tempfperms_len);
1614 			tempfperms[0] = '\0';
1615 
1616 			for (j = 0; j < fperms_len; j++)
1617 			{
1618 			    if (fperms[j] == 'w' || fperms[j] == 't' ||
1619 				fperms[j] == 'd')
1620 			    {
1621 				char *temp;
1622 				temp = xmalloc (2);
1623 				temp[0] = fperms[j];
1624 				temp[1] = '\0';
1625 
1626 				xrealloc_and_strcat (&tempfperms,
1627 						     &tempfperms_len, temp);
1628 				free (temp);
1629 			    }
1630 			    else if (fperms[j] == 'a' || fperms[j] == 'p')
1631 			    {
1632 				err = 1;
1633 				*xerrmsg = xstrdup ("user has higher"
1634 						    " permissions, cannot use"
1635 						    " +/- create permissions");
1636 			    }
1637 			    else if (fperms[j] == 'n' || fperms[i] == 'r')
1638 			    {
1639 				if (perms[0] == '-')
1640 				    err = 1;
1641 				*xerrmsg = xstrdup ("user does not have create"
1642 						    " ('c') permission");
1643 			    }
1644 			    else if (fperms[j] == 'c')
1645 			    {
1646 				per = 1;
1647 				if (perms[0] == '+') {
1648 				    err = 1;
1649 				    *xerrmsg = xstrdup ("user already have"
1650 							" create ('c')"
1651 							" permission");
1652 				}
1653 			    }
1654 			}
1655 
1656 			fperms = tempfperms;
1657 			fperms_len = strlen (fperms);
1658 
1659 			if (!per && !err && (perms[0] == '-')) {
1660 			    err = 1;
1661 			    *xerrmsg = xstrdup ("user does not have create"
1662 						" ('c') permission");
1663 			}
1664 
1665 			if (perms[0] == '+')
1666 			{
1667 			    xrealloc_and_strcat (&fperms, &fperms_len, "c");
1668 			}
1669 		    }
1670 		    break;
1671 		case 'd':
1672 		    {
1673 			tempfperms_len = 1;
1674 
1675 			tempfperms = xmalloc (tempfperms_len);
1676 			tempfperms[0] = '\0';
1677 
1678 			for (j = 0; j < fperms_len; j++)
1679 			{
1680 			    if (fperms[j] == 'w' || fperms[j] == 'c' ||
1681 				fperms[j] == 't')
1682 			    {
1683 				char *temp;
1684 				temp = xmalloc (2);
1685 				temp[0] = fperms[j];
1686 				temp[1] = '\0';
1687 
1688 				xrealloc_and_strcat (&tempfperms,
1689 						     &tempfperms_len, temp);
1690 				free (temp);
1691 			    }
1692 			    else if (fperms[j] == 'a' || fperms[j] == 'p')
1693 			    {
1694 				err = 1;
1695 				*xerrmsg = xstrdup ("user has higher"
1696 						    " permissions, cannot use"
1697 						    " +/- delete permissions");
1698 			    }
1699 			    else if (fperms[j] == 'n' || fperms[i] == 'r')
1700 			    {
1701 				if (perms[0] == '-')
1702 				    err = 1;
1703 				*xerrmsg = xstrdup ("user does not have delete"
1704 						    " ('d') permission");
1705 			    }
1706 			    else if (fperms[j] == 'd')
1707 			    {
1708 				per = 1;
1709 				if (perms[0] == '+') {
1710 				    err = 1;
1711 				    *xerrmsg = xstrdup ("user already have"
1712 							" delete ('d')"
1713 							" permission");
1714 				}
1715 			    }
1716 			}
1717 
1718 			fperms = tempfperms;
1719 			fperms_len = strlen (fperms);
1720 
1721 			if (!per && !err && (perms[0] == '-')) {
1722 				err = 1;
1723 				*xerrmsg = xstrdup ("user does not have delete"
1724 						    " ('d') permission");
1725 			}
1726 
1727 			if (perms[0] == '+')
1728 			{
1729 				xrealloc_and_strcat (&fperms, &fperms_len, "d");
1730 			}
1731 		    }
1732 		    break;
1733 		default:
1734 		    err  = 1;
1735 		    *xerrmsg = xstrdup ("error in 'access' file format");
1736 		    break;
1737 		}
1738 
1739 		if (fperms[0] == '\0')
1740 		    retperms = xstrdup ("none");
1741 		else
1742 		    retperms = xstrdup (fperms);
1743 	    }
1744 	}
1745 	else
1746 	{
1747 	    err = 1;
1748 	    *xerrmsg = xstrdup("user is not given any permissions to remove/add");
1749 	}
1750     }
1751     else
1752     {
1753 	retperms = xstrdup (perms);
1754     }
1755     if (fperms)
1756 	free (fperms);
1757     if (err && retperms)
1758 	free (retperms);
1759 
1760     return (err ? NULL : retperms);
1761 }
1762 
1763 /* prepare and write resulting permissions to access file */
1764 static int
1765 write_perms (const char *user, const char *perms, const char *founduserpart,
1766 	     int foundline, char *otheruserparts,
1767 	     const char *part_type, const char *part_object,
1768 	     const char *part_tag, int pos, const char *arepos)
1769 {
1770     char *accessfile;
1771     char *tmpaccessout;
1772     FILE *accessfpin;
1773     FILE *accessfpout;
1774 
1775     char *newline = NULL;
1776     size_t newlinelen = 1;
1777     char *object;
1778 
1779     char *line = NULL;
1780     size_t line_allocated = 0;
1781 
1782     newline = xmalloc (newlinelen);
1783     newline[0] = '\0';
1784 
1785     if (!strcasecmp (part_tag, "ALL"))
1786 	part_tag = "ALL";
1787 
1788     /* strip any trailing slash if found */
1789     object = xstrdup (part_object);
1790     if (object[strlen (object) - 1] == '/')
1791 	object[strlen (object) - 1] = '\0';
1792 
1793     /* first parts, part type, object, and tag */
1794     xrealloc_and_strcat (&newline, &newlinelen, part_type);
1795     xrealloc_and_strcat (&newline, &newlinelen, ":");
1796     xrealloc_and_strcat (&newline, &newlinelen, object);
1797     xrealloc_and_strcat (&newline, &newlinelen, ":");
1798     xrealloc_and_strcat (&newline, &newlinelen, part_tag);
1799     xrealloc_and_strcat (&newline, &newlinelen, ":");
1800 
1801     if (strncmp (perms, "none", 4) != 0)
1802     {
1803 	xrealloc_and_strcat (&newline, &newlinelen, user);
1804 	xrealloc_and_strcat (&newline, &newlinelen, "!");
1805 	xrealloc_and_strcat (&newline, &newlinelen, perms);
1806 	if (otheruserparts != NULL)
1807 	    xrealloc_and_strcat (&newline, &newlinelen, ",");
1808     }
1809 
1810     if (otheruserparts != NULL)
1811     {
1812 	if (otheruserparts[strlen (otheruserparts) - 1] == '\n')
1813 	    otheruserparts[strlen (otheruserparts) - 1] = '\0';
1814 
1815 	xrealloc_and_strcat (&newline, &newlinelen, otheruserparts);
1816     }
1817 
1818     xrealloc_and_strcat (&newline, &newlinelen, ":");
1819 
1820     if (foundline)
1821     {
1822 	accessfpout = cvs_temp_file (&tmpaccessout);
1823 	if (accessfpout == NULL)
1824 	    error (1, errno, "cannot open temporary file: %s", tmpaccessout);
1825 
1826 	accessfpin = open_accessfile ("r", arepos, &accessfile);
1827 	if (accessfpout == NULL)
1828 	    error (1, errno, "cannot open access file: %s", accessfile);
1829 
1830 	while (getline (&line, &line_allocated, accessfpin) >= 0)
1831 	{
1832 	    if (pos != ftell (accessfpin))
1833 	    {
1834 		if (fprintf (accessfpout, "%s", line) < 0)
1835 		    error (1, errno, "writing temporary file: %s", tmpaccessout);
1836 	    }
1837 	    else
1838 	    {
1839 		if (fprintf (accessfpout, "%s\n", newline) < 0)
1840 		    error (1, errno, "writing temporary file: %s", tmpaccessout);
1841 	    }
1842 
1843 	}
1844 	if (fclose (accessfpin) == EOF)
1845 		error (1, errno, "cannot close access file: %s", accessfile);
1846 
1847 	if (fclose (accessfpout) == EOF)
1848 	    error (1, errno, "cannot close temporary file: %s", tmpaccessout);
1849 
1850 	if (CVS_UNLINK (accessfile) < 0)
1851 	    error (0, errno, "cannot remove %s", accessfile);
1852 
1853 	copy_file (tmpaccessout, accessfile);
1854 
1855 	if (CVS_UNLINK (tmpaccessout) < 0)
1856 	    error (0, errno, "cannot remove temporary file: %s", tmpaccessout);
1857     }
1858     else
1859     {
1860 	accessfpout = open_accessfile ("r+", arepos, &accessfile);
1861 
1862 	if (accessfpout == NULL)
1863 	{
1864 	    if (existence_error (errno))
1865 	    {
1866 		accessfpout = open_accessfile ("w+", arepos, &accessfile);
1867 	    }
1868 	    if (accessfpout == NULL)
1869 		error (1, errno, "cannot open access file: %s", accessfile);
1870 	}
1871 	else {
1872 	    if (fseek (accessfpout, 0, 2) != 0)
1873 		error (1, errno, "cannot fseek access file: %s", accessfile);
1874 	}
1875 
1876 	if (fprintf (accessfpout, "%s\n", newline) < 0)
1877 	    error (1, errno, "writing access file: %s", accessfile);
1878 
1879 	if (fclose (accessfpout) == EOF)
1880 	    error (1, errno, "cannot close access file: %s", accessfile);
1881     }
1882 
1883     free (line);
1884     free (newline);
1885 
1886     chmod (accessfile, 0644);
1887 
1888     return 0;
1889 }
1890 
1891 static int
1892 acllist_fileproc (void *callerdat, struct file_info *finfo)
1893 {
1894     char *filefullname;
1895     const char *frepository;
1896     char *line = NULL;
1897     int pos;
1898 
1899     if (!aclfile)
1900 	return 0;
1901 
1902     frepository = Short_Repository (finfo->repository);
1903 
1904     filefullname = Xasprintf("%s/%s", frepository, finfo->file);
1905 
1906     /* check that user, which run acl/racl command, has admin permisson,
1907      * and also return the line with permissions from access file.  */
1908     if (!access_allowed (finfo->file, finfo->repository, tag, 5, &line, &pos,
1909 			 0))
1910 	error (1, 0, "You do not have admin rights on '%s'", frepository);
1911 
1912     acllist_print (line, filefullname);
1913 
1914     free (filefullname);
1915 
1916     return 0;
1917 }
1918 
1919 static Dtype
1920 acllist_dirproc (void *callerdat, const char *dir, const char *repos,
1921 		 const char *update_dir, List *entries)
1922 {
1923     char *line = NULL;
1924     const char *drepository;
1925     int pos;
1926 
1927     if (repos[0] == '\0')
1928 	repos = Name_Repository (dir, NULL);
1929 
1930     if (!acldir)
1931 	return 0;
1932 
1933     drepository = Short_Repository (repos);
1934 
1935     /* check that user, which run acl/racl command, has admin permisson,
1936      * and also return the line with permissions from access file.  */
1937     if (!access_allowed (NULL, repos, tag, 5, &line, &pos, 0))
1938 	error (1, 0, "You do not have admin rights on '%s'", drepository);
1939 
1940     acllist_print (line, drepository);
1941 
1942     return 0;
1943 }
1944 
1945 /* Prints permissions to screen with -l option */
1946 void
1947 acllist_print (char *line, const char *obj)
1948 {
1949     char *temp;
1950     int c = 0;
1951     int def = 0;
1952 
1953     char *printedusers[255];
1954     printedusers[0] = NULL;
1955 
1956     if (line != NULL)
1957     {
1958 	temp = strtok (line, ":\t");
1959 
1960 	if (acldir)
1961 	    cvs_output ("d ", 0);
1962 	else if (aclfile)
1963 	    cvs_output ("f ", 0);
1964 
1965 	temp = strtok (NULL, ":\t");
1966 
1967 	cvs_output(obj, 0);
1968 	cvs_output (" | ", 0);
1969 
1970 	temp = strtok (NULL, ":\t");
1971 	cvs_output (temp, 0);
1972 	cvs_output (" | ", 0);
1973 
1974 	while ((temp = strtok (NULL, "!\t")) != NULL)
1975 	{
1976 	    if (strncmp (temp, ":", 1) == 0)
1977 		break;
1978 
1979 	    if (strcmp (temp, "ALL") == 0)
1980 	    {
1981 		temp = strtok (NULL, ",\t");
1982 		continue;
1983 	    }
1984 
1985 	    cvs_output (temp, 0);
1986 	    cvs_output (":", 0);
1987 
1988 	    while (printedusers[c] != NULL)
1989 		c++;
1990 
1991 	    printedusers[c] = xstrdup (temp);
1992 	    c++;
1993 	    printedusers[c] = NULL;
1994 
1995 	    temp = strtok (NULL, ",\t");
1996 
1997 	    if (temp != NULL && temp[strlen (temp) - 2] == ':')
1998 		temp[strlen (temp) - 2] = '\0';
1999 
2000 	    cvs_output (temp, 0);
2001 	    cvs_output (" ", 0);
2002 	}
2003 
2004 	if (default_perms_object)
2005 	{
2006 	    cvs_output ("| defaults ", 0);
2007 	    cvs_output ("ALL:", 0);
2008 	    cvs_output (default_perms_object, 0);
2009 	    def = 1;
2010 	}
2011 	if (default_part_perms_accessfile)
2012 	{
2013 	    size_t i;
2014 	    i = strlen (default_part_perms_accessfile);
2015 	    xrealloc_and_strcat (&default_part_perms_accessfile, &i, ",");
2016 
2017 	    free (line);
2018 	    line = xstrdup (default_part_perms_accessfile);
2019 
2020 	    if (!def)
2021 		cvs_output ("| defaults ", 0);
2022 	    else
2023 		cvs_output (" ", 0);
2024 
2025 	    temp = strtok (line, "!\t");
2026 	    cvs_output (temp, 0);
2027 	    cvs_output (":", 0);
2028 
2029 	    temp = strtok (NULL, ",\t");
2030 
2031 	    cvs_output (temp, 0);
2032 	    cvs_output (" ", 0);
2033 
2034 	    while ((temp = strtok (NULL, "!\t")) != NULL)
2035 	    {
2036 		int printed = 0;
2037 		int c2 = 0;
2038 		while (printedusers[c2] != NULL && printed == 0)
2039 		{
2040 		    if (strcmp (printedusers[c2], temp) == 0)
2041 		    {
2042 			printed = 1;
2043 			break;
2044 		    }
2045 		    c2++;
2046 		}
2047 
2048 		if (printed == 0)
2049 		{
2050 		    cvs_output (temp, 0);
2051 		    cvs_output (":", 0);
2052 		}
2053 
2054 		temp = strtok (NULL, ",\t");
2055 
2056 		if (temp[strlen (temp) - 2] == ':')
2057 		    temp[strlen (temp) - 2] = '\0';
2058 
2059 		if (printed == 0)
2060 		{
2061 		    cvs_output (temp, 0);
2062 		    cvs_output (" ", 0);
2063 		}
2064 	    }
2065 	    def = 1;
2066 	}
2067 	else if (cvs_acl_default_permissions)
2068 	{
2069 	    cvs_output ("| defaults ", 0);
2070 	    cvs_output ("ALL: ", 0);
2071 	    cvs_output (cvs_acl_default_permissions, 0);
2072 	}
2073     }
2074     else
2075     {
2076 	if (acldir)
2077 	    cvs_output ("d ", 0);
2078 	else if (aclfile)
2079 	    cvs_output ("f ", 0);
2080 	cvs_output (obj, 0);
2081 	cvs_output (" | ", 0);
2082 	cvs_output (tag, 0);
2083 	cvs_output (" | ", 0);
2084 
2085 	if (default_perms_object)
2086 	{
2087 	    cvs_output ("| defaults ", 0);
2088 	    cvs_output ("ALL:", 0);
2089 	    cvs_output (default_perms_object, 0);
2090 	    def = 1;
2091 	}
2092 	if (default_part_perms_accessfile)
2093 	{
2094 	    free (line);
2095 	    line = xstrdup (default_part_perms_accessfile);
2096 
2097 	    if (!def)
2098 		cvs_output ("| defaults ", 0);
2099 	    else
2100 		cvs_output (" ", 0);
2101 
2102 	    temp = strtok (line, "!\t");
2103 	    cvs_output (temp, 0);
2104 	    cvs_output (":", 0);
2105 
2106 	    temp = strtok (NULL, ",\t");
2107 
2108 	    if (temp[strlen (temp) - 2] == ':')
2109 		temp[strlen (temp) - 2] = '\0';
2110 
2111 	    cvs_output (temp, 0);
2112 	    cvs_output (" ", 0);
2113 
2114 	    while ((temp = strtok (NULL, "!\t")) != NULL)
2115 	    {
2116 		cvs_output (temp, 0);
2117 		cvs_output (":", 0);
2118 
2119 		if ((temp = strtok (NULL, ",\t")) != NULL)
2120 		{
2121 		    if (temp[strlen (temp) - 2] == ':')
2122 			temp[strlen (temp) - 2] = '\0';
2123 
2124 		    cvs_output (temp, 0);
2125 		    cvs_output (" ", 0);
2126 		}
2127 	    }
2128 	    cvs_output ("\n", 0);
2129 	}
2130 	else if (cvs_acl_default_permissions)
2131 	{
2132 	    cvs_output ("| defaults ", 0);
2133 	    cvs_output ("ALL: ", 0);
2134 	    cvs_output (cvs_acl_default_permissions, 0);
2135 	}
2136 	else
2137 	    cvs_output ("default:p (no perms)", 0);
2138     }
2139     cvs_output ("\n", 0);
2140 
2141     while (c >= 0)  {
2142 	free (printedusers[c]);
2143 	c--;
2144     }
2145 
2146     free (line);
2147 }
2148 
2149 /* find username
2150  * returns username with its permissions if user found
2151  * returns NULL if user not found */
2152 char *findusername (const char *string1, const char *string2)
2153 {
2154     char *tmp1, *tmp2;
2155 
2156     if (string1 != NULL && string2 != NULL)
2157     {
2158 	tmp1 = xstrdup (string1);
2159 	tmp2 = strtok (tmp1, ",\t");
2160 
2161 	do
2162 	{
2163 	    if (strncmp (tmp2, string2, strlen (string2)) == 0 &&
2164 				     tmp2[strlen (string2)] == '!')
2165 	    {
2166 		tmp2 = xstrdup (tmp2);
2167 		free (tmp1);
2168 		return tmp2;
2169 	    }
2170 	    tmp2 = strtok (NULL, ",\t");
2171 	}
2172 	while (tmp2 != NULL);
2173 
2174 	free (tmp1);
2175 
2176 	return NULL;
2177     }
2178     else
2179 	return NULL;
2180 }
2181 
2182 /* find user name in group file
2183  * returns group name if user found
2184  * returns NULL if user not found */
2185 char *findgroupname (const char *string1, const char *string2)
2186 {
2187     char *tmp1, *tmp2;
2188     char *grpname;
2189 
2190     if (string1 != NULL && string2 != NULL)
2191     {
2192 	tmp1 = xstrdup (string1);
2193 	grpname = strtok (tmp1, ":\t");
2194 
2195 	while (tmp2 = strtok(NULL, ",\t"))
2196 	{
2197 	    if (strcmp (tmp2, string2) == 0)
2198 	    {
2199 		grpname = xstrdup (grpname);
2200 		free (tmp1);
2201 		return grpname;
2202 	    }
2203 	}
2204 
2205 	free (tmp1);
2206 
2207 	return NULL;
2208     }
2209     else
2210 	return NULL;
2211 }
2212