2086 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			2086 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* security.cc: NT security functions
 | |
| 
 | |
|    Copyright 1997, 1998, 1999, 2000 Cygnus Solutions.
 | |
| 
 | |
|    Originaly written by Gunther Ebert, gunther.ebert@ixos-leipzig.de
 | |
|    Extensions by Corinna Vinschen <corinna.vinschen@cityweb.de>
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include <grp.h>
 | |
| #include <pwd.h>
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #include <errno.h>
 | |
| #include <limits.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/acl.h>
 | |
| #include "winsup.h"
 | |
| #include <ctype.h>
 | |
| 
 | |
| #define MAX_SID_LEN     40
 | |
| 
 | |
| extern BOOL allow_ntea;
 | |
| BOOL allow_ntsec = FALSE;
 | |
| 
 | |
| SID_IDENTIFIER_AUTHORITY sid_auth[] = {
 | |
|         {SECURITY_NULL_SID_AUTHORITY},
 | |
|         {SECURITY_WORLD_SID_AUTHORITY},
 | |
|         {SECURITY_LOCAL_SID_AUTHORITY},
 | |
|         {SECURITY_CREATOR_SID_AUTHORITY},
 | |
|         {SECURITY_NON_UNIQUE_AUTHORITY},
 | |
|         {SECURITY_NT_AUTHORITY}
 | |
| };
 | |
| 
 | |
| #define DONT_INHERIT (0)
 | |
| #define INHERIT_ALL  (CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE)
 | |
| #define INHERIT_ONLY (INHERIT_ONLY_ACE|CONTAINER_INHERIT_ACE|OBJECT_INHERIT_ACE)
 | |
| 
 | |
| PSID
 | |
| get_sid (PSID psid, DWORD s, DWORD cnt, DWORD *r)
 | |
| {
 | |
|   DWORD i;
 | |
| 
 | |
|   if (! psid || s > 5 || cnt < 1 || cnt > 8)
 | |
|     return NULL;
 | |
| 
 | |
|   InitializeSid(psid, &sid_auth[s], cnt);
 | |
|   for (i = 0; i < cnt; ++i)
 | |
|     memcpy ((char *) psid + 8 + sizeof (DWORD) * i, &r[i], sizeof (DWORD));
 | |
|   return psid;
 | |
| }
 | |
| 
 | |
| PSID
 | |
| get_ssid (PSID psid, const char *sid_str)
 | |
| {
 | |
|   char sid_buf[256];
 | |
|   char *t;
 | |
|   DWORD cnt = 0;
 | |
|   DWORD s = 0;
 | |
|   DWORD i, r[8];
 | |
| 
 | |
|   if (! sid_str || strncmp (sid_str, "S-1-", 4))
 | |
|     return NULL;
 | |
| 
 | |
|   strcpy (sid_buf, sid_str);
 | |
| 
 | |
|   for (t = sid_buf + 4, i = 0; cnt < 8 && (t = strtok (t, "-")); t = NULL, ++i)
 | |
|     if (i == 0)
 | |
|       s = strtoul (t, NULL, 10);
 | |
|     else
 | |
|       r[cnt++] = strtoul (t, NULL, 10);
 | |
| 
 | |
|   return get_sid (psid, s, cnt, r);
 | |
| }
 | |
| 
 | |
| BOOL
 | |
| get_pw_sid (PSID sid, struct passwd *pw)
 | |
| {
 | |
|   char *sp = strrchr (pw->pw_gecos, ',');
 | |
| 
 | |
|   if (!sp)
 | |
|     return FALSE;
 | |
|   return get_ssid (sid, ++sp) != NULL;
 | |
| }
 | |
| 
 | |
| BOOL
 | |
| get_gr_sid (PSID sid, struct group *gr)
 | |
| {
 | |
|   return get_ssid (sid, gr->gr_passwd) != NULL;
 | |
| }
 | |
| 
 | |
| PSID
 | |
| get_admin_sid ()
 | |
| {
 | |
|   static NO_COPY char admin_sid_buf[MAX_SID_LEN];
 | |
|   static NO_COPY PSID admin_sid = NULL;
 | |
| 
 | |
|   if (!admin_sid)
 | |
|     {
 | |
|       admin_sid = (PSID) admin_sid_buf;
 | |
|       get_ssid (admin_sid, "S-1-5-32-544");
 | |
|     }
 | |
|   return admin_sid;
 | |
| }
 | |
| 
 | |
| PSID
 | |
| get_system_sid ()
 | |
| {
 | |
|   static NO_COPY char system_sid_buf[MAX_SID_LEN];
 | |
|   static NO_COPY PSID system_sid = NULL;
 | |
| 
 | |
|   if (!system_sid)
 | |
|     {
 | |
|       system_sid = (PSID) system_sid_buf;
 | |
|       get_ssid (system_sid, "S-1-5-18");
 | |
|     }
 | |
|   return system_sid;
 | |
| }
 | |
| 
 | |
| PSID
 | |
| get_creator_owner_sid ()
 | |
| {
 | |
|   static NO_COPY char owner_sid_buf[MAX_SID_LEN];
 | |
|   static NO_COPY PSID owner_sid = NULL;
 | |
| 
 | |
|   if (!owner_sid)
 | |
|     {
 | |
|       owner_sid = (PSID) owner_sid_buf;
 | |
|       get_ssid (owner_sid, "S-1-3-0");
 | |
|     }
 | |
|   return owner_sid;
 | |
| }
 | |
| 
 | |
| PSID
 | |
| get_world_sid ()
 | |
| {
 | |
|   static NO_COPY char world_sid_buf[MAX_SID_LEN];
 | |
|   static NO_COPY PSID world_sid = NULL;
 | |
| 
 | |
|   if (!world_sid)
 | |
|     {
 | |
|       world_sid = (PSID) world_sid_buf;
 | |
|       get_ssid (world_sid, "S-1-1-0");
 | |
|     }
 | |
|   return world_sid;
 | |
| }
 | |
| 
 | |
| int passwd_sem = 0;
 | |
| int group_sem = 0;
 | |
| 
 | |
| static int
 | |
| get_id_from_sid (PSID psid, BOOL search_grp, int *type)
 | |
