acl: implement Update in C
The original implementation was effectively just writing C in Go. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
		
							parent
							
								
									7e69893264
								
							
						
					
					
						commit
						e85be67fd9
					
				
							
								
								
									
										69
									
								
								acl/acl-update.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								acl/acl-update.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,69 @@ | |||||||
|  | #include "acl-update.h" | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <sys/acl.h> | ||||||
|  | #include <acl/libacl.h> | ||||||
|  | 
 | ||||||
|  | int f_acl_update_file_by_uid(const char *path_p, uid_t uid, acl_perm_t *perms, size_t plen) { | ||||||
|  |     int ret = -1; | ||||||
|  |     bool v; | ||||||
|  |     int i; | ||||||
|  |     acl_t acl; | ||||||
|  |     acl_entry_t entry; | ||||||
|  |     acl_tag_t tag_type; | ||||||
|  |     void *qualifier_p; | ||||||
|  |     acl_permset_t permset; | ||||||
|  | 
 | ||||||
|  |     acl = acl_get_file(path_p, ACL_TYPE_ACCESS); | ||||||
|  |     if (acl == NULL) | ||||||
|  |         goto out; | ||||||
|  | 
 | ||||||
|  |     // prune entries by uid
 | ||||||
|  |     for (i = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); i == 1; i = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) { | ||||||
|  |         if (acl_get_tag_type(entry, &tag_type) != 0) | ||||||
|  |             return -1; | ||||||
|  |         if (tag_type != ACL_USER) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         qualifier_p = acl_get_qualifier(entry); | ||||||
|  |         if (qualifier_p == NULL) | ||||||
|  |             return -1; | ||||||
|  |         v = *(uid_t *)qualifier_p == uid; | ||||||
|  |         acl_free(qualifier_p); | ||||||
|  | 
 | ||||||
|  |         if (!v) | ||||||
|  |             continue; | ||||||
|  | 
 | ||||||
|  |         acl_delete_entry(acl, entry); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (plen == 0) | ||||||
|  |         goto set; | ||||||
|  | 
 | ||||||
|  |     if (acl_create_entry(&acl, &entry) != 0) | ||||||
|  |         goto out; | ||||||
|  |     if (acl_get_permset(entry, &permset) != 0) | ||||||
|  |         goto out; | ||||||
|  |     for (i = 0; i < plen; i++) { | ||||||
|  |         if (acl_add_perm(permset, perms[i]) != 0) | ||||||
|  |             goto out; | ||||||
|  |     } | ||||||
|  |     if (acl_set_tag_type(entry, ACL_USER) != 0) | ||||||
|  |         goto out; | ||||||
|  |     if (acl_set_qualifier(entry, (void *)&uid) != 0) | ||||||
|  |         goto out; | ||||||
|  | 
 | ||||||
|  | set: | ||||||
|  |     if (acl_calc_mask(&acl) != 0) | ||||||
|  |         goto out; | ||||||
|  |     if (acl_valid(acl) != 0) | ||||||
|  |         goto out; | ||||||
|  |     if (acl_set_file(path_p, ACL_TYPE_ACCESS, acl) == 0) | ||||||
|  |         ret = 0; | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  |     free((void *)path_p); | ||||||
|  |     if (acl != NULL) | ||||||
|  |         acl_free((void *)acl); | ||||||
|  |     return ret; | ||||||
|  | } | ||||||
							
								
								
									
										3
									
								
								acl/acl-update.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								acl/acl-update.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | #include <sys/acl.h> | ||||||
|  | 
 | ||||||
|  | int f_acl_update_file_by_uid(const char *path_p, uid_t uid, acl_perm_t *perms, size_t plen); | ||||||
							
								
								
									
										193
									
								
								acl/acl.go
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								acl/acl.go
									
									
									
									
									
								
							| @ -1,197 +1,36 @@ | |||||||
| // Package acl implements simple ACL manipulation via libacl. | // Package acl implements simple ACL manipulation via libacl. | ||||||
| package acl | package acl | ||||||
| 
 | 
 | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
| 	"runtime" |  | ||||||
| 	"syscall" |  | ||||||
| 	"unsafe" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| /* | /* | ||||||
| #cgo linux pkg-config: --static libacl | #cgo linux pkg-config: --static libacl | ||||||
| 
 | 
 | ||||||
| #include <stdlib.h> | #include "acl-update.h" | ||||||
| #include <sys/acl.h> |  | ||||||
| #include <acl/libacl.h> |  | ||||||
| 
 |  | ||||||
| static acl_t _go_acl_get_file(const char *path_p, acl_type_t type) { |  | ||||||
| 	acl_t acl = acl_get_file(path_p, type); |  | ||||||
| 	free((void *)path_p); |  | ||||||
| 	return acl; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int _go_acl_set_file(const char *path_p, acl_type_t type, acl_t acl) { |  | ||||||
| 	if (acl_valid(acl) != 0) { |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	int ret = acl_set_file(path_p, type, acl); |  | ||||||
| 	free((void *)path_p); |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| */ | */ | ||||||
| import "C" | import "C" | ||||||
| 
 | 
 | ||||||
