xref: /netbsd-src/bin/getfacl/getfacl.c (revision fb222b1585f5f5d76406fb1dd2b715d24575d745)
1*fb222b15Sjoerg /*	$NetBSD: getfacl.c,v 1.2 2020/05/22 01:28:00 joerg Exp $	*/
29aa2a9c3Schristos 
39aa2a9c3Schristos /*-
49aa2a9c3Schristos  * Copyright (c) 1999, 2001, 2002 Robert N M Watson
59aa2a9c3Schristos  * All rights reserved.
69aa2a9c3Schristos  *
79aa2a9c3Schristos  * This software was developed by Robert Watson for the TrustedBSD Project.
89aa2a9c3Schristos  *
99aa2a9c3Schristos  * Redistribution and use in source and binary forms, with or without
109aa2a9c3Schristos  * modification, are permitted provided that the following conditions
119aa2a9c3Schristos  * are met:
129aa2a9c3Schristos  * 1. Redistributions of source code must retain the above copyright
139aa2a9c3Schristos  *    notice, this list of conditions and the following disclaimer.
149aa2a9c3Schristos  * 2. Redistributions in binary form must reproduce the above copyright
159aa2a9c3Schristos  *    notice, this list of conditions and the following disclaimer in the
169aa2a9c3Schristos  *    documentation and/or other materials provided with the distribution.
179aa2a9c3Schristos  *
189aa2a9c3Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199aa2a9c3Schristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
209aa2a9c3Schristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
219aa2a9c3Schristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
229aa2a9c3Schristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239aa2a9c3Schristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249aa2a9c3Schristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259aa2a9c3Schristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
269aa2a9c3Schristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
279aa2a9c3Schristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
289aa2a9c3Schristos  * SUCH DAMAGE.
299aa2a9c3Schristos  */
309aa2a9c3Schristos /*
319aa2a9c3Schristos  * getfacl -- POSIX.1e utility to extract ACLs from files and directories
329aa2a9c3Schristos  * and send the results to stdout
339aa2a9c3Schristos  */
349aa2a9c3Schristos 
359aa2a9c3Schristos 
369aa2a9c3Schristos #include <sys/cdefs.h>
379aa2a9c3Schristos #if 0
389aa2a9c3Schristos __FBSDID("$FreeBSD: head/bin/getfacl/getfacl.c 340014 2018-11-01 17:45:29Z markj $");
399aa2a9c3Schristos #else
40*fb222b15Sjoerg __RCSID("$NetBSD: getfacl.c,v 1.2 2020/05/22 01:28:00 joerg Exp $");
419aa2a9c3Schristos #endif
429aa2a9c3Schristos 
439aa2a9c3Schristos #include <sys/types.h>
449aa2a9c3Schristos #include <sys/param.h>
459aa2a9c3Schristos #include <sys/acl.h>
469aa2a9c3Schristos #include <sys/stat.h>
479aa2a9c3Schristos 
489aa2a9c3Schristos #include <err.h>
499aa2a9c3Schristos #include <errno.h>
509aa2a9c3Schristos #include <grp.h>
519aa2a9c3Schristos #include <pwd.h>
529aa2a9c3Schristos #include <stdio.h>
539aa2a9c3Schristos #include <stdlib.h>
549aa2a9c3Schristos #include <string.h>
559aa2a9c3Schristos #include <unistd.h>
569aa2a9c3Schristos 
579aa2a9c3Schristos static int more_than_one = 0;
589aa2a9c3Schristos 
599aa2a9c3Schristos static __dead void
usage(void)609aa2a9c3Schristos usage(void)
619aa2a9c3Schristos {
629aa2a9c3Schristos 
639aa2a9c3Schristos 	fprintf(stderr, "Usage: %s [-dhnqv] [file ...]\n", getprogname());
64*fb222b15Sjoerg 	exit(1);
659aa2a9c3Schristos }
669aa2a9c3Schristos 
679aa2a9c3Schristos static char *
getuname(uid_t uid)689aa2a9c3Schristos getuname(uid_t uid)
699aa2a9c3Schristos {
709aa2a9c3Schristos 	struct passwd *pw;
719aa2a9c3Schristos 	static char uids[10];
729aa2a9c3Schristos 
739aa2a9c3Schristos 	if ((pw = getpwuid(uid)) == NULL) {
749aa2a9c3Schristos 		(void)snprintf(uids, sizeof(uids), "%u", uid);
759aa2a9c3Schristos 		return (uids);
769aa2a9c3Schristos 	} else
779aa2a9c3Schristos 		return (pw->pw_name);
789aa2a9c3Schristos }
799aa2a9c3Schristos 
809aa2a9c3Schristos static char *
getgname(gid_t gid)819aa2a9c3Schristos getgname(gid_t gid)
829aa2a9c3Schristos {
839aa2a9c3Schristos 	struct group *gr;
849aa2a9c3Schristos 	static char gids[10];
859aa2a9c3Schristos 
869aa2a9c3Schristos 	if ((gr = getgrgid(gid)) == NULL) {
879aa2a9c3Schristos 		(void)snprintf(gids, sizeof(gids), "%u", gid);
889aa2a9c3Schristos 		return (gids);
899aa2a9c3Schristos 	} else
909aa2a9c3Schristos 		return (gr->gr_name);
919aa2a9c3Schristos }
929aa2a9c3Schristos 
939aa2a9c3Schristos /*
949aa2a9c3Schristos  * return an ACL corresponding to the permissions
959aa2a9c3Schristos  * contained in struct stat
969aa2a9c3Schristos  */
979aa2a9c3Schristos static acl_t
acl_from_stat(const struct stat * sb)989aa2a9c3Schristos acl_from_stat(const struct stat *sb)
999aa2a9c3Schristos {
1009aa2a9c3Schristos 	acl_t acl;
1019aa2a9c3Schristos 	acl_entry_t entry;
1029aa2a9c3Schristos 	acl_permset_t perms;
1039aa2a9c3Schristos 
1049aa2a9c3Schristos 	/* create the ACL */
1059aa2a9c3Schristos 	acl = acl_init(3);
1069aa2a9c3Schristos 	if (!acl)
1079aa2a9c3Schristos 		return NULL;
1089aa2a9c3Schristos 
1099aa2a9c3Schristos 	/* First entry: ACL_USER_OBJ */
1109aa2a9c3Schristos 	if (acl_create_entry(&acl, &entry) == -1)
1119aa2a9c3Schristos 		return NULL;
1129aa2a9c3Schristos 	if (acl_set_tag_type(entry, ACL_USER_OBJ) == -1)
1139aa2a9c3Schristos 		return NULL;
1149aa2a9c3Schristos 
1159aa2a9c3Schristos 	if (acl_get_permset(entry, &perms) == -1)
1169aa2a9c3Schristos 		return NULL;
1179aa2a9c3Schristos 	if (acl_clear_perms(perms) == -1)
1189aa2a9c3Schristos 		return NULL;
1199aa2a9c3Schristos 
1209aa2a9c3Schristos 	/* calculate user mode */
1219aa2a9c3Schristos 	if (sb->st_mode & S_IRUSR)
1229aa2a9c3Schristos 		if (acl_add_perm(perms, ACL_READ) == -1)
1239aa2a9c3Schristos 			return NULL;
1249aa2a9c3Schristos 	if (sb->st_mode & S_IWUSR)
1259aa2a9c3Schristos 		if (acl_add_perm(perms, ACL_WRITE) == -1)
1269aa2a9c3Schristos 			return NULL;
1279aa2a9c3Schristos 	if (sb->st_mode & S_IXUSR)
1289aa2a9c3Schristos 		if (acl_add_perm(perms, ACL_EXECUTE) == -1)
1299aa2a9c3Schristos 			return NULL;
1309aa2a9c3Schristos 	if (acl_set_permset(entry, perms) == -1)
1319aa2a9c3Schristos 		return NULL;
1329aa2a9c3Schristos 
1339aa2a9c3Schristos 	/* Second entry: ACL_GROUP_OBJ */
1349aa2a9c3Schristos 	if (acl_create_entry(&acl, &entry) == -1)
1359aa2a9c3Schristos 		return NULL;
1369aa2a9c3Schristos 	if (acl_set_tag_type(entry, ACL_GROUP_OBJ) == -1)
1379aa2a9c3Schristos 		return NULL;
1389aa2a9c3Schristos 
1399aa2a9c3Schristos 	if (acl_get_permset(entry, &perms) == -1)
1409aa2a9c3Schristos 		return NULL;
1419aa2a9c3Schristos 	if (acl_clear_perms(perms) == -1)
1429aa2a9c3Schristos 		return NULL;
1439aa2a9c3Schristos 
1449aa2a9c3Schristos 	/* calculate group mode */
1459aa2a9c3Schristos 	if (sb->st_mode & S_IRGRP)
1469aa2a9c3Schristos 		if (acl_add_perm(perms, ACL_READ) == -1)
1479aa2a9c3Schristos 			return NULL;
1489aa2a9c3Schristos 	if (sb->st_mode & S_IWGRP)
1499aa2a9c3Schristos 		if (acl_add_perm(perms, ACL_WRITE) == -1)
1509aa2a9c3Schristos 			return NULL;
1519aa2a9c3Schristos 	if (sb->st_mode & S_IXGRP)
1529aa2a9c3Schristos 		if (acl_add_perm(perms, ACL_EXECUTE) == -1)
1539aa2a9c3Schristos 			return NULL;
1549aa2a9c3Schristos 	if (acl_set_permset(entry, perms) == -1)
1559aa2a9c3Schristos 		return NULL;
1569aa2a9c3Schristos 
1579aa2a9c3Schristos 	/* Third entry: ACL_OTHER */
1589aa2a9c3Schristos 	if (acl_create_entry(&acl, &entry) == -1)
1599aa2a9c3Schristos 		return NULL;
1609aa2a9c3Schristos 	if (acl_set_tag_type(entry, ACL_OTHER) == -1)
1619aa2a9c3Schristos 		return NULL;
1629aa2a9c3Schristos 
1639aa2a9c3Schristos 	if (acl_get_permset(entry, &perms) == -1)
1649aa2a9c3Schristos 		return NULL;
1659aa2a9c3Schristos 	if (acl_clear_perms(perms) == -1)
1669aa2a9c3Schristos 		return NULL;
1679aa2a9c3Schristos 
1689aa2a9c3Schristos 	/* calculate other mode */
1699aa2a9c3Schristos 	if (sb->st_mode & S_IROTH)
1709aa2a9c3Schristos 		if (acl_add_perm(perms, ACL_READ) == -1)
1719aa2a9c3Schristos 			return NULL;
1729aa2a9c3Schristos 	if (sb->st_mode & S_IWOTH)
1739aa2a9c3Schristos 		if (acl_add_perm(perms, ACL_WRITE) == -1)
1749aa2a9c3Schristos 			return NULL;
1759aa2a9c3Schristos 	if (sb->st_mode & S_IXOTH)
1769aa2a9c3Schristos 		if (acl_add_perm(perms, ACL_EXECUTE) == -1)
1779aa2a9c3Schristos 			return NULL;
1789aa2a9c3Schristos 	if (acl_set_permset(entry, perms) == -1)
1799aa2a9c3Schristos 		return NULL;
1809aa2a9c3Schristos 
1819aa2a9c3Schristos 	return(acl);
1829aa2a9c3Schristos }
1839aa2a9c3Schristos 
1849aa2a9c3Schristos static int
print_acl(char * path,acl_type_t type,int hflag,int iflag,int nflag,int qflag,int vflag)1859aa2a9c3Schristos print_acl(char *path, acl_type_t type, int hflag, int iflag, int nflag,
1869aa2a9c3Schristos     int qflag, int vflag)
1879aa2a9c3Schristos {
1889aa2a9c3Schristos 	struct stat	sb;
1899aa2a9c3Schristos 	acl_t	acl;
1909aa2a9c3Schristos 	char	*acl_text;
1919aa2a9c3Schristos 	int	error, flags = 0, ret;
1929aa2a9c3Schristos 
1939aa2a9c3Schristos 	if (hflag)
1949aa2a9c3Schristos 		error = lstat(path, &sb);
1959aa2a9c3Schristos 	else
1969aa2a9c3Schristos 		error = stat(path, &sb);
1979aa2a9c3Schristos 	if (error == -1) {
1989aa2a9c3Schristos 		warn("%s: stat() failed", path);
1999aa2a9c3Schristos 		return(-1);
2009aa2a9c3Schristos 	}
2019aa2a9c3Schristos 
2029aa2a9c3Schristos 	if (hflag)
2039aa2a9c3Schristos 		ret = lpathconf(path, _PC_ACL_NFS4);
2049aa2a9c3Schristos 	else
2059aa2a9c3Schristos 		ret = pathconf(path, _PC_ACL_NFS4);
2069aa2a9c3Schristos 	if (ret > 0) {
2079aa2a9c3Schristos 		if (type == ACL_TYPE_DEFAULT) {
2089aa2a9c3Schristos 			warnx("%s: there are no default entries in NFSv4 ACLs",
2099aa2a9c3Schristos 			    path);
2109aa2a9c3Schristos 			return (-1);
2119aa2a9c3Schristos 		}
2129aa2a9c3Schristos 		type = ACL_TYPE_NFS4;
2139aa2a9c3Schristos 	} else if (ret < 0 && errno != EINVAL) {
2149aa2a9c3Schristos 		warn("%s: pathconf(..., _PC_ACL_NFS4) failed", path);
2159aa2a9c3Schristos 		return (-1);
2169aa2a9c3Schristos 	}
2179aa2a9c3Schristos 
2189aa2a9c3Schristos 	if (more_than_one)
2199aa2a9c3Schristos 		printf("\n");
2209aa2a9c3Schristos 	else
2219aa2a9c3Schristos 		more_than_one++;
2229aa2a9c3Schristos 
2239aa2a9c3Schristos 	if (!qflag)
2249aa2a9c3Schristos 		printf("# file: %s\n# owner: %s\n# group: %s\n", path,
2259aa2a9c3Schristos 		    getuname(sb.st_uid), getgname(sb.st_gid));
2269aa2a9c3Schristos 
2279aa2a9c3Schristos 	if (hflag)
2289aa2a9c3Schristos 		acl = acl_get_link_np(path, type);
2299aa2a9c3Schristos 	else
2309aa2a9c3Schristos 		acl = acl_get_file(path, type);
2319aa2a9c3Schristos 	if (!acl) {
2329aa2a9c3Schristos 		if (errno != EOPNOTSUPP) {
2339aa2a9c3Schristos 			warn("%s", path);
2349aa2a9c3Schristos 			return(-1);
2359aa2a9c3Schristos 		}
2369aa2a9c3Schristos 		errno = 0;
2379aa2a9c3Schristos 		if (type == ACL_TYPE_DEFAULT)
2389aa2a9c3Schristos 			return(0);
2399aa2a9c3Schristos 		acl = acl_from_stat(&sb);
2409aa2a9c3Schristos 		if (!acl) {
2419aa2a9c3Schristos 			warn("%s: acl_from_stat() failed", path);
2429aa2a9c3Schristos 			return(-1);
2439aa2a9c3Schristos 		}
2449aa2a9c3Schristos 	}
2459aa2a9c3Schristos 
2469aa2a9c3Schristos 	if (iflag)
2479aa2a9c3Schristos 		flags |= ACL_TEXT_APPEND_ID;
2489aa2a9c3Schristos 
2499aa2a9c3Schristos 	if (nflag)
2509aa2a9c3Schristos 		flags |= ACL_TEXT_NUMERIC_IDS;
2519aa2a9c3Schristos 
2529aa2a9c3Schristos 	if (vflag)
2539aa2a9c3Schristos 		flags |= ACL_TEXT_VERBOSE;
2549aa2a9c3Schristos 
2559aa2a9c3Schristos 	acl_text = acl_to_text_np(acl, 0, flags);
2569aa2a9c3Schristos 	if (!acl_text) {
2579aa2a9c3Schristos 		warn("%s: acl_to_text_np() failed", path);
2589aa2a9c3Schristos 		return(-1);
2599aa2a9c3Schristos 	}
2609aa2a9c3Schristos 
2619aa2a9c3Schristos 	printf("%s", acl_text);
2629aa2a9c3Schristos 
2639aa2a9c3Schristos 	(void)acl_free(acl);
2649aa2a9c3Schristos 	(void)acl_free(acl_text);
2659aa2a9c3Schristos 
2669aa2a9c3Schristos 	return(0);
2679aa2a9c3Schristos }
2689aa2a9c3Schristos 
2699aa2a9c3Schristos static int
print_acl_from_stdin(acl_type_t type,int hflag,int iflag,int nflag,int qflag,int vflag)2709aa2a9c3Schristos print_acl_from_stdin(acl_type_t type, int hflag, int iflag, int nflag,
2719aa2a9c3Schristos     int qflag, int vflag)
2729aa2a9c3Schristos {
2739aa2a9c3Schristos 	char	*p, pathname[PATH_MAX];
2749aa2a9c3Schristos 	int	carried_error = 0;
2759aa2a9c3Schristos 
2769aa2a9c3Schristos 	while (fgets(pathname, (int)sizeof(pathname), stdin)) {
2779aa2a9c3Schristos 		if ((p = strchr(pathname, '\n')) != NULL)
2789aa2a9c3Schristos 			*p = '\0';
2799aa2a9c3Schristos 		if (print_acl(pathname, type, hflag, iflag, nflag,
2809aa2a9c3Schristos 		    qflag, vflag) == -1) {
2819aa2a9c3Schristos 			carried_error = -1;
2829aa2a9c3Schristos 		}
2839aa2a9c3Schristos 	}
2849aa2a9c3Schristos 
2859aa2a9c3Schristos 	return(carried_error);
2869aa2a9c3Schristos }
2879aa2a9c3Schristos 
2889aa2a9c3Schristos int
main(int argc,char * argv[])2899aa2a9c3Schristos main(int argc, char *argv[])
2909aa2a9c3Schristos {
2919aa2a9c3Schristos 	acl_type_t	type = ACL_TYPE_ACCESS;
2929aa2a9c3Schristos 	int	carried_error = 0;
2939aa2a9c3Schristos 	int	ch, error, i;
2949aa2a9c3Schristos 	int	hflag, iflag, qflag, nflag, vflag;
2959aa2a9c3Schristos 
2969aa2a9c3Schristos 	hflag = 0;
2979aa2a9c3Schristos 	iflag = 0;
2989aa2a9c3Schristos 	qflag = 0;
2999aa2a9c3Schristos 	nflag = 0;
3009aa2a9c3Schristos 	vflag = 0;
3019aa2a9c3Schristos 	while ((ch = getopt(argc, argv, "dhinqv")) != -1)
3029aa2a9c3Schristos 		switch(ch) {
3039aa2a9c3Schristos 		case 'd':
3049aa2a9c3Schristos 			type = ACL_TYPE_DEFAULT;
3059aa2a9c3Schristos 			break;
3069aa2a9c3Schristos 		case 'h':
3079aa2a9c3Schristos 			hflag = 1;
3089aa2a9c3Schristos 			break;
3099aa2a9c3Schristos 		case 'i':
3109aa2a9c3Schristos 			iflag = 1;
3119aa2a9c3Schristos 			break;
3129aa2a9c3Schristos 		case 'n':
3139aa2a9c3Schristos 			nflag = 1;
3149aa2a9c3Schristos 			break;
3159aa2a9c3Schristos 		case 'q':
3169aa2a9c3Schristos 			qflag = 1;
3179aa2a9c3Schristos 			break;
3189aa2a9c3Schristos 		case 'v':
3199aa2a9c3Schristos 			vflag = 1;
3209aa2a9c3Schristos 			break;
3219aa2a9c3Schristos 		default:
3229aa2a9c3Schristos 			usage();
3239aa2a9c3Schristos 		}
3249aa2a9c3Schristos 	argc -= optind;
3259aa2a9c3Schristos 	argv += optind;
3269aa2a9c3Schristos 
3279aa2a9c3Schristos 	if (argc == 0) {
3289aa2a9c3Schristos 		error = print_acl_from_stdin(type, hflag, iflag, nflag,
3299aa2a9c3Schristos 		    qflag, vflag);
3309aa2a9c3Schristos 		return(error ? 1 : 0);
3319aa2a9c3Schristos 	}
3329aa2a9c3Schristos 
3339aa2a9c3Schristos 	for (i = 0; i < argc; i++) {
3349aa2a9c3Schristos 		if (!strcmp(argv[i], "-")) {
3359aa2a9c3Schristos 			error = print_acl_from_stdin(type, hflag, iflag, nflag,
3369aa2a9c3Schristos 			    qflag, vflag);
3379aa2a9c3Schristos 			if (error == -1)
3389aa2a9c3Schristos 				carried_error = -1;
3399aa2a9c3Schristos 		} else {
3409aa2a9c3Schristos 			error = print_acl(argv[i], type, hflag, iflag, nflag,
3419aa2a9c3Schristos 			    qflag, vflag);
3429aa2a9c3Schristos 			if (error == -1)
3439aa2a9c3Schristos 				carried_error = -1;
3449aa2a9c3Schristos 		}
3459aa2a9c3Schristos 	}
3469aa2a9c3Schristos 
3479aa2a9c3Schristos 	return(carried_error ? 1 : 0);
3489aa2a9c3Schristos }
349