| {
 | |
|   if (!IsValidSid (psid))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       small_printf ("IsValidSid failed with %E");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /* First try to get SID from passwd or group entry */
 | |
|   if (allow_ntsec)
 | |
|     {
 | |
|       char sidbuf[MAX_SID_LEN];
 | |
|       PSID sid = (PSID) sidbuf;
 | |
|       int id = -1;
 | |
| 
 | |
|       if (! search_grp)
 | |
|         {
 | |
|           if (passwd_sem > 0)
 | |
|             return 0;
 | |
|           ++passwd_sem;
 | |
| 
 | |
|           struct passwd *pw;
 | |
|           while ((pw = getpwent ()) != NULL)
 | |
|             {
 | |
|               if (get_pw_sid (sid, pw) && EqualSid (psid, sid))
 | |
|                 {
 | |
|                   id = pw->pw_uid;
 | |
|                   break;
 | |
|                 }
 | |
|             }
 | |
|           endpwent ();
 | |
|           --passwd_sem;
 | |
|           if (id >= 0)
 | |
|             {
 | |
|               if (type)
 | |
|                 *type = USER;
 | |
|               return id;
 | |
|             }
 | |
|         }
 | |
|       if (search_grp || type)
 | |
|         {
 | |
|           if (group_sem > 0)
 | |
|             return 0;
 | |
|           ++group_sem;
 | |
| 
 | |
|           struct group *gr;
 | |
|           while ((gr = getgrent ()) != NULL)
 | |
|             {
 | |
|               if (get_gr_sid (sid, gr) && EqualSid (psid, sid))
 | |
|                 {
 | |
|                   id = gr->gr_gid;
 | |
|                   break;
 | |
|                 }
 | |
|             }
 | |
|           endgrent ();
 | |
|           --group_sem;
 | |
|           if (id >= 0)
 | |
|             {
 | |
|               if (type)
 | |
|                 *type = GROUP;
 | |
|               return id;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   /* We use the RID as default UID/GID */
 | |
|   int id = *GetSidSubAuthority(psid, *GetSidSubAuthorityCount(psid) - 1);
 | |
| 
 | |
|   /*
 | |
|    * The RID maybe -1 if accountname == computername.
 | |
|    * In this case we search for the accountname in the passwd and group files.
 | |
|    * If type is needed, we search in each case.
 | |
|    */
 | |
|   if (id == -1 || type)
 | |
|     {
 | |
|       char account[MAX_USER_NAME];
 | |
|       char domain[MAX_COMPUTERNAME_LENGTH+1];
 | |
|       DWORD acc_len = MAX_USER_NAME;
 | |
|       DWORD dom_len = MAX_COMPUTERNAME_LENGTH+1;
 | |
|       SID_NAME_USE acc_type;
 | |
| 
 | |
|       if (!LookupAccountSid (NULL, psid, account, &acc_len,
 | |
|                              domain, &dom_len, &acc_type))
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  return -1;
 | |
| 	}
 | |
| 
 | |
|       switch (acc_type)
 | |
| 	{
 | |
| 	  case SidTypeGroup:
 | |
| 	  case SidTypeAlias:
 | |
| 	  case SidTypeWellKnownGroup:
 | |
|             if (type)
 | |
|               *type = GROUP;
 | |
|             if (id == -1)
 | |
|               {
 | |
|                 struct group *gr = getgrnam (account);
 | |
|                 if (gr)
 | |
|                   id = gr->gr_gid;
 | |
|               }
 | |
|             break;
 | |
| 	  case SidTypeUser:
 | |
|             if (type)
 | |
|               *type = USER;
 | |
|             if (id == -1)
 | |
|               {
 | |
| 	        struct passwd *pw = getpwnam (account);
 | |
|                 if (pw)
 | |
|                   id = pw->pw_uid;
 | |
| 	      }
 | |
|             break;
 | |
| 	  default:
 | |
|             break;
 | |
| 	}
 | |
|     }
 | |
|   if (id == -1)
 | |
|     id = getuid ();
 | |
|   return id;
 | |
| }
 | |
| 
 | |
| int
 | |
| get_id_from_sid (PSID psid, BOOL search_grp)
 | |
| {
 | |
|   return get_id_from_sid (psid, search_grp, NULL);
 | |
| }
 | |
| 
 | |
| static BOOL
 | |
| legal_sid_type (SID_NAME_USE type)
 | |
| {
 | |
|   return type == SidTypeUser || type == SidTypeGroup
 | |
|                  || SidTypeAlias || SidTypeWellKnownGroup;
 | |
| }
 | |
| 
 | |
| BOOL
 | |
| is_grp_member (uid_t uid, gid_t gid)
 | |
| {
 | |
|   extern int getgroups (int, gid_t *, gid_t, const char *);
 | |
|   BOOL grp_member = TRUE;
 | |
| 
 | |
|   if (!group_sem && !passwd_sem)
 | |
|     {
 | |
|       struct passwd *pw = getpwuid (uid);
 | |
|       gid_t grps[NGROUPS_MAX];
 | |
|       int cnt = getgroups (NGROUPS_MAX, grps,
 | |
|                            pw ? pw->pw_gid : myself->gid,
 | |
|                            pw ? pw->pw_name : myself->username);
 | |
|       int i;
 | |
|       for (i = 0; i < cnt; ++i)
 | |
|         if (grps[i] == gid)
 | |
|           break;
 | |
|       grp_member = (i < cnt);
 | |
|     }
 | |
|   return grp_member;
 | |
| }
 | |
| 
 | |
| BOOL
 | |
| lookup_name (const char *name, const char *logsrv, PSID ret_sid)
 | |
| {
 | |
|   char sidbuf[MAX_SID_LEN];
 | |
|   PSID sid = (PSID) sidbuf;
 | |
|   DWORD sidlen;
 | |
|   char domuser[MAX_COMPUTERNAME_LENGTH+MAX_USER_NAME+1];
 | |
|   char dom[MAX_COMPUTERNAME_LENGTH+1];
 | |
|   DWORD domlen;
 | |
|   SID_NAME_USE acc_type;
 | |
| 
 | |
|   debug_printf ("name  : %s", name ? name : "NULL");
 | |
| 
 | |
|   if (! name)
 | |
|     return FALSE;
 | |
| 
 | |
|   if (logsrv && *logsrv)
 | |
|     {
 | |
|       if (LookupAccountName (logsrv, name,
 | |
|                              sid, (sidlen = MAX_SID_LEN, &sidlen),
 | |
|                              dom, (domlen = MAX_COMPUTERNAME_LENGTH, &domlen),
 | |
|                              &acc_type)
 | |
|           && legal_sid_type (acc_type))
 | |
|         goto got_it;
 | |
|       if (acc_type == SidTypeDomain)
 | |
|         {
 | |
|           strcat (strcat (strcpy (domuser, dom), "\\"), name);
 | |
|           if (LookupAccountName (logsrv, domuser,
 | |
|                                  sid,(sidlen = MAX_SID_LEN, &sidlen),
 | |
|                                  dom,(domlen = MAX_COMPUTERNAME_LENGTH,&domlen),
 | |
|                                  &acc_type))
 | |
|             goto got_it;
 | |
|         }
 | |
|     }
 | |
|   if (LookupAccountName (NULL, name,
 | |
|                          sid, (sidlen = MAX_SID_LEN, &sidlen),
 | |
|                          dom, (domlen = 100, &domlen),
 | |
|                          &acc_type)
 | |
|       && legal_sid_type (acc_type))
 | |
|     goto got_it;
 | |
|   if (acc_type == SidTypeDomain)
 | |
|     {
 | |
|       strcat (strcat (strcpy (domuser, dom), "\\"), name);
 | |
|       if (LookupAccountName (NULL, domuser,
 | |
|                              sid, (sidlen = MAX_SID_LEN, &sidlen),
 | |
|                              dom, (domlen = MAX_COMPUTERNAME_LENGTH, &domlen),
 | |
|                              &acc_type))
 | |
|         goto got_it;
 | |
|     }
 | |
|   debug_printf ("LookupAccountName(%s) %E", name);
 | |
|   __seterrno ();
 | |
|   return FALSE;
 | |
| 
 | |
| got_it:
 | |
|   debug_printf ("sid : [%d]", *GetSidSubAuthority((PSID) sid,
 | |
|                               *GetSidSubAuthorityCount((PSID) sid) - 1));
 | |
| 
 | |
|   if (ret_sid)
 | |
|     memcpy (ret_sid, sid, sidlen);
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /* ReadSD reads a security descriptor from a file.
 | |
|    In case of error, -1 is returned and errno is set.
 | |
|    If the file doesn't have a SD, 0 is returned.
 | |
|    Otherwise, the size of the SD is returned and
 | |
|    the SD is copied to the buffer, pointed to by sdBuf.
 | |
|    sdBufSize contains the size of the buffer. If
 | |
|    it's too small, to contain the complete SD, 0 is
 | |
|    returned and sdBufSize is set to the needed size
 | |
|    of the buffer.
 | |
| */
 | |
| 
 | |
| LONG
 | |
| ReadSD(const char *file, PSECURITY_DESCRIPTOR sdBuf, LPDWORD sdBufSize)
 | |
| {
 | |
|   /* Check parameters */
 | |
|   if (! sdBufSize)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /* Open file for read */
 | |
|   HANDLE hFile = CreateFile (file, GENERIC_READ,
 | |
| 			     FILE_SHARE_READ | FILE_SHARE_WRITE,
 | |
| 			     &sec_none_nih, OPEN_EXISTING,
 | |
| 			     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
 | |
| 			     NULL);
 | |
| 
 | |
|   if (hFile == INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /* step through the backup streams and search for the security data */
 | |
|   WIN32_STREAM_ID header;
 | |
|   DWORD bytes_read = 0;
 | |
|   LPVOID context = NULL;
 | |
|   PSECURITY_DESCRIPTOR psd = NULL;
 | |
|   DWORD datasize;
 | |
|   LONG ret = 0;
 | |
| 
 | |
|   while (BackupRead (hFile, (LPBYTE) &header,
 | |
| 		     3 * sizeof (DWORD) + sizeof (LARGE_INTEGER),
 | |
| 		     &bytes_read, FALSE, TRUE, &context))
 | |
|     {
 | |
|       if (header.dwStreamId != BACKUP_SECURITY_DATA)
 | |
| 	continue;
 | |
| 
 | |
|       /* security data found */
 | |
|       datasize = header.Size.LowPart + header.dwStreamNameSize;
 | |
|       char b[datasize];
 | |
| 
 | |
|       if (! BackupRead (hFile, (LPBYTE) b, datasize, &bytes_read,
 | |
| 			FALSE, TRUE, &context))
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  ret = -1;
 | |
| 	  break;
 | |
| 	}
 | |
| 
 | |
|       /* Check validity of the SD */
 | |
|       psd = (PSECURITY_DESCRIPTOR) &b[header.dwStreamNameSize];
 | |
|       if (! IsValidSecurityDescriptor (psd))
 | |
| 	continue;
 | |
| 
 | |
|       /* It's a valid SD */
 | |
|       datasize -= header.dwStreamNameSize;
 | |
|       debug_printf ("SD-Size: %d", datasize);
 | |
| 
 | |
|       /* buffer to small? */
 | |
|       if (*sdBufSize < datasize)
 | |
| 	{
 | |
| 	  *sdBufSize = datasize;
 | |
| 	  ret = 0;
 | |
| 	  break;
 | |
| 	}
 | |
| 
 | |
|       if (sdBuf)
 | |
| 	memcpy (sdBuf, psd, datasize);
 | |
| 
 | |
|       ret = *sdBufSize = datasize;
 | |
|       break;
 | |
| 
 | |
|     }
 | |
|   BackupRead (hFile, NULL, 0, &bytes_read, TRUE, TRUE, &context);
 | |
|   CloseHandle (hFile);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| LONG
 | |
| WriteSD(const char *file, PSECURITY_DESCRIPTOR sdBuf, DWORD sdBufSize)
 | |
| {
 | |
|   /* Check parameters */
 | |
|   if (! sdBuf || ! sdBufSize)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   HANDLE hFile = CreateFile (file,
 | |
| 			     WRITE_OWNER | WRITE_DAC,
 | |
|                              FILE_SHARE_READ | FILE_SHARE_WRITE,
 | |
|                              &sec_none_nih,
 | |
|                              OPEN_EXISTING,
 | |
|                              FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
 | |
|                              NULL);
 | |
| 
 | |
|   if (hFile == INVALID_HANDLE_VALUE)
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   LPVOID context = NULL;
 | |
|   DWORD bytes_written = 0;
 | |
|   WIN32_STREAM_ID header;
 | |
| 
 | |
|   memset (&header, 0, sizeof (header));
 | |
|   /* write new security info header */
 | |
|   header.dwStreamId = BACKUP_SECURITY_DATA;
 | |
|   header.dwStreamAttributes = STREAM_CONTAINS_SECURITY;
 | |
|   header.Size.HighPart = 0;
 | |
|   header.Size.LowPart = sdBufSize;
 | |
|   header.dwStreamNameSize = 0;
 | |
|   if (!BackupWrite (hFile, (LPBYTE) &header,
 | |
| 		    3 * sizeof (DWORD) + sizeof (LARGE_INTEGER),
 | |
| 		    &bytes_written, FALSE, TRUE, &context))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       CloseHandle (hFile);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /* write new security descriptor */
 | |
|   if (!BackupWrite (hFile, (LPBYTE) sdBuf,
 | |
| 		    header.Size.LowPart + header.dwStreamNameSize,
 | |
| 		    &bytes_written, FALSE, TRUE, &context))
 | |
|     {
 | |
|       /* Samba returns ERROR_NOT_SUPPORTED.
 | |
|          FAT returns ERROR_INVALID_SECURITY_DESCR.
 | |
|          This shouldn't return as error, but better be ignored. */
 | |
|       DWORD ret = GetLastError ();
 | |
|       if (ret != ERROR_NOT_SUPPORTED && ret != ERROR_INVALID_SECURITY_DESCR)
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  BackupWrite (hFile, NULL, 0, &bytes_written, TRUE, TRUE, &context);
 | |
| 	  CloseHandle (hFile);
 | |
| 	  return -1;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   /* terminate the restore process */
 | |
|   BackupWrite (hFile, NULL, 0, &bytes_written, TRUE, TRUE, &context);
 | |
|   CloseHandle (hFile);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| set_process_privileges ()
 | |
| {
 | |
|   HANDLE hProcess = NULL;
 | |
|   HANDLE hToken = NULL;
 | |
|   LUID restore_priv;
 | |
|   LUID backup_priv;
 | |
|   char buf[sizeof (TOKEN_PRIVILEGES) + 2 * sizeof (LUID_AND_ATTRIBUTES)];
 | |
|   TOKEN_PRIVILEGES *new_priv = (TOKEN_PRIVILEGES *) buf;
 | |
|   int ret = -1;
 | |
| 
 | |
|   hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId ());
 | |
|   if (! hProcess)
 | |
|     {
 | |
|       __seterrno ();
 | |
|       goto out;
 | |
|     }
 | |
| 
 | |
|   if (! OpenProcessToken (hProcess,
 | |
| 			  TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
 | |
| 			  &hToken))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       goto out;
 | |
|     }
 | |
| 
 | |
|   if (! LookupPrivilegeValue (NULL, SE_RESTORE_NAME, &restore_priv))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       goto out;
 | |
|     }
 | |
|   if (! LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &backup_priv))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       goto out;
 | |
|     }
 | |
| 
 | |
|   new_priv->PrivilegeCount = 2;
 | |
|   new_priv->Privileges[0].Luid = restore_priv;
 | |
|   new_priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
 | |
|   new_priv->Privileges[1].Luid = backup_priv;
 | |
|   new_priv->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
 | |
| 
 | |
|   if (! AdjustTokenPrivileges (hToken, FALSE, new_priv, 0, NULL, NULL))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       goto out;
 | |
|     }
 | |
| 
 | |
|   ret = 0;
 | |
| 
 | |
|   if (ret == -1)
 | |
|     __seterrno ();
 | |
| 
 | |
| out:
 | |
|   if (hToken)
 | |
|     CloseHandle (hToken);
 | |
|   if (hProcess)
 | |
|     CloseHandle (hProcess);
 | |
| 
 | |
|   syscall_printf ("%d = set_process_privileges ()", ret);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| get_nt_attribute (const char *file, int *attribute,
 | |
|                   uid_t *uidret, gid_t *gidret)
 | |
| {
 | |
|   if (os_being_run != winNT)
 | |
|     return 0;
 | |
| 
 | |
|   syscall_printf ("file: %s", file);
 | |
| 
 | |
|   /* Yeah, sounds too much, but I've seen SDs of 2100 bytes! */
 | |
|   DWORD sd_size = 4096;
 | |
|   char sd_buf[4096];
 | |
|   PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
 | |
| 
 | |
|   int ret;
 | |
|   if ((ret = ReadSD (file, psd, &sd_size)) <= 0)
 | |
|     {
 | |
|       debug_printf ("ReadSD %E");
 | |
|       return ret;
 | |
|     }
 | |
| 
 | |
|   PSID owner_sid;
 | |
|   PSID group_sid;
 | |
|   BOOL dummy;
 | |
| 
 | |
|   if (! GetSecurityDescriptorOwner (psd, &owner_sid, &dummy))
 | |
|     debug_printf ("GetSecurityDescriptorOwner %E");
 | |
|   if (! GetSecurityDescriptorGroup (psd, &group_sid, &dummy))
 | |
|     debug_printf ("GetSecurityDescriptorGroup %E");
 | |
| 
 | |
|   PACL acl;
 | |
|   BOOL acl_exists;
 | |
| 
 | |
|   if (! GetSecurityDescriptorDacl (psd, &acl_exists, &acl, &dummy))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       debug_printf ("GetSecurityDescriptorDacl %E");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   uid_t uid = get_uid_from_sid (owner_sid);
 | |
|   gid_t gid = get_gid_from_sid (group_sid);
 | |
|   if (uidret)
 | |
|     *uidret = uid;
 | |
|   if (gidret)
 | |
|     *gidret = gid;
 | |
| 
 | |
|   if (! attribute)
 | |
|     {
 | |
|       syscall_printf ("file: %s uid %d, gid %d", uid, gid);
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   BOOL grp_member = is_grp_member (uid, gid);
 | |
| 
 | |
|   if (! acl_exists || ! acl)
 | |
|     {
 | |
|       *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
 | |
|       syscall_printf ("file: %s No ACL = %x, uid %d, gid %d",
 | |
|                       file, *attribute, uid, gid);
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   ACCESS_ALLOWED_ACE *ace;
 | |
|   int allow = 0;
 | |
|   int deny = 0;
 | |
|   int *flags, *anti;
 | |
| 
 | |
|   for (DWORD i = 0; i < acl->AceCount; ++i)
 | |
|     {
 | |
|       if (!GetAce (acl, i, (PVOID *) &ace))
 | |
|         continue;
 | |
|       if (ace->Header.AceFlags & INHERIT_ONLY_ACE)
 | |
|         continue;
 | |
|       switch (ace->Header.AceType)
 | |
|         {
 | |
|         case ACCESS_ALLOWED_ACE_TYPE:
 | |
|           flags = &allow;
 | |
|           anti = &deny;
 | |
|           break;
 | |
|         case ACCESS_DENIED_ACE_TYPE:
 | |
|           flags = &deny;
 | |
|           anti = &allow;
 | |
|           break;
 | |
|         default:
 | |
|           continue;
 | |
|         }
 | |
| 
 | |
|       PSID ace_sid = (PSID) &ace->SidStart;
 | |
|       if (owner_sid && EqualSid (ace_sid, owner_sid))
 | |
|         {
 | |
|           if (ace->Mask & FILE_READ_DATA)
 | |
|             *flags |= S_IRUSR;
 | |
|           if (ace->Mask & FILE_WRITE_DATA)
 | |
|             *flags |= S_IWUSR;
 | |
|           if (ace->Mask & FILE_EXECUTE)
 | |
|             *flags |= S_IXUSR;
 | |
|         }
 | |
|       else if (group_sid && EqualSid (ace_sid, group_sid))
 | |
|         {
 | |
|           if (ace->Mask & FILE_READ_DATA)
 | |
|             *flags |= S_IRGRP
 | |
|                       | ((grp_member && !(*anti & S_IRUSR)) ? S_IRUSR : 0);
 | |
|           if (ace->Mask & FILE_WRITE_DATA)
 | |
|             *flags |= S_IWGRP
 | |
|                       | ((grp_member && !(*anti & S_IWUSR)) ? S_IWUSR : 0);
 | |
|           if (ace->Mask & FILE_EXECUTE)
 | |
|             *flags |= S_IXGRP
 | |
|                       | ((grp_member && !(*anti & S_IXUSR)) ? S_IXUSR : 0);
 | |
|         }
 | |
|       else if (EqualSid (ace_sid, get_world_sid ()))
 | |
|         {
 | |
|           if (ace->Mask & FILE_READ_DATA)
 | |
|             *flags |= S_IROTH
 | |
|                       | ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
 | |
|                       | ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
 | |
|           if (ace->Mask & FILE_WRITE_DATA)
 | |
|             *flags |= S_IWOTH
 | |
|                       | ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
 | |
|                       | ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
 | |
|           if (ace->Mask & FILE_EXECUTE)
 | |
|             {
 | |
|               *flags |= S_IXOTH
 | |
|                         | ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
 | |
|                         | ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
 | |
|               // Sticky bit for directories according to linux rules.
 | |
|               // No sense for files.
 | |
|               if (! (ace->Mask & FILE_DELETE_CHILD)
 | |
|                   && S_ISDIR(*attribute)
 | |
|                   && !(*anti & S_ISVTX))
 | |
|                 *flags |= S_ISVTX;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|   *attribute &= ~(S_IRWXU|S_IRWXG|S_IRWXO|S_ISVTX);
 | |
|   *attribute |= allow;
 | |
|   *attribute &= ~deny;
 | |
|   syscall_printf ("file: %s %x, uid %d, gid %d", file, *attribute, uid, gid);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| get_file_attribute (int use_ntsec, const char *file, int *attribute,
 | |
|                     uid_t *uidret, gid_t *gidret)
 | |
| {
 | |
|   if (use_ntsec && allow_ntsec)
 | |
|     return get_nt_attribute (file, attribute, uidret, gidret);
 | |
| 
 | |
|   if (uidret)
 | |
|     *uidret = getuid ();
 | |
|   if (gidret)
 | |
|     *gidret = getgid ();
 | |
| 
 | |
|   if (! attribute)
 | |
|     return 0;
 | |
| 
 | |
|   int res = NTReadEA (file, ".UNIXATTR",
 | |
|                       (char *) attribute, sizeof (*attribute));
 | |
| 
 | |
|   // symlinks are anything for everyone!
 | |
|   if ((*attribute & S_IFLNK) == S_IFLNK)
 | |
|     *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
 | |
| 
 | |
|   if (res <= 0)
 | |
|     set_errno (ENOSYS);
 | |
|   return res > 0 ? 0 : -1;
 | |
| }
 | |
| 
 | |
| BOOL add_access_allowed_ace (PACL acl, int offset, DWORD attributes,
 | |
|                              PSID sid, size_t &len_add, DWORD inherit)
 | |
| {
 | |
|   if (! AddAccessAllowedAce (acl, ACL_REVISION, attributes, sid))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return FALSE;
 | |
|     }
 | |
|   ACCESS_ALLOWED_ACE *ace;
 | |
|   if (GetAce(acl, offset, (PVOID *) &ace))
 | |
|     ace->Header.AceFlags |= inherit;
 | |
|   len_add += sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD)
 | |
|              + GetLengthSid (sid);
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| BOOL add_access_denied_ace (PACL acl, int offset, DWORD attributes,
 | |
|                             PSID sid, size_t &len_add, DWORD inherit)
 | |
| {
 | |
|   if (! AddAccessDeniedAce (acl, ACL_REVISION, attributes, sid))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return FALSE;
 | |
|     }
 | |
|   ACCESS_DENIED_ACE *ace;
 | |
|   if (GetAce(acl, offset, (PVOID *) &ace))
 | |
|     ace->Header.AceFlags |= inherit;
 | |
|   len_add += sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD)
 | |
|              + GetLengthSid (sid);
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| PSECURITY_DESCRIPTOR
 | |
| alloc_sd (uid_t uid, gid_t gid, const char *logsrv, int attribute,
 | |
| 	  PSECURITY_DESCRIPTOR sd_ret, DWORD *sd_size_ret)
 | |
| {
 | |
|   BOOL dummy;
 | |
| 
 | |
|   if (os_being_run != winNT)
 | |
|     return NULL;
 | |
| 
 | |
|   if (! sd_ret || ! sd_size_ret)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   // Get SID and name of new owner
 | |
|   char owner[MAX_USER_NAME];
 | |
|   char *owner_sid_buf[MAX_SID_LEN];
 | |
|   PSID owner_sid = NULL;
 | |
|   struct passwd *pw = getpwuid (uid);
 | |
|   strcpy (owner, pw ? pw->pw_name : getlogin ());
 | |
|   owner_sid = (PSID) owner_sid_buf;
 | |
|   if ((! pw || ! get_pw_sid (owner_sid, pw))
 | |
|       && ! lookup_name (owner, logsrv, owner_sid))
 | |
|     return NULL;
 | |
|   debug_printf ("owner: %s [%d]", owner,
 | |
|                 *GetSidSubAuthority((PSID) owner_sid,
 | |
| 		*GetSidSubAuthorityCount((PSID) owner_sid) - 1));
 | |
| 
 | |
|   // Get SID and name of new group
 | |
|   char *group_sid_buf[MAX_SID_LEN];
 | |
|   PSID group_sid = NULL;
 | |
|   struct group *grp = getgrgid (gid);
 | |
|   if (grp)
 | |
|     {
 | |
|       group_sid = (PSID) group_sid_buf;
 | |
|       if ((! grp || ! get_gr_sid (group_sid, grp))
 | |
|           && ! lookup_name (grp->gr_name, logsrv, group_sid))
 | |
|         return NULL;
 | |
|     }
 | |
|   else
 | |
|     debug_printf ("no group");
 | |
| 
 | |
|   // Initialize local security descriptor
 | |
|   SECURITY_DESCRIPTOR sd;
 | |
|   PSECURITY_DESCRIPTOR psd = NULL;
 | |
|   if (! InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   if (! SetSecurityDescriptorOwner(&sd, owner_sid, FALSE))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return NULL;
 | |
|     }
 | |
|   if (group_sid
 | |
|       && ! SetSecurityDescriptorGroup(&sd, group_sid, FALSE))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   // Initialize local access control list
 | |
|   char acl_buf[3072];
 | |
|   PACL acl = (PACL) acl_buf;
 | |
|   if (! InitializeAcl (acl, 3072, ACL_REVISION))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   // VTX bit may only be set if executable for `other' is set.
 | |
|   // For correct handling under WinNT, FILE_DELETE_CHILD has to
 | |
|   // be (un)set in each ACE.
 | |
|   if (! (attribute & S_IXOTH))
 | |
|     attribute &= ~S_ISVTX;
 | |
| 
 | |
|   // From here fill ACL
 | |
|   size_t acl_len = sizeof (ACL);
 | |
|   int ace_off = 0;
 | |
| 
 | |
|   // Construct allow attribute for owner
 | |
|   DWORD owner_allow = (STANDARD_RIGHTS_ALL & ~DELETE)
 | |
|                       | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA;
 | |
|   if (attribute & S_IRUSR)
 | |
|     owner_allow |= FILE_GENERIC_READ;
 | |
|   if (attribute & S_IWUSR)
 | |
|     owner_allow |= FILE_GENERIC_WRITE | DELETE;
 | |
|   if (attribute & S_IXUSR)
 | |
|     owner_allow |= FILE_GENERIC_EXECUTE;
 | |
|   if (! (attribute & S_ISVTX))
 | |
|     owner_allow |= FILE_DELETE_CHILD;
 | |
| 
 | |
|   // Construct allow attribute for group
 | |
|   DWORD group_allow = STANDARD_RIGHTS_READ
 | |
|                       | FILE_READ_ATTRIBUTES | FILE_READ_EA;
 | |
|   if (attribute & S_IRGRP)
 | |
|     group_allow |= FILE_GENERIC_READ;
 | |
|   if (attribute & S_IWGRP)
 | |
|     group_allow |= STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE | DELETE;
 | |
|   if (attribute & S_IXGRP)
 | |
|     group_allow |= FILE_GENERIC_EXECUTE;
 | |
|   if (! (attribute & S_ISVTX))
 | |
|     group_allow |= FILE_DELETE_CHILD;
 | |
| 
 | |
|   // Construct allow attribute for everyone
 | |
|   DWORD other_allow = STANDARD_RIGHTS_READ
 | |
|                       | FILE_READ_ATTRIBUTES | FILE_READ_EA;
 | |
|   if (attribute & S_IROTH)
 | |
|     other_allow |= FILE_GENERIC_READ;
 | |
|   if (attribute & S_IWOTH)
 | |
|     other_allow |= STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE | DELETE;
 | |
|   if (attribute & S_IXOTH)
 | |
|     other_allow |= FILE_GENERIC_EXECUTE;
 | |
|   if (! (attribute & S_ISVTX))
 | |
|     other_allow |= FILE_DELETE_CHILD;
 | |
|   
 | |
|   // Construct deny attributes for owner and group
 | |
|   DWORD owner_deny = 0;
 | |
|   if (is_grp_member (uid, gid))
 | |
|     owner_deny = ~owner_allow & (group_allow | other_allow);
 | |
|   else
 | |
|     owner_deny = ~owner_allow & other_allow;
 | |
|   owner_deny &= ~(STANDARD_RIGHTS_READ
 | |
|                   | FILE_READ_ATTRIBUTES | FILE_READ_EA
 | |
|                   | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA);
 | |
|   DWORD group_deny = ~group_allow & other_allow;
 | |
|   group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_READ_EA);
 | |
| 
 | |
|   // Set deny ACE for owner
 | |
|   if (owner_deny
 | |
|       && ! add_access_denied_ace (acl, ace_off++, owner_deny,
 | |
|                                   owner_sid, acl_len, INHERIT_ALL))
 | |
|       return NULL;
 | |
|   // Set allow ACE for owner
 | |
|   if (! add_access_allowed_ace (acl, ace_off++, owner_allow,
 | |
|                                 owner_sid, acl_len, INHERIT_ALL))
 | |
|     return NULL;
 | |
|   // Set deny ACE for group
 | |
|   if (group_deny
 | |
|       && ! add_access_denied_ace (acl, ace_off++, group_deny,
 | |
|                                   group_sid, acl_len, INHERIT_ALL))
 | |
|       return NULL;
 | |
|   // Set allow ACE for group
 | |
|   if (! add_access_allowed_ace (acl, ace_off++, group_allow,
 | |
|                                 group_sid, acl_len, INHERIT_ALL))
 | |
|     return NULL;
 | |
| 
 | |
|   // Get owner and group from current security descriptor
 | |
|   PSID cur_owner_sid = NULL;
 | |
|   PSID cur_group_sid = NULL;
 | |
|   if (! GetSecurityDescriptorOwner (sd_ret, &cur_owner_sid, &dummy))
 | |
|     debug_printf ("GetSecurityDescriptorOwner %E");
 | |
|   if (! GetSecurityDescriptorGroup (sd_ret, &cur_group_sid, &dummy))
 | |
|     debug_printf ("GetSecurityDescriptorGroup %E");
 | |
| 
 | |
|   // Fill ACL with unrelated ACEs from current security descriptor
 | |
|   PACL oacl;
 | |
|   BOOL acl_exists;
 | |
|   ACCESS_ALLOWED_ACE *ace;
 | |
|   if (GetSecurityDescriptorDacl (sd_ret, &acl_exists, &oacl, &dummy)
 | |
|       && acl_exists && oacl)
 | |
|     for (DWORD i = 0; i < oacl->AceCount; ++i)
 | |
|       if (GetAce (oacl, i, (PVOID *) &ace))
 | |
|         {
 | |
|           PSID ace_sid = (PSID) &ace->SidStart;
 | |
|           // Check for related ACEs
 | |
|           if ((cur_owner_sid && EqualSid (ace_sid, cur_owner_sid))
 | |
|               || (owner_sid && EqualSid (ace_sid, owner_sid))
 | |
|               || (cur_group_sid && EqualSid (ace_sid, cur_group_sid))
 | |
|               || (group_sid && EqualSid (ace_sid, group_sid))
 | |
|               || (EqualSid (ace_sid, get_world_sid ())))
 | |
|             continue;
 | |
|           // Add unrelated ACCESS_DENIED_ACE to the beginning but
 | |
|           // behind the owner_deny, ACCESS_ALLOWED_ACE to the end
 | |
|           // but in front of the `everyone' ACE.
 | |
|           if (! AddAce(acl, ACL_REVISION,
 | |
|                        ace->Header.AceType == ACCESS_DENIED_ACE_TYPE ?
 | |
|                        (owner_deny ? 1 : 0) : MAXDWORD,
 | |
|                        (LPVOID) ace, ace->Header.AceSize))
 | |
|             {
 | |
|               __seterrno ();
 | |
|               return NULL;
 | |
|             }
 | |
|           acl_len += ace->Header.AceSize;
 | |
|           ++ace_off;
 | |
|         }
 | |
| 
 | |
|   // Set allow ACE for everyone
 | |
|   if (! add_access_allowed_ace (acl, ace_off++, other_allow,
 | |
|                                 get_world_sid (), acl_len, INHERIT_ALL))
 | |
|     return NULL;
 | |
| 
 | |
|   // Set AclSize to computed value
 | |
|   acl->AclSize = acl_len;
 | |
|   debug_printf ("ACL-Size: %d", acl_len);
 | |
| 
 | |
|   // Create DACL for local security descriptor
 | |
|   if (! SetSecurityDescriptorDacl (&sd, TRUE, acl, FALSE))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   // Make self relative security descriptor
 | |
|   *sd_size_ret = 0;
 | |
|   MakeSelfRelativeSD (&sd, sd_ret, sd_size_ret);
 | |
|   if (*sd_size_ret <= 0)
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return NULL;
 | |
|     }
 | |
|   if (! MakeSelfRelativeSD (&sd, sd_ret, sd_size_ret))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return NULL;
 | |
|     }
 | |
|   psd = sd_ret;
 | |
|   debug_printf ("Created SD-Size: %d", *sd_size_ret);
 | |
| 
 | |
|   return psd;
 | |
| }
 | |
| 
 | |
| static int
 | |
| set_nt_attribute (const char *file, uid_t uid, gid_t gid,
 | |
|                   const char *logsrv, int attribute)
 | |
| {
 | |
|   if (os_being_run != winNT)
 | |
|     return 0;
 | |
| 
 | |
|   DWORD sd_size = 4096;
 | |
|   char sd_buf[4096];
 | |
|   PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
 | |
| 
 | |
|   int ret;
 | |
|   if ((ret = ReadSD (file, psd, &sd_size)) <= 0)
 | |
|     {
 | |
|       debug_printf ("ReadSD %E");
 | |
|       return ret;
 | |
|     }
 | |
| 
 | |
|   sd_size = 4096;
 | |
|   if (! (psd = alloc_sd (uid, gid, logsrv, attribute, psd, &sd_size)))
 | |
|     return -1;
 | |
| 
 | |
|   return WriteSD (file, psd, sd_size);
 | |
| }
 | |
| 
 | |
| int
 | |
| set_file_attribute (int use_ntsec, const char *file,
 | |
|                     uid_t uid, gid_t gid,
 | |
|                     int attribute, const char *logsrv)
 | |
| {
 | |
|   // symlinks are anything for everyone!
 | |
|   if ((attribute & S_IFLNK) == S_IFLNK)
 | |
|     attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
 | |
| 
 | |
|   if (!use_ntsec || !allow_ntsec)
 | |
|     {
 | |
|       if (! NTWriteEA (file, ".UNIXATTR",
 | |
|                        (char *) &attribute, sizeof (attribute)))
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  return -1;
 | |
| 	}
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   int ret = set_nt_attribute (file, uid, gid, logsrv, attribute);
 | |
|   syscall_printf ("%d = set_file_attribute (%s, %d, %d, %p)",
 | |
| 		  ret, file, uid, gid, attribute);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| int
 | |
| set_file_attribute (int use_ntsec, const char *file, int attribute)
 | |
| {
 | |
|   return set_file_attribute (use_ntsec, file,
 | |
|                              myself->uid, myself->gid,
 | |
|                              attribute, myself->logsrv);
 | |
| }
 | |
| 
 | |
| static int
 | |
| searchace (aclent_t *aclp, int nentries, int type, int id = -1)
 | |
| {
 | |
|   int i;
 | |
| 
 | |
|   for (i = 0; i < nentries; ++i)
 | |
|     if ((aclp[i].a_type == type && (id < 0 || aclp[i].a_id == id))
 | |
|         || !aclp[i].a_type)
 | |
|       return i;
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| setacl (const char *file, int nentries, aclent_t *aclbufp)
 | |
| {
 | |
|   DWORD sd_size = 4096;
 | |
|   char sd_buf[4096];
 | |
|   PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
 | |
| 
 | |
|   if (ReadSD (file, psd, &sd_size) <= 0)
 | |
|     {
 | |
|       debug_printf ("ReadSD %E");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   BOOL dummy;
 | |
| 
 | |
|   // Get owner SID
 | |
|   PSID owner_sid = NULL;
 | |
|   if (! GetSecurityDescriptorOwner (psd, &owner_sid, &dummy))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   char owner_buf[MAX_SID_LEN];
 | |
|   if (!CopySid (MAX_SID_LEN, (PSID) owner_buf, owner_sid))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   owner_sid = (PSID) owner_buf;
 | |
| 
 | |
|   // Get group SID
 | |
|   PSID group_sid = NULL;
 | |
|   if (! GetSecurityDescriptorGroup (psd, &group_sid, &dummy))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   char group_buf[MAX_SID_LEN];
 | |
|   if (!CopySid (MAX_SID_LEN, (PSID) group_buf, group_sid))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   group_sid = (PSID) group_buf;
 | |
| 
 | |
|   // Initialize local security descriptor
 | |
|   SECURITY_DESCRIPTOR sd;
 | |
|   if (! InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   if (! SetSecurityDescriptorOwner(&sd, owner_sid, FALSE))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   if (group_sid
 | |
|       && ! SetSecurityDescriptorGroup(&sd, group_sid, FALSE))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   // Fill access control list
 | |
|   char acl_buf[3072];
 | |
|   PACL acl = (PACL) acl_buf;
 | |
|   size_t acl_len = sizeof (ACL);
 | |
|   int ace_off = 0;
 | |
| 
 | |
|   char sidbuf[MAX_SID_LEN];
 | |
|   PSID sid = (PSID) sidbuf;
 | |
|   struct passwd *pw;
 | |
|   struct group *gr;
 | |
|   int pos;
 | |
| 
 | |
|   if (! InitializeAcl (acl, 3072, ACL_REVISION))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   for (int i = 0; i < nentries; ++i)
 | |
|     {
 | |
|       DWORD allow = STANDARD_RIGHTS_READ
 | |
|                     | FILE_READ_ATTRIBUTES | FILE_READ_EA;
 | |
|       if (aclbufp[i].a_perm & S_IROTH)
 | |
|         allow |= FILE_GENERIC_READ;
 | |
|       if (aclbufp[i].a_perm & S_IWOTH)
 | |
|         allow |= STANDARD_RIGHTS_ALL | FILE_GENERIC_WRITE
 | |
|                  | DELETE | FILE_DELETE_CHILD;
 | |
|       if (aclbufp[i].a_perm & S_IXOTH)
 | |
|         allow |= FILE_GENERIC_EXECUTE;
 | |
|       // Set inherit property
 | |
|       DWORD inheritance = (aclbufp[i].a_type & ACL_DEFAULT)
 | |
|                           ? INHERIT_ONLY : DONT_INHERIT;
 | |
|       // If a specific acl contains a corresponding default entry with
 | |
|       // identical permissions, only one Windows ACE with proper
 | |
|       // inheritance bits is created.
 | |
|       if (!(aclbufp[i].a_type & ACL_DEFAULT)
 | |
|           && (pos = searchace (aclbufp, nentries,
 | |
|                                aclbufp[i].a_type | ACL_DEFAULT,
 | |
|                                (aclbufp[i].a_type & (USER|GROUP))
 | |
|                                ? aclbufp[i].a_id : -1)) >= 0
 | |
|           && pos < nentries
 | |
|           && aclbufp[i].a_perm == aclbufp[pos].a_perm)
 | |
|         {
 | |
|           inheritance = INHERIT_ALL;
 | |
|           // This eliminates the corresponding default entry.
 | |
|           aclbufp[pos].a_type = 0;
 | |
|         }
 | |
|       switch (aclbufp[i].a_type)
 | |
|         {
 | |
|         case USER_OBJ:
 | |
|         case DEF_USER_OBJ:
 | |
|           allow |= STANDARD_RIGHTS_ALL & ~DELETE;
 | |
|           if (! add_access_allowed_ace (acl, ace_off++, allow,
 | |
|                                         owner_sid, acl_len, inheritance))
 | |
|             return -1;
 | |
|           break;
 | |
|         case USER:
 | |
|         case DEF_USER:
 | |
|           if (!(pw = getpwuid (aclbufp[i].a_id))
 | |
|               || ! get_pw_sid (sid, pw)
 | |
|               || ! add_access_allowed_ace (acl, ace_off++, allow,
 | |
|                                            sid, acl_len, inheritance))
 | |
|             return -1;
 | |
|           break;
 | |
|         case GROUP_OBJ:
 | |
|         case DEF_GROUP_OBJ:
 | |
|           if (! add_access_allowed_ace (acl, ace_off++, allow,
 | |
|                                         group_sid, acl_len, inheritance))
 | |
|             return -1;
 | |
|           break;
 | |
|         case GROUP:
 | |
|         case DEF_GROUP:
 | |
|           if (!(gr = getgrgid (aclbufp[i].a_id))
 | |
|               || ! get_gr_sid (sid, gr)
 | |
|               || ! add_access_allowed_ace (acl, ace_off++, allow,
 | |
|                                            sid, acl_len, inheritance))
 | |
|             return -1;
 | |
|           break;
 | |
|         case OTHER_OBJ:
 | |
|         case DEF_OTHER_OBJ:
 | |
|           if (! add_access_allowed_ace (acl, ace_off++, allow,
 | |
|                                         get_world_sid(), acl_len, inheritance))
 | |
|             return -1;
 | |
|           break;
 | |
|         }
 | |
|     }
 | |
|   // Set AclSize to computed value
 | |
|   acl->AclSize = acl_len;
 | |
|   debug_printf ("ACL-Size: %d", acl_len);
 | |
|   // Create DACL for local security descriptor
 | |
|   if (! SetSecurityDescriptorDacl (&sd, TRUE, acl, FALSE))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   // Make self relative security descriptor in psd
 | |
|   sd_size = 0;
 | |
|   MakeSelfRelativeSD (&sd, psd, &sd_size);
 | |
|   if (sd_size <= 0)
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   if (! MakeSelfRelativeSD (&sd, psd, &sd_size))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   debug_printf ("Created SD-Size: %d", sd_size);
 | |
|   return WriteSD (file, psd, sd_size);
 | |
| }
 | |
| 
 | |
| static void
 | |
| getace (aclent_t &acl, int type, int id, DWORD win_ace_mask, DWORD win_ace_type)
 | |
| {
 | |
|   acl.a_type = type;
 | |
|   acl.a_id = id;
 | |
| 
 | |
|   if (win_ace_mask & FILE_READ_DATA)
 | |
|     if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
 | |
|       acl.a_perm |= (acl.a_perm & S_IRGRP) ? 0 : S_IRUSR;
 | |
|     else if (win_ace_type == ACCESS_DENIED_ACE_TYPE)
 | |
|       acl.a_perm &= ~S_IRGRP;
 | |
| 
 | |
|   if (win_ace_mask & FILE_WRITE_DATA)
 | |
|     if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
 | |
|       acl.a_perm |= (acl.a_perm & S_IWGRP) ? 0 : S_IWUSR;
 | |
|     else if (win_ace_type == ACCESS_DENIED_ACE_TYPE)
 | |
|       acl.a_perm &= ~S_IWGRP;
 | |
| 
 | |
|   if (win_ace_mask & FILE_EXECUTE)
 | |
|     if (win_ace_type == ACCESS_ALLOWED_ACE_TYPE)
 | |
|       acl.a_perm |= (acl.a_perm & S_IXGRP) ? 0 : S_IXUSR;
 | |
|     else if (win_ace_type == ACCESS_DENIED_ACE_TYPE)
 | |
|       acl.a_perm &= ~S_IXGRP;
 | |
| }
 | |
| 
 | |
| static int
 | |
| getacl (const char *file, DWORD attr, int nentries, aclent_t *aclbufp)
 | |
| {
 | |
|   DWORD sd_size = 4096;
 | |
|   char sd_buf[4096];
 | |
|   PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) sd_buf;
 | |
| 
 | |
|   int ret;
 | |
|   if ((ret = ReadSD (file, psd, &sd_size)) <= 0)
 | |
|     {
 | |
|       debug_printf ("ReadSD %E");
 | |
|       return ret;
 | |
|     }
 | |
| 
 | |
|   PSID owner_sid;
 | |
|   PSID group_sid;
 | |
|   BOOL dummy;
 | |
|   uid_t uid;
 | |
|   gid_t gid;
 | |
| 
 | |
|   if (! GetSecurityDescriptorOwner (psd, &owner_sid, &dummy))
 | |
|     {
 | |
|       debug_printf ("GetSecurityDescriptorOwner %E");
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   uid = get_uid_from_sid (owner_sid);
 | |
| 
 | |
|   if (! GetSecurityDescriptorGroup (psd, &group_sid, &dummy))
 | |
|     {
 | |
|       debug_printf ("GetSecurityDescriptorGroup %E");
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
|   gid = get_gid_from_sid (group_sid);
 | |
| 
 | |
|   aclent_t lacl[MAX_ACL_ENTRIES];
 | |
|   memset (&lacl, 0, MAX_ACL_ENTRIES * sizeof (aclent_t));
 | |
|   lacl[0].a_type = USER_OBJ;
 | |
|   lacl[0].a_id = uid;
 | |
|   lacl[1].a_type = GROUP_OBJ;
 | |
|   lacl[1].a_id = gid;
 | |
|   lacl[2].a_type = OTHER_OBJ;
 | |
| 
 | |
|   PACL acl;
 | |
|   BOOL acl_exists;
 | |
| 
 | |
|   if (! GetSecurityDescriptorDacl (psd, &acl_exists, &acl, &dummy))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       debug_printf ("GetSecurityDescriptorDacl %E");
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   int pos, i;
 | |
| 
 | |
|   if (! acl_exists || ! acl)
 | |
|     {
 | |
|       for (pos = 0; pos < MIN_ACL_ENTRIES; ++pos)
 | |
|         lacl[pos].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
 | |
|       pos = nentries < MIN_ACL_ENTRIES ? nentries : MIN_ACL_ENTRIES;
 | |
|       memcpy (aclbufp, lacl, pos * sizeof (aclent_t));
 | |
|       return pos;
 | |
|     }
 | |
| 
 | |
|   for (i = 0; i < acl->AceCount && (!nentries || i < nentries); ++i)
 | |
|     {
 | |
|       ACCESS_ALLOWED_ACE *ace;
 | |
| 
 | |
|       if (!GetAce (acl, i, (PVOID *) &ace))
 | |
|         continue;
 | |
| 
 | |
|       PSID ace_sid = (PSID) &ace->SidStart;
 | |
|       int id;
 | |
|       int type = 0;
 | |
| 
 | |
|       if (EqualSid (ace_sid, owner_sid))
 | |
|         {
 | |
|           type = USER_OBJ;
 | |
|           id = uid;
 | |
|         }
 | |
|       else if (EqualSid (ace_sid, group_sid))
 | |
|         {
 | |
|           type = GROUP_OBJ;
 | |
|           id = gid;
 | |
|         }
 | |
|       else if (EqualSid (ace_sid, get_world_sid ()))
 | |
|         {
 | |
|           type = OTHER_OBJ;
 | |
|           id = 0;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           id = get_id_from_sid (ace_sid, FALSE, &type);
 | |
|           if (type != GROUP)
 | |
|             {
 | |
|               int type2 = 0;
 | |
|               int id2 = get_id_from_sid (ace_sid, TRUE, &type2);
 | |
|               if (type2 == GROUP)
 | |
|                 {
 | |
|                   id = id2;
 | |
|                   type = GROUP;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|       if (!type)
 | |
|         continue;
 | |
|       if (!(ace->Header.AceFlags & INHERIT_ONLY_ACE))
 | |
|         {
 | |
|           if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
 | |
|             getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType);
 | |
|         }
 | |
|       if ((ace->Header.AceFlags & INHERIT_ALL)
 | |
|           && (attr & FILE_ATTRIBUTE_DIRECTORY))
 | |
|         {
 | |
|           type |= ACL_DEFAULT;
 | |
|           if ((pos = searchace (lacl, MAX_ACL_ENTRIES, type, id)) >= 0)
 | |
|             getace (lacl[pos], type, id, ace->Mask, ace->Header.AceType);
 | |
|         }
 | |
|     }
 | |
|   if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0)
 | |
|     pos = MAX_ACL_ENTRIES;
 | |
|   for (i = 0; i < pos; ++i)
 | |
|     {
 | |
|       lacl[i].a_perm = (lacl[i].a_perm & S_IRWXU)
 | |
|                        & ~((lacl[i].a_perm & S_IRWXG) << 3);
 | |
|       lacl[i].a_perm |= (lacl[i].a_perm & S_IRWXU) >> 3
 | |
|                         | (lacl[i].a_perm & S_IRWXU) >> 6;
 | |
|     }
 | |
|   if ((searchace (lacl, MAX_ACL_ENTRIES, USER) >= 0
 | |
|        || searchace (lacl, MAX_ACL_ENTRIES, GROUP) >= 0)
 | |
|       && (pos = searchace (lacl, MAX_ACL_ENTRIES, CLASS_OBJ)) >= 0)
 | |
|     {
 | |
|       lacl[pos].a_type = CLASS_OBJ;
 | |
|       lacl[pos].a_perm =
 | |
|           lacl[searchace (lacl, MAX_ACL_ENTRIES, GROUP_OBJ)].a_perm;
 | |
|     }
 | |
|   int dgpos;
 | |
|   if ((searchace (lacl, MAX_ACL_ENTRIES, DEF_USER) >= 0
 | |
|        || searchace (lacl, MAX_ACL_ENTRIES, DEF_GROUP) >= 0)
 | |
|       && (dgpos = searchace (lacl, MAX_ACL_ENTRIES, DEF_GROUP_OBJ)) >= 0
 | |
|       && (pos = searchace (lacl, MAX_ACL_ENTRIES, DEF_CLASS_OBJ)) >= 0
 | |
|       && (attr & FILE_ATTRIBUTE_DIRECTORY))
 | |
|     {
 | |
|       lacl[pos].a_type = DEF_CLASS_OBJ;
 | |
|       lacl[pos].a_perm = lacl[dgpos].a_perm;
 | |
|     }
 | |
|   if ((pos = searchace (lacl, MAX_ACL_ENTRIES, 0)) < 0)
 | |
|     pos = MAX_ACL_ENTRIES;
 | |
|   if (pos > nentries)
 | |
|     pos = nentries;
 | |
|   if (aclbufp)
 | |
|     memcpy (aclbufp, lacl, pos * sizeof (aclent_t));
 | |
|   aclsort (pos, 0, aclbufp);
 | |
|   syscall_printf ("%d = getacl (%s)", pos, file);
 | |
|   return pos;
 | |
| }
 | |
| 
 | |
| int
 | |
| acl_access (const char *path, int flags)
 | |
| {
 | |
|   aclent_t acls[MAX_ACL_ENTRIES];
 | |
|   int cnt;
 | |
| 
 | |
|   if ((cnt = acl (path, GETACL, MAX_ACL_ENTRIES, acls)) < 1)
 | |
|     return -1;
 | |
| 
 | |
|   // Only check existance.
 | |
|   if (!(flags & (R_OK|W_OK|X_OK)))
 | |
|     return 0;
 | |
| 
 | |
|   for (int i = 0; i < cnt; ++i)
 | |
|     {
 | |
|       switch (acls[i].a_type)
 | |
|         {
 | |
|         case USER_OBJ:
 | |
|         case USER:
 | |
|           if (acls[i].a_id != myself->uid)
 | |
|             {
 | |
|               // Check if user is a NT group:
 | |
|               // Take SID from passwd, search SID in group, check is_grp_member
 | |
|               char owner_sidbuf[MAX_SID_LEN];
 | |
|               PSID owner_sid = (PSID) owner_sidbuf;
 | |
|               char group_sidbuf[MAX_SID_LEN];
 | |
|               PSID group_sid = (PSID) group_sidbuf;
 | |
|               struct passwd *pw;
 | |
|               struct group *gr = NULL;
 | |
| 
 | |
|               if (group_sem > 0)
 | |
|                 continue;
 | |
|               ++group_sem;
 | |
|               if ((pw = getpwuid (acls[i].a_id)) != NULL
 | |
|                   && get_pw_sid (owner_sid, pw))
 | |
|                 {
 | |
|                   while ((gr = getgrent ()))
 | |
|                     if (get_gr_sid (group_sid, gr)
 | |
|                         && EqualSid (owner_sid, group_sid)
 | |
|                         && is_grp_member (myself->uid, gr->gr_gid))
 | |
|                       break;
 | |
|                   endgrent ();
 | |
|                 }
 | |
|               --group_sem;
 | |
|               if (! gr)
 | |
|                 continue;
 | |
|             }
 | |
|           break;
 | |
|         case GROUP_OBJ:
 | |
|         case GROUP:
 | |
|           if (acls[i].a_id != myself->gid &&
 | |
|               !is_grp_member (myself->uid, acls[i].a_id))
 | |
|             continue;
 | |
|           break;
 | |
|         case OTHER_OBJ:
 | |
|           break;
 | |
|         default:
 | |
|           continue;
 | |
|         }
 | |
|       if ((!(flags & R_OK) || (acls[i].a_perm & S_IREAD))
 | |
|           && (!(flags & W_OK) || (acls[i].a_perm & S_IWRITE))
 | |
|           && (!(flags & X_OK) || (acls[i].a_perm & S_IEXEC)))
 | |
|         return 0;
 | |
|     }
 | |
|   set_errno (EACCES);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int
 | |
| acl (const char *path, int cmd, int nentries, aclent_t *aclbufp)
 | |
| {
 | |
|   path_conv real_path (path);
 | |
|   if (real_path.error)
 | |
|     {
 | |
|       set_errno (real_path.error);
 | |
|       syscall_printf ("-1 = acl (%s)", path);
 | |
|       return -1;
 | |
|     }
 | |
|   if (!real_path.has_acls ())
 | |
|     {
 | |
|       struct stat st;
 | |
|       int ret = -1;
 | |
| 
 | |
|       switch (cmd)
 | |
|         {
 | |
|         case SETACL:
 | |
|           set_errno (ENOSYS);
 | |
|           break;
 | |
|         case GETACL:
 | |
|           if (nentries < 1)
 | |
|             set_errno (EINVAL);
 | |
|           else if (! stat (path, &st))
 | |
|             {
 | |
|               aclent_t lacl[4];
 | |
|               if (nentries > 0)
 | |
|                 {
 | |
|                   lacl[0].a_type = USER_OBJ;
 | |
|                   lacl[0].a_id = st.st_uid;
 | |
|                   lacl[0].a_perm = (st.st_mode & S_IRWXU)
 | |
|                                    | (st.st_mode & S_IRWXU) >> 3
 | |
|                                    | (st.st_mode & S_IRWXU) >> 6;
 | |
|                 }
 | |
|               if (nentries > 1)
 | |
|                 {
 | |
|                   lacl[1].a_type = GROUP_OBJ;
 | |
|                   lacl[1].a_id = st.st_gid;
 | |
|                   lacl[1].a_perm = (st.st_mode & S_IRWXG)
 | |
|                                    | (st.st_mode & S_IRWXG) << 3
 | |
|                                    | (st.st_mode & S_IRWXG) >> 3;
 | |
|                 }
 | |
|               if (nentries > 2)
 | |
|                 {
 | |
|                   lacl[2].a_type = OTHER_OBJ;
 | |
|                   lacl[2].a_id = 0;
 | |
|                   lacl[2].a_perm = (st.st_mode & S_IRWXO)
 | |
|                                    | (st.st_mode & S_IRWXO) << 6
 | |
|                                    | (st.st_mode & S_IRWXO) << 3;
 | |
|                 }
 | |
|               if (nentries > 3)
 | |
|                 {
 | |
|                   lacl[3].a_type = CLASS_OBJ;
 | |
|                   lacl[3].a_id = 0;
 | |
|                   lacl[3].a_perm = (st.st_mode & S_IRWXG)
 | |
|                                    | (st.st_mode & S_IRWXG) << 3
 | |
|                                    | (st.st_mode & S_IRWXG) >> 3;
 | |
|                 }
 | |
|               if (nentries > 4)
 | |
|                 nentries = 4;
 | |
|               if (aclbufp)
 | |
|                 memcpy (aclbufp, lacl, nentries * sizeof (aclent_t));
 | |
|               ret = nentries;
 | |
|             }
 | |
|           break;
 | |
|         case GETACLCNT:
 | |
|           ret = 4;
 | |
|           break;
 | |
|         }
 | |
|       syscall_printf ("%d = acl (%s)", ret, path);
 | |
|       return ret;
 | |
|     }
 | |
|   switch (cmd)
 | |
|     {
 | |
|       case SETACL:
 | |
|         if (!aclsort(nentries, 0, aclbufp))
 | |
|           return setacl (real_path.get_win32 (),
 | |
|                          nentries, aclbufp);
 | |
|         break;
 | |
|       case GETACL:
 | |
|         if (nentries < 1)
 | |
|           break;
 | |
|         return getacl (real_path.get_win32 (),
 | |
|                        real_path.file_attributes (),
 | |
|                        nentries, aclbufp);
 | |
|       case GETACLCNT:
 | |
|         return getacl (real_path.get_win32 (),
 | |
|                        real_path.file_attributes (),
 | |
|                        0, NULL);
 | |
|       default:
 | |
|         break;
 | |
|     }
 | |
|   set_errno (EINVAL);
 | |
|   syscall_printf ("-1 = acl (%s)", path);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int
 | |
| facl (int fd, int cmd, int nentries, aclent_t *aclbufp)
 | |
| {
 | |
|   if (dtable.not_open (fd))
 | |
|     {
 | |
|       syscall_printf ("-1 = facl (%d)", fd);
 | |
|       set_errno (EBADF);
 | |
|       return -1;
 | |
|     }
 | |
|   const char *path = dtable[fd]->get_name ();
 | |
|   if (path == NULL)
 | |
|     {
 | |
|       syscall_printf ("-1 = facl (%d) (no name)", fd);
 | |
|       set_errno (ENOSYS);
 | |
|       return -1;
 | |
|     }
 | |
|   syscall_printf ("facl (%d): calling acl (%s)", fd, path);
 | |
|   return acl (path, cmd, nentries, aclbufp);
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int
 | |
| aclcheck (aclent_t *aclbufp, int nentries, int *which)
 | |
| {
 | |
|   BOOL has_user_obj = FALSE;
 | |
|   BOOL has_group_obj = FALSE;
 | |
|   BOOL has_other_obj = FALSE;
 | |
|   BOOL has_class_obj = FALSE;
 | |
|   BOOL has_ug_objs = FALSE;
 | |
|   BOOL has_def_user_obj = FALSE;
 | |
|   BOOL has_def_group_obj = FALSE;
 | |
|   BOOL has_def_other_obj = FALSE;
 | |
|   BOOL has_def_class_obj = FALSE;
 | |
|   BOOL has_def_ug_objs = FALSE;
 | |
|   int pos2;
 | |
| 
 | |
|   for (int pos = 0; pos < nentries; ++pos)
 | |
|     switch (aclbufp[pos].a_type)
 | |
|       {
 | |
|       case USER_OBJ:
 | |
|         if (has_user_obj)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos;
 | |
|             return USER_ERROR;
 | |
|           }
 | |
|         has_user_obj = TRUE;
 | |
|         break;
 | |
|       case GROUP_OBJ:
 | |
|         if (has_group_obj)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos;
 | |
|             return GRP_ERROR;
 | |
|           }
 | |
|         has_group_obj = TRUE;
 | |
|         break;
 | |
|       case OTHER_OBJ:
 | |
|         if (has_other_obj)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos;
 | |
|             return OTHER_ERROR;
 | |
|           }
 | |
|         has_other_obj = TRUE;
 | |
|         break;
 | |
|       case CLASS_OBJ:
 | |
|         if (has_class_obj)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos;
 | |
|             return CLASS_ERROR;
 | |
|           }
 | |
|         has_class_obj = TRUE;
 | |
|         break;
 | |
|       case USER:
 | |
|       case GROUP:
 | |
|         if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1,
 | |
|                                aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos2;
 | |
|             return DUPLICATE_ERROR;
 | |
|           }
 | |
|         has_ug_objs = TRUE;
 | |
|         break;
 | |
|       case DEF_USER_OBJ:
 | |
|         if (has_def_user_obj)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos;
 | |
|             return USER_ERROR;
 | |
|           }
 | |
|         has_def_user_obj = TRUE;
 | |
|         break;
 | |
|       case DEF_GROUP_OBJ:
 | |
|         if (has_def_group_obj)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos;
 | |
|             return GRP_ERROR;
 | |
|           }
 | |
|         has_def_group_obj = TRUE;
 | |
|         break;
 | |
|       case DEF_OTHER_OBJ:
 | |
|         if (has_def_other_obj)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos;
 | |
|             return OTHER_ERROR;
 | |
|           }
 | |
|         has_def_other_obj = TRUE;
 | |
|         break;
 | |
|       case DEF_CLASS_OBJ:
 | |
|         if (has_def_class_obj)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos;
 | |
|             return CLASS_ERROR;
 | |
|           }
 | |
|         has_def_class_obj = TRUE;
 | |
|         break;
 | |
|       case DEF_USER:
 | |
|       case DEF_GROUP:
 | |
|         if ((pos2 = searchace (aclbufp + pos + 1, nentries - pos - 1,
 | |
|                                aclbufp[pos].a_type, aclbufp[pos].a_id)) >= 0)
 | |
|           {
 | |
|             if (which)
 | |
|               *which = pos2;
 | |
|             return DUPLICATE_ERROR;
 | |
|           }
 | |
|         has_def_ug_objs = TRUE;
 | |
|         break;
 | |
|       default:
 | |
|         return ENTRY_ERROR;
 | |
|       }
 | |
|   if (!has_user_obj
 | |
|       || !has_group_obj
 | |
|       || !has_other_obj
 | |
| #if 0
 | |
|       // These checks are not ok yet since CLASS_OBJ isn't fully implemented.
 | |
|       || (has_ug_objs && !has_class_obj)
 | |
|       || (has_def_ug_objs && !has_def_class_obj)
 | |
| #endif
 | |
|      )
 | |
|     {
 | |
|       if (which)
 | |
|         *which = -1;
 | |
|       return MISS_ERROR;
 | |
|     }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int acecmp (const void *a1, const void *a2)
 | |
| {
 | |
| #define ace(i) ((const aclent_t *) a##i)
 | |
|   int ret = ace(1)->a_type - ace(2)->a_type;
 | |
|   if (!ret)
 | |
|     ret = ace(1)->a_id - ace(2)->a_id;
 | |
|   return ret;
 | |
| #undef ace
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int
 | |
| aclsort (int nentries, int, aclent_t *aclbufp)
 | |
| {
 | |
|   if (aclcheck (aclbufp, nentries, NULL))
 | |
|     return -1;
 | |
|   if (!aclbufp || nentries < 1)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   qsort((void *) aclbufp, nentries, sizeof (aclent_t), acecmp);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int
 | |
| acltomode (aclent_t *aclbufp, int nentries, mode_t *modep)
 | |
| {
 | |
|   int pos;
 | |
| 
 | |
|   if (!aclbufp || nentries < 1 || ! modep)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   *modep = 0;
 | |
|   if ((pos = searchace (aclbufp, nentries, USER_OBJ)) < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   *modep |= aclbufp[pos].a_perm & S_IRWXU;
 | |
|   if ((pos = searchace (aclbufp, nentries, GROUP_OBJ)) < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   if (searchace (aclbufp, nentries, CLASS_OBJ) < 0)
 | |
|     pos = searchace (aclbufp, nentries, CLASS_OBJ);
 | |
|   *modep |= (aclbufp[pos].a_perm & S_IRWXU) >> 3;
 | |
|   if ((pos = searchace (aclbufp, nentries, OTHER_OBJ)) < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   *modep |= (aclbufp[pos].a_perm & S_IRWXU) >> 6;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int
 | |
| aclfrommode(aclent_t *aclbufp, int nentries, mode_t *modep)
 | |
| {
 | |
|   int pos;
 | |
| 
 | |
|   if (!aclbufp || nentries < 1 || ! modep)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   if ((pos = searchace (aclbufp, nentries, USER_OBJ)) < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   aclbufp[pos].a_perm = (*modep & S_IRWXU)
 | |
|                         | (*modep & S_IRWXU) >> 3
 | |
|                         | (*modep & S_IRWXU) >> 6;
 | |
|   if ((pos = searchace (aclbufp, nentries, GROUP_OBJ)) < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   if (searchace (aclbufp, nentries, CLASS_OBJ) < 0)
 | |
|     pos = searchace (aclbufp, nentries, CLASS_OBJ);
 | |
|   aclbufp[pos].a_perm = (*modep & S_IRWXG)
 | |
|                         | (*modep & S_IRWXG) << 3
 | |
|                         | (*modep & S_IRWXG) >> 3;
 | |
|   if ((pos = searchace (aclbufp, nentries, OTHER_OBJ)) < 0)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
|   aclbufp[pos].a_perm = (*modep & S_IRWXO)
 | |
|                         | (*modep & S_IRWXO) << 6
 | |
|                         | (*modep & S_IRWXO) << 3;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int
 | |
| acltopbits (aclent_t *aclbufp, int nentries, mode_t *pbitsp)
 | |
| {
 | |
|   return acltomode (aclbufp, nentries, pbitsp);
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| int
 | |
| aclfrompbits (aclent_t *aclbufp, int nentries, mode_t *pbitsp)
 | |
| {
 | |
|   return aclfrommode (aclbufp, nentries, pbitsp);
 | |
| }
 | |
| 
 | |
| static char *
 | |
| permtostr (mode_t perm)
 | |
| {
 | |
|   static char pbuf[4];
 | |
| 
 | |
|   pbuf[0] = (perm & S_IREAD) ? 'r' : '-';
 | |
|   pbuf[1] = (perm & S_IWRITE) ? 'w' : '-';
 | |
|   pbuf[2] = (perm & S_IEXEC) ? 'x' : '-';
 | |
|   pbuf[3] = '\0';
 | |
|   return pbuf;
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| char *
 | |
| acltotext (aclent_t *aclbufp, int aclcnt)
 | |
| {
 | |
|   if (!aclbufp || aclcnt < 1 || aclcnt > MAX_ACL_ENTRIES
 | |
|       || aclcheck (aclbufp, aclcnt, NULL))
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return NULL;
 | |
|     }
 | |
|   char buf[32000];
 | |
|   buf[0] = '\0';
 | |
|   BOOL first = TRUE;
 | |
| 
 | |
|   for (int pos = 0; pos < aclcnt; ++pos)
 | |
|     {
 | |
|       if (!first)
 | |
|         strcat (buf, ",");
 | |
|       first = FALSE;
 | |
|       if (aclbufp[pos].a_type & ACL_DEFAULT)
 | |
|         strcat (buf, "default");
 | |
|       switch (aclbufp[pos].a_type)
 | |
|         {
 | |
|         case USER_OBJ:
 | |
|           sprintf (buf + strlen (buf), "user::%s",
 | |
|                    permtostr (aclbufp[pos].a_perm));
 | |
|           break;
 | |
|         case USER:
 | |
|           sprintf (buf + strlen (buf), "user:%d:%s",
 | |
|                    aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm));
 | |
|           break;
 | |
|         case GROUP_OBJ:
 | |
|           sprintf (buf + strlen (buf), "group::%s",
 | |
|                    permtostr (aclbufp[pos].a_perm));
 | |
|           break;
 | |
|         case GROUP:
 | |
|           sprintf (buf + strlen (buf), "group:%d:%s",
 | |
|                    aclbufp[pos].a_id, permtostr (aclbufp[pos].a_perm));
 | |
|           break;
 | |
|         case CLASS_OBJ:
 | |
|           sprintf (buf + strlen (buf), "mask::%s",
 | |
|                    permtostr (aclbufp[pos].a_perm));
 | |
|           break;
 | |
|         case OTHER_OBJ:
 | |
|           sprintf (buf + strlen (buf), "other::%s",
 | |
|                    permtostr (aclbufp[pos].a_perm));
 | |
|           break;
 | |
|         default:
 | |
|           set_errno (EINVAL);
 | |
|           return NULL;
 | |
|         }
 | |
|     }
 | |
|   return strdup (buf);
 | |
| }
 | |
| 
 | |
| static mode_t
 | |
| permfromstr (char *perm)
 | |
| {
 | |
|   mode_t mode = 0;
 | |
| 
 | |
|   if (strlen (perm) != 3)
 | |
|     return 01000;
 | |
|   if (perm[0] == 'r')
 | |
|     mode |= S_IRUSR | S_IRGRP | S_IROTH;
 | |
|   else if (perm[0] != '-')
 | |
|     return 01000;
 | |
|   if (perm[1] == 'w')
 | |
|     mode |= S_IWUSR | S_IWGRP | S_IWOTH;
 | |
|   else if (perm[1] != '-')
 | |
|     return 01000;
 | |
|   if (perm[2] == 'x')
 | |
|     mode |= S_IXUSR | S_IXGRP | S_IXOTH;
 | |
|   else if (perm[2] != '-')
 | |
|     return 01000;
 | |
|   return mode;
 | |
| }
 | |
| 
 | |
| extern "C"
 | |
| aclent_t *
 | |
| aclfromtext (char *acltextp, int *)
 | |
| {
 | |
|   if (!acltextp)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return NULL;
 | |
|     }
 | |
|   char buf[strlen (acltextp) + 1];
 | |
|   aclent_t lacl[MAX_ACL_ENTRIES];
 | |
|   memset (lacl, 0, sizeof lacl);
 | |
|   int pos = 0;
 | |
|   for (char *c = strtok (buf, ","); c; c = strtok (NULL, ","))
 | |
|     {
 | |
|       if (!strncmp (c, "default", 7))
 | |
|         {
 | |
|           lacl[pos].a_type |= ACL_DEFAULT;
 | |
|           c += 7;
 | |
|         }
 | |
|       if (!strncmp (c, "user:", 5))
 | |
|         {
 | |
|           if (c[5] == ':')
 | |
|             lacl[pos].a_type |= USER_OBJ;
 | |
|           else
 | |
|             {
 | |
|               lacl[pos].a_type |= USER;
 | |
|               c += 5;
 | |
|               if (isalpha (*c))
 | |
|                 {
 | |
|                   struct passwd *pw = getpwnam (c);
 | |
|                   if (!pw)
 | |
|                     {
 | |
|                       set_errno (EINVAL);
 | |
|                       return NULL;
 | |
|                     }
 | |
|                   lacl[pos].a_id = pw->pw_uid;
 | |
|                   c = strchr (c, ':');
 | |
|                 }
 | |
|               else if (isdigit (*c))
 | |
|                 lacl[pos].a_id = strtol (c, &c, 10);
 | |
|               if (!c || *c != ':')
 | |
|                 {
 | |
|                   set_errno (EINVAL);
 | |
|                   return NULL;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|       else if (!strncmp (c, "group:", 6))
 | |
|         {
 | |
|           if (c[5] == ':')
 | |
|             lacl[pos].a_type |= GROUP_OBJ;
 | |
|           else
 | |
|             {
 | |
|               lacl[pos].a_type |= GROUP;
 | |
|               c += 5;
 | |
|               if (isalpha (*c))
 | |
|                 {
 | |
|                   struct group *gr = getgrnam (c);
 | |
|                   if (!gr)
 | |
|                     {
 | |
|                       set_errno (EINVAL);
 | |
|                       return NULL;
 | |
|                     }
 | |
|                   lacl[pos].a_id = gr->gr_gid;
 | |
|                   c = strchr (c, ':');
 | |
|                 }
 | |
|               else if (isdigit (*c))
 | |
|                 lacl[pos].a_id = strtol (c, &c, 10);
 | |
|               if (!c || *c != ':')
 | |
|                 {
 | |
|                   set_errno (EINVAL);
 | |
|                   return NULL;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|       else if (!strncmp (c, "mask:", 5))
 | |
|         {
 | |
|           if (c[5] == ':')
 | |
|             lacl[pos].a_type |= CLASS_OBJ;
 | |
|           else
 | |
|             {
 | |
|               set_errno (EINVAL);
 | |
|               return NULL;
 | |
|             }
 | |
|         }
 | |
|       else if (!strncmp (c, "other:", 6))
 | |
|         {
 | |
|           if (c[5] == ':')
 | |
|             lacl[pos].a_type |= OTHER_OBJ;
 | |
|           else
 | |
|             {
 | |
|               set_errno (EINVAL);
 | |
|               return NULL;
 | |
|             }
 | |
|         }
 | |
|       if ((lacl[pos].a_perm = permfromstr (c)) == 01000)
 | |
|         {
 | |
|           set_errno (EINVAL);
 | |
|           return NULL;
 | |
|         }
 | |
|       ++pos;
 | |
|     }
 | |
|   aclent_t *aclp = (aclent_t *) malloc (pos * sizeof (aclent_t));
 | |
|   if (aclp)
 | |
|     memcpy (aclp, lacl, pos * sizeof (aclent_t));
 | |
|   return aclp;
 | |
| }
 | |
| 
 |