| func getFile(name string, t C.acl_type_t) (*ACL, error) { | type Perm C.acl_perm_t | ||||||
| 	a, err := C._go_acl_get_file(C.CString(name), t) |  | ||||||
| 	if errors.Is(err, syscall.ENODATA) { |  | ||||||
| 		err = nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return newACL(a), err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (acl *ACL) setFile(name string, t C.acl_type_t) error { |  | ||||||
| 	_, err := C._go_acl_set_file(C.CString(name), t, acl.acl) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func newACL(a C.acl_t) *ACL { |  | ||||||
| 	acl := &ACL{a} |  | ||||||
| 	runtime.SetFinalizer(acl, (*ACL).free) |  | ||||||
| 	return acl |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type ACL struct { |  | ||||||
| 	acl C.acl_t |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (acl *ACL) free() { |  | ||||||
| 	C.acl_free(unsafe.Pointer(acl.acl)) |  | ||||||
| 
 |  | ||||||
| 	// no need for a finalizer anymore |  | ||||||
| 	runtime.SetFinalizer(acl, nil) |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	Read    = C.ACL_READ | 	Read    Perm = C.ACL_READ | ||||||
| 	Write   = C.ACL_WRITE | 	Write   Perm = C.ACL_WRITE | ||||||
| 	Execute = C.ACL_EXECUTE | 	Execute Perm = C.ACL_EXECUTE | ||||||
| 
 |  | ||||||
| 	TypeDefault = C.ACL_TYPE_DEFAULT |  | ||||||
| 	TypeAccess  = C.ACL_TYPE_ACCESS |  | ||||||
| 
 |  | ||||||
| 	UndefinedTag = C.ACL_UNDEFINED_TAG |  | ||||||
| 	UserObj      = C.ACL_USER_OBJ |  | ||||||
| 	User         = C.ACL_USER |  | ||||||
| 	GroupObj     = C.ACL_GROUP_OBJ |  | ||||||
| 	Group        = C.ACL_GROUP |  | ||||||
| 	Mask         = C.ACL_MASK |  | ||||||
| 	Other        = C.ACL_OTHER |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type ( |  | ||||||
| 	Perm C.acl_perm_t |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func (acl *ACL) removeEntry(tt C.acl_tag_t, tq int) error { |  | ||||||
| 	var e C.acl_entry_t |  | ||||||
| 
 |  | ||||||
| 	// get first entry |  | ||||||
| 	if r, err := C.acl_get_entry(acl.acl, C.ACL_FIRST_ENTRY, &e); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} else if r == 0 { |  | ||||||
| 		// return on acl with no entries |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for { |  | ||||||
| 		if r, err := C.acl_get_entry(acl.acl, C.ACL_NEXT_ENTRY, &e); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} else if r == 0 { |  | ||||||
| 			// return on drained acl |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		var ( |  | ||||||
| 			q int |  | ||||||
| 			t C.acl_tag_t |  | ||||||
| 		) |  | ||||||
| 
 |  | ||||||
| 		// get current entry tag type |  | ||||||
| 		if _, err := C.acl_get_tag_type(e, &t); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// get current entry qualifier |  | ||||||
| 		if rq, err := C.acl_get_qualifier(e); err != nil { |  | ||||||
| 			// neither ACL_USER nor ACL_GROUP |  | ||||||
| 			if errors.Is(err, syscall.EINVAL) { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			return err |  | ||||||
| 		} else { |  | ||||||
| 			q = *(*int)(rq) |  | ||||||
| 			C.acl_free(rq) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// delete on match |  | ||||||
| 		if t == tt && q == tq { |  | ||||||
| 			_, err := C.acl_delete_entry(acl.acl, e) |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Update replaces ACL_USER entry with qualifier uid. | // Update replaces ACL_USER entry with qualifier uid. | ||||||
| func Update(name string, uid int, perms ...Perm) error { | func Update(name string, uid int, perms ...Perm) error { | ||||||
| 	// read acl from file | 	var p *Perm | ||||||
| 	a, err := getFile(name, TypeAccess) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	// free acl on return if get is successful |  | ||||||
| 	defer a.free() |  | ||||||
| 
 |  | ||||||
| 	// remove existing entry |  | ||||||
| 	if err = a.removeEntry(User, uid); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// create new entry if perms are passed |  | ||||||
| 	if len(perms) > 0 { | 	if len(perms) > 0 { | ||||||
| 		// create new acl entry | 		p = &perms[0] | ||||||
| 		var e C.acl_entry_t |  | ||||||
| 		if _, err = C.acl_create_entry(&a.acl, &e); err != nil { |  | ||||||
| 			return err |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 		// get perm set of new entry | 	r, err := C.f_acl_update_file_by_uid( | ||||||
| 		var p C.acl_permset_t | 		C.CString(name), | ||||||
| 		if _, err = C.acl_get_permset(e, &p); err != nil { | 		C.uid_t(uid), | ||||||
|  | 		(*C.acl_perm_t)(p), | ||||||
|  | 		C.size_t(len(perms)), | ||||||
|  | 	) | ||||||
|  | 	if r == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
| 	return err | 	return err | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// add target perms |  | ||||||
| 		for _, perm := range perms { |  | ||||||
| 			if _, err = C.acl_add_perm(p, C.acl_perm_t(perm)); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// set perm set to new entry |  | ||||||
| 		if _, err = C.acl_set_permset(e, p); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// set user tag to new entry |  | ||||||
| 		if _, err = C.acl_set_tag_type(e, User); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// set qualifier (uid) to new entry |  | ||||||
| 		if _, err = C.acl_set_qualifier(e, unsafe.Pointer(&uid)); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// calculate mask after update |  | ||||||
| 	if _, err = C.acl_calc_mask(&a.acl); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// write acl to file |  | ||||||
| 	return a.setFile(name, TypeAccess) |  | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user