1510 lines · 42.4 KB · C
RawDownload
/* * Handle passing Access Control Lists between systems. * * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2006-2022 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */ #include "rsync.h"#include "lib/sysacls.h"#include "lib/acl.h"#ifdef HAVE_LIBACL_AT#include <fcntl.h>	/* AT_EMPTY_PATH / AT_SYMLINK_NOFOLLOW */#endif #ifdef SUPPORT_ACLS extern int dry_run;extern int am_root;extern int read_only;extern int list_only;extern mode_t orig_umask;extern int numeric_ids;extern int inc_recurse;extern int preserve_devices;extern int preserve_specials; /* Flags used to indicate what items are being transmitted for an entry. */#define XMIT_USER_OBJ (1<<0)#define XMIT_GROUP_OBJ (1<<1)#define XMIT_MASK_OBJ (1<<2)#define XMIT_OTHER_OBJ (1<<3)#define XMIT_NAME_LIST (1<<4) #define NO_ENTRY ((uchar)0x80) /* Default value of a NON-name-list entry. */ #define NAME_IS_USER (1u<<31) /* Bit used only on a name-list entry. */ /* When we send the access bits over the wire, we shift them 2 bits to the * left and use the lower 2 bits as flags (relevant only to a name entry). * This makes the protocol more efficient than sending a value that would * be likely to have its highest bits set. */#define XFLAG_NAME_FOLLOWS 0x0001u#define XFLAG_NAME_IS_USER 0x0002u /* === ACL structures === */ typedef struct {	id_t id;	uint32 access;} id_access; typedef struct {	id_access *idas;	int count;} ida_entries; typedef struct {	char *name;	uchar len;} idname; typedef struct rsync_acl {	ida_entries names;	/* These will be NO_ENTRY if there's no such entry. */	uchar user_obj;	uchar group_obj;	uchar mask_obj;	uchar other_obj;} rsync_acl; typedef struct {	rsync_acl racl;	SMB_ACL_T sacl;} acl_duo; static const rsync_acl empty_rsync_acl = {	{NULL, 0}, NO_ENTRY, NO_ENTRY, NO_ENTRY, NO_ENTRY}; static item_list access_acl_list = EMPTY_ITEM_LIST;static item_list default_acl_list = EMPTY_ITEM_LIST; static size_t prior_access_count = (size_t)-1;static size_t prior_default_count = (size_t)-1; /* === Calculations on ACL types === */ static const char *str_acl_type(SMB_ACL_TYPE_T type){	switch (type) {	case SMB_ACL_TYPE_ACCESS:#ifdef HAVE_OSX_ACLS		return "ACL_TYPE_EXTENDED";#else		return "ACL_TYPE_ACCESS";#endif	case SMB_ACL_TYPE_DEFAULT:		return "ACL_TYPE_DEFAULT";	default:		break;	}	return "unknown ACL type!";} static int calc_sacl_entries(const rsync_acl *racl){	/* A System ACL always gets user/group/other permission entries. */	return racl->names.count#ifdef ACLS_NEED_MASK	     + 1#else	     + (racl->mask_obj != NO_ENTRY)#endif	     + 3;} /* Extracts and returns the permission bits from the ACL.  This cannot be * called on an rsync_acl that has NO_ENTRY in any spot but the mask. */static int rsync_acl_get_perms(const rsync_acl *racl){	return (racl->user_obj << 6)	     + ((racl->mask_obj != NO_ENTRY ? racl->mask_obj : racl->group_obj) << 3)	     + racl->other_obj;} /* Removes the permission-bit entries from the ACL because these * can be reconstructed from the file's mode. */static void rsync_acl_strip_perms(stat_x *sxp){	rsync_acl *racl = sxp->acc_acl; 	racl->user_obj = NO_ENTRY;	if (racl->mask_obj == NO_ENTRY)		racl->group_obj = NO_ENTRY;	else {		int group_perms = (sxp->st.st_mode >> 3) & 7;		if (racl->group_obj == group_perms)			racl->group_obj = NO_ENTRY;#ifndef HAVE_SOLARIS_ACLS		if (racl->names.count != 0 && racl->mask_obj == group_perms)			racl->mask_obj = NO_ENTRY;#endif	}	racl->other_obj = NO_ENTRY;} /* Given an empty rsync_acl, fake up the permission bits. */static void rsync_acl_fake_perms(rsync_acl *racl, mode_t mode){	racl->user_obj = (mode >> 6) & 7;	racl->group_obj = (mode >> 3) & 7;	racl->other_obj = mode & 7;} /* === Rsync ACL functions === */ static rsync_acl *create_racl(void){	rsync_acl *racl = new(rsync_acl); 	*racl = empty_rsync_acl; 	return racl;} static BOOL ida_entries_equal(const ida_entries *ial1, const ida_entries *ial2){	id_access *ida1, *ida2;	int count = ial1->count;	if (count != ial2->count)		return False;	ida1 = ial1->idas;	ida2 = ial2->idas;	for (; count--; ida1++, ida2++) {		if (ida1->access != ida2->access || ida1->id != ida2->id)			return False;	}	return True;} static BOOL rsync_acl_equal(const rsync_acl *racl1, const rsync_acl *racl2){	return racl1->user_obj == racl2->user_obj	    && racl1->group_obj == racl2->group_obj	    && racl1->mask_obj == racl2->mask_obj	    && racl1->other_obj == racl2->other_obj	    && ida_entries_equal(&racl1->names, &racl2->names);} /* Are the extended (non-permission-bit) entries equal?  If so, the rest of * the ACL will be handled by the normal mode-preservation code.  This is * only meaningful for access ACLs!  Note: the 1st arg is a fully-populated * rsync_acl, but the 2nd parameter can be a condensed rsync_acl, which means * that it might have several of its permission objects set to NO_ENTRY. */static BOOL rsync_acl_equal_enough(const rsync_acl *racl1,				   const rsync_acl *racl2, mode_t m){	if ((racl1->mask_obj ^ racl2->mask_obj) & NO_ENTRY)		return False; /* One has a mask and the other doesn't */ 	/* When there's a mask, the group_obj becomes an extended entry. */	if (racl1->mask_obj != NO_ENTRY) {		/* A condensed rsync_acl with a mask can only have no		 * group_obj when it was identical to the mask.  This		 * means that it was also identical to the group attrs		 * from the mode. */		if (racl2->group_obj == NO_ENTRY) {			if (racl1->group_obj != ((m >> 3) & 7))				return False;		} else if (racl1->group_obj != racl2->group_obj)			return False;	}	return ida_entries_equal(&racl1->names, &racl2->names);} static void rsync_acl_free(rsync_acl *racl){	if (racl->names.idas)		free(racl->names.idas);	*racl = empty_rsync_acl;} void free_acl(stat_x *sxp){	if (sxp->acc_acl) {		rsync_acl_free(sxp->acc_acl);		free(sxp->acc_acl);		sxp->acc_acl = NULL;	}	if (sxp->def_acl) {		rsync_acl_free(sxp->def_acl);		free(sxp->def_acl);		sxp->def_acl = NULL;	}} #ifdef SMB_ACL_NEED_SORTstatic int id_access_sorter(const void *r1, const void *r2){	id_access *ida1 = (id_access *)r1;	id_access *ida2 = (id_access *)r2;	id_t rid1 = ida1->id, rid2 = ida2->id;	if ((ida1->access ^ ida2->access) & NAME_IS_USER)		return ida1->access & NAME_IS_USER ? -1 : 1;	return rid1 == rid2 ? 0 : rid1 < rid2 ? -1 : 1;}#endif /* === System ACLs === */ /* Unpack system ACL -> rsync ACL verbatim.  Return whether we succeeded. */static BOOL unpack_smb_acl(SMB_ACL_T sacl, rsync_acl *racl){	static item_list temp_ida_list = EMPTY_ITEM_LIST;	SMB_ACL_ENTRY_T entry;	const char *errfun;	int rc; 	errfun = "sys_acl_get_entry";	for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry);	     rc == 1;	     rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) {		SMB_ACL_TAG_T tag_type;		uint32 access;		id_t g_u_id;		id_access *ida;		if ((rc = sys_acl_get_info(entry, &tag_type, &access, &g_u_id)) != 0) {			errfun = "sys_acl_get_info";			break;		}		/* continue == done with entry; break == store in temporary ida list */		switch (tag_type) {#ifndef HAVE_OSX_ACLS		case SMB_ACL_USER_OBJ:			if (racl->user_obj == NO_ENTRY)				racl->user_obj = access;			else				rprintf(FINFO, "unpack_smb_acl: warning: duplicate USER_OBJ entry ignored\n");			continue;		case SMB_ACL_GROUP_OBJ:			if (racl->group_obj == NO_ENTRY)				racl->group_obj = access;			else				rprintf(FINFO, "unpack_smb_acl: warning: duplicate GROUP_OBJ entry ignored\n");			continue;		case SMB_ACL_MASK:			if (racl->mask_obj == NO_ENTRY)				racl->mask_obj = access;			else				rprintf(FINFO, "unpack_smb_acl: warning: duplicate MASK entry ignored\n");			continue;		case SMB_ACL_OTHER:			if (racl->other_obj == NO_ENTRY)				racl->other_obj = access;			else				rprintf(FINFO, "unpack_smb_acl: warning: duplicate OTHER entry ignored\n");			continue;#endif		case SMB_ACL_USER:			access |= NAME_IS_USER;			break;		case SMB_ACL_GROUP:			break;		default:			rprintf(FINFO, "unpack_smb_acl: warning: entry with unrecognized tag type ignored\n");			continue;		}		ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10);		ida->id = g_u_id;		ida->access = access;	}	if (rc) {		rsyserr(FERROR_XFER, errno, "unpack_smb_acl: %s()", errfun);		rsync_acl_free(racl);		return False;	} 	/* Transfer the count id_access items out of the temp_ida_list	 * into the names ida_entries list in racl. */	if (temp_ida_list.count) {#ifdef SMB_ACL_NEED_SORT		if (temp_ida_list.count > 1) {			qsort(temp_ida_list.items, temp_ida_list.count, sizeof (id_access), id_access_sorter);		}#endif		racl->names.idas = new_array(id_access, temp_ida_list.count);		memcpy(racl->names.idas, temp_ida_list.items, temp_ida_list.count * sizeof (id_access));	} else		racl->names.idas = NULL; 	racl->names.count = temp_ida_list.count; 	/* Truncate the temporary list now that its idas have been saved. */	temp_ida_list.count = 0; 	return True;} /* Synactic sugar for system calls */ #define CALL_OR_ERROR(func,args,str) \	do { \		if (func args) { \			errfun = str; \			goto error_exit; \		} \	} while (0) #define COE(func,args) CALL_OR_ERROR(func,args,#func)#define COE2(func,args) CALL_OR_ERROR(func,args,NULL) #ifndef HAVE_OSX_ACLS/* Store the permissions in the system ACL entry. */static int store_access_in_entry(uint32 access, SMB_ACL_ENTRY_T entry){	if (sys_acl_set_access_bits(entry, access)) {		rsyserr(FERROR_XFER, errno, "store_access_in_entry sys_acl_set_access_bits()");		return -1;	}	return 0;}#endif /* Pack rsync ACL -> system ACL verbatim.  Return whether we succeeded. */static BOOL pack_smb_acl(SMB_ACL_T *smb_acl, const rsync_acl *racl){#ifdef ACLS_NEED_MASK	uchar mask_bits;#endif	size_t count;	id_access *ida;	const char *errfun = NULL;	SMB_ACL_ENTRY_T entry; 	if (!(*smb_acl = sys_acl_init(calc_sacl_entries(racl)))) {		rsyserr(FERROR_XFER, errno, "pack_smb_acl: sys_acl_init()");		return False;	} #ifndef HAVE_OSX_ACLS	COE( sys_acl_create_entry,(smb_acl, &entry) );	COE( sys_acl_set_info,(entry, SMB_ACL_USER_OBJ, racl->user_obj & ~NO_ENTRY, 0) );#endif 	for (ida = racl->names.idas, count = racl->names.count; count; ida++, count--) {#ifdef SMB_ACL_NEED_SORT		if (!(ida->access & NAME_IS_USER))			break;#endif		COE( sys_acl_create_entry,(smb_acl, &entry) );		COE( sys_acl_set_info,		    (entry,		     ida->access & NAME_IS_USER ? SMB_ACL_USER : SMB_ACL_GROUP,		     ida->access & ~NAME_IS_USER, ida->id) );	} #ifndef HAVE_OSX_ACLS	COE( sys_acl_create_entry,(smb_acl, &entry) );	COE( sys_acl_set_info,(entry, SMB_ACL_GROUP_OBJ, racl->group_obj & ~NO_ENTRY, 0) ); #ifdef SMB_ACL_NEED_SORT	for ( ; count; ida++, count--) {		COE( sys_acl_create_entry,(smb_acl, &entry) );		COE( sys_acl_set_info,(entry, SMB_ACL_GROUP, ida->access, ida->id) );	}#endif #ifdef ACLS_NEED_MASK	mask_bits = racl->mask_obj == NO_ENTRY ? racl->group_obj & ~NO_ENTRY : racl->mask_obj;	COE( sys_acl_create_entry,(smb_acl, &entry) );	COE( sys_acl_set_info,(entry, SMB_ACL_MASK, mask_bits, 0) );#else	if (racl->mask_obj != NO_ENTRY) {		COE( sys_acl_create_entry,(smb_acl, &entry) );		COE( sys_acl_set_info,(entry, SMB_ACL_MASK, racl->mask_obj, 0) );	}#endif 	COE( sys_acl_create_entry,(smb_acl, &entry) );	COE( sys_acl_set_info,(entry, SMB_ACL_OTHER, racl->other_obj & ~NO_ENTRY, 0) );#endif #ifdef DEBUG	if (sys_acl_valid(*smb_acl) < 0)		rprintf(FERROR_XFER, "pack_smb_acl: warning: system says the ACL I packed is invalid\n");#endif 	return True;   error_exit:	if (errfun) {		rsyserr(FERROR_XFER, errno, "pack_smb_acl %s()", errfun);	}	sys_acl_free_acl(*smb_acl);	return False;} static int find_matching_rsync_acl(const rsync_acl *racl, SMB_ACL_TYPE_T type,				   const item_list *racl_list){	static int access_match = -1, default_match = -1;	int *match = type == SMB_ACL_TYPE_ACCESS ? &access_match : &default_match;	size_t count = racl_list->count; 	/* If this is the first time through or we didn't match the last	 * time, then start at the end of the list, which should be the	 * best place to start hunting. */	if (*match == -1)		*match = racl_list->count - 1;	while (count--) {		rsync_acl *base = racl_list->items;		if (rsync_acl_equal(base + *match, racl))			return *match;		if (!(*match)--)			*match = racl_list->count - 1;	} 	*match = -1;	return *match;} /* These two bridge lib/acl.c's neutral (tag,perm,id) entry array; with * HAVE_LIBACL_AT the libacl *_at path uses unpack_smb_acl/pack_smb_acl directly, * so they are unused there. */#if defined(SUPPORT_ACL_FD) && !defined(HAVE_LIBACL_AT)/* Convert a packed system ACL into the neutral (tag,perm,id) entry array that * lib/acl.c serializes.  Reuses pack_smb_acl()+change_sacl_perms() output so * the bytes we write match exactly what acl_set_file() would have written. * Returns the entry count and a malloc'd array in *ents_p, or -1 on error. */static int sacl_to_entries(SMB_ACL_T sacl, rsync_acl_ent **ents_p){	static item_list ent_list = EMPTY_ITEM_LIST;	SMB_ACL_ENTRY_T entry;	rsync_acl_ent *out;	int rc; 	ent_list.count = 0;	for (rc = sys_acl_get_entry(sacl, SMB_ACL_FIRST_ENTRY, &entry); rc == 1;	     rc = sys_acl_get_entry(sacl, SMB_ACL_NEXT_ENTRY, &entry)) {		SMB_ACL_TAG_T tag_type;		uint32 access;		id_t g_u_id;		rsync_acl_ent *e;		uint16_t tag; 		if ((rc = sys_acl_get_info(entry, &tag_type, &access, &g_u_id)) != 0)

Lines 1–500 of 1510.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t