From f995b164b98221e224661e370d61ad08dadb2986 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 12 Nov 2004 23:42:12 +0000 Subject: r3713: Implementation of get posix acls in UNIX extensions. Passes valgrind. Need to add printout functions in client and set posix acl in server. SteveF - take a look at this for the cifsfs client ! Once this is working and tested the next step is to write this up for the UNIX extensions spec. documents. Jeremy. (This used to be commit 1bd3f133442a472b4718b94a636f2fec89a2e0dc) --- source3/client/client.c | 47 +++++++++++ source3/include/trans2.h | 71 ++++++++++++++++ source3/libsmb/clifile.c | 48 +++++++++++ source3/libsmb/clifsinfo.c | 58 +++++++++++++ source3/smbd/trans2.c | 197 ++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 418 insertions(+), 3 deletions(-) (limited to 'source3') diff --git a/source3/client/client.c b/source3/client/client.c index 311eaef8f2..5644635de7 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -1788,6 +1788,52 @@ static char *unix_mode_to_str(char *s, mode_t m) return s; } +/**************************************************************************** + UNIX getfacl. +****************************************************************************/ + +static int cmd_getfacl(void) +{ + pstring src, name; + uint16 major, minor; + uint32 caplow, caphigh; + char *retbuf = NULL; + + if (!SERVER_HAS_UNIX_CIFS(cli)) { + d_printf("Server doesn't support UNIX CIFS calls.\n"); + return 1; + } + + if (!cli_unix_extensions_version(cli, &major, &minor, &caplow, &caphigh)) { + d_printf("Can't get UNIX CIFS version from server.\n"); + return 1; + } + + if (!(caplow & CIFS_UNIX_POSIX_ACLS_CAP)) { + d_printf("This server supports UNIX extensions but doesn't support POSIX ACLs.\n"); + return 1; + } + + pstrcpy(src,cur_dir); + + if (!next_token_nr(NULL,name,NULL,sizeof(name))) { + d_printf("stat file\n"); + return 1; + } + + pstrcat(src,name); + + if (!cli_unix_getfacl(cli, src, &retbuf)) { + d_printf("%s getfacl file %s\n", + cli_errstr(cli), src); + return 1; + } + + /* ToDo : Print out the ACL values. */ + SAFE_FREE(retbuf); + return 0; +} + /**************************************************************************** UNIX stat. ****************************************************************************/ @@ -2355,6 +2401,7 @@ static struct {"du",cmd_du," computes the total size of the current directory",{COMPL_REMOTE,COMPL_NONE}}, {"exit",cmd_quit,"logoff the server",{COMPL_NONE,COMPL_NONE}}, {"get",cmd_get," [local name] get a file",{COMPL_REMOTE,COMPL_LOCAL}}, + {"getfacl",cmd_getfacl," get the POSIX ACL on a file (UNIX extensions only)",{COMPL_REMOTE,COMPL_LOCAL}}, {"hardlink",cmd_hardlink," create a Windows hard link",{COMPL_REMOTE,COMPL_REMOTE}}, {"help",cmd_help,"[command] give help on a command",{COMPL_NONE,COMPL_NONE}}, {"history",cmd_history,"displays the command history",{COMPL_NONE,COMPL_NONE}}, diff --git a/source3/include/trans2.h b/source3/include/trans2.h index 37b3cb24b1..55b28ae937 100644 --- a/source3/include/trans2.h +++ b/source3/include/trans2.h @@ -459,4 +459,75 @@ Offset Size Name /* ... more as we think of them :-). */ +/* SMB POSIX ACL definitions. */ +/* Wire format is (all little endian) : + +[2 bytes] - Version number. +[2 bytes] - Number of ACE entries to follow. +[2 bytes] - Number of default ACE entries to follow. +------------------------------------- +^ +| +ACE entries +| +v +------------------------------------- +^ +| +Default ACE entries +| +v +------------------------------------- + +Where an ACE entry looks like : + +[1 byte] - Entry type. + +Entry types are : + +ACL_USER_OBJ 0x01 +ACL_USER 0x02 +ACL_GROUP_OBJ 0x04 +ACL_GROUP 0x08 +ACL_MASK 0x10 +ACL_OTHER 0x20 + +[1 byte] - permissions (perm_t) + +perm_t types are : + +ACL_READ 0x04 +ACL_WRITE 0x02 +ACL_EXECUTE 0x01 + +[8 bytes] - uid/gid to apply this permission to. + +In the same format as the uid/gid fields in the other +UNIX extensions definitions. Use 0xFFFFFFFFFFFFFFFF for +the MASK and OTHER entry types. + +*/ + +/* The query/set info levels for POSIX ACLs. */ +#define SMB_QUERY_POSIX_ACL 0x204 +#define SMB_SET_POSIX_ACL 0x204 + +/* Current on the wire ACL version. */ +#define SMB_POSIX_ACL_VERSION 1 + +/* ACE entry type. */ +#define SMB_POSIX_ACL_USER_OBJ 0x01 +#define SMB_POSIX_ACL_USER 0x02 +#define SMB_POSIX_ACL_GROUP_OBJ 0x04 +#define SMB_POSIX_ACL_GROUP 0x08 +#define SMB_POSIX_ACL_MASK 0x10 +#define SMB_POSIX_ACL_OTHER 0x20 + +/* perm_t types. */ +#define SMB_POSIX_ACL_READ 0x04 +#define SMB_POSIX_ACL_WRITE 0x02 +#define SMB_POSIX_ACL_EXECUTE 0x01 + +#define SMB_POSIX_ACL_HEADER_SIZE 6 +#define SMB_POSIX_ACL_ENTRY_SIZE 10 #endif diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c index 144fc4a0c8..b616abd4d2 100644 --- a/source3/libsmb/clifile.c +++ b/source3/libsmb/clifile.c @@ -167,6 +167,54 @@ static mode_t unix_filetype_from_wire(uint32 wire_type) } } +/**************************************************************************** + Do a POSIX getfacl (UNIX extensions). +****************************************************************************/ + +BOOL cli_unix_getfacl(struct cli_state *cli, const char *name, char **retbuf) +{ + unsigned int param_len = 0; + unsigned int data_len = 0; + uint16 setup = TRANSACT2_QPATHINFO; + char param[sizeof(pstring)+6]; + char *rparam=NULL, *rdata=NULL; + char *p; + + p = param; + memset(p, 0, 6); + SSVAL(p, 0, SMB_QUERY_POSIX_ACL); + p += 6; + p += clistr_push(cli, p, name, sizeof(pstring)-6, STR_TERMINATE); + param_len = PTR_DIFF(p, param); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, /* name */ + -1, 0, /* fid, flags */ + &setup, 1, 0, /* setup, length, max */ + param, param_len, 2, /* param, length, max */ + NULL, 0, cli->max_xmit /* data, length, max */ + )) { + return False; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, ¶m_len, + &rdata, &data_len)) { + return False; + } + + if (data_len < 6) { + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return False; + } + + SAFE_FREE(rparam); + *retbuf = rdata; + + return True; +} + /**************************************************************************** Stat a file (UNIX extensions). ****************************************************************************/ diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c index 00fe189e9a..22c8bff3ba 100644 --- a/source3/libsmb/clifsinfo.c +++ b/source3/libsmb/clifsinfo.c @@ -20,6 +20,64 @@ #include "includes.h" +/**************************************************************************** + Get UNIX extensions version info. +****************************************************************************/ + +BOOL cli_unix_extensions_version(struct cli_state *cli, uint16 *pmajor, uint16 *pminor, + uint32 *pcaplow, uint32 *pcaphigh) +{ + BOOL ret = False; + uint16 setup; + char param[2]; + char *rparam=NULL, *rdata=NULL; + unsigned int rparam_count=0, rdata_count=0; + + setup = TRANSACT2_QFSINFO; + + SSVAL(param,0,SMB_QUERY_CIFS_UNIX_INFO); + + if (!cli_send_trans(cli, SMBtrans2, + NULL, + 0, 0, + &setup, 1, 0, + param, 2, 0, + NULL, 0, 560)) { + goto cleanup; + } + + if (!cli_receive_trans(cli, SMBtrans2, + &rparam, &rparam_count, + &rdata, &rdata_count)) { + goto cleanup; + } + + if (cli_is_error(cli)) { + ret = False; + goto cleanup; + } else { + ret = True; + } + + if (rdata_count < 12) { + goto cleanup; + } + + *pmajor = SVAL(rdata,0); + *pminor = SVAL(rdata,2); + *pcaplow = IVAL(rdata,4); + *pcaphigh = IVAL(rdata,8); + + /* todo: but not yet needed + * return the other stuff + */ + +cleanup: + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return ret; +} BOOL cli_get_fs_attr_info(struct cli_state *cli, uint32 *fs_attr) { diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index b61839df07..a129227215 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -2086,7 +2086,7 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned data_len = 12; SSVAL(pdata,0,CIFS_UNIX_MAJOR_VERSION); SSVAL(pdata,2,CIFS_UNIX_MINOR_VERSION); - SBIG_UINT(pdata,4,((SMB_BIG_UINT)0)); /* No capabilities for now... */ + SBIG_UINT(pdata,4,((SMB_BIG_UINT)CIFS_UNIX_POSIX_ACLS_CAP)); /* We have POSIX ACLs. */ break; case SMB_MAC_QUERY_FS_INFO: @@ -2227,8 +2227,8 @@ static int call_trans2setfsinfo(connection_struct *conn, char *inbuf, char *outb #endif /* HAVE_SYS_QUOTAS */ /**************************************************************************** - * Utility function to set bad path error. - ****************************************************************************/ + Utility function to set bad path error. +****************************************************************************/ int set_bad_path_error(int err, BOOL bad_path, char *outbuf, int def_class, uint32 def_code) { @@ -2245,6 +2245,120 @@ int set_bad_path_error(int err, BOOL bad_path, char *outbuf, int def_class, uint return UNIXERROR(def_class,def_code); } +/**************************************************************************** + Utility function to count the number of entries in a POSIX acl. +****************************************************************************/ + +static unsigned int count_acl_entries(connection_struct *conn, SMB_ACL_T posix_acl) +{ + unsigned int ace_count = 0; + int entry_id = SMB_ACL_FIRST_ENTRY; + SMB_ACL_ENTRY_T entry; + + while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) { + /* get_next... */ + if (entry_id == SMB_ACL_FIRST_ENTRY) { + entry_id = SMB_ACL_NEXT_ENTRY; + } + ace_count++; + } + return ace_count; +} + +/**************************************************************************** + Utility function to marshall a POSIX acl into wire format. +****************************************************************************/ + +static BOOL marshall_posix_acl(connection_struct *conn, char *pdata, SMB_STRUCT_STAT *pst, SMB_ACL_T posix_acl) +{ + int entry_id = SMB_ACL_FIRST_ENTRY; + SMB_ACL_ENTRY_T entry; + + while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) { + SMB_ACL_TAG_T tagtype; + SMB_ACL_PERMSET_T permset; + unsigned char perms = 0; + unsigned int own_grp; + + /* get_next... */ + if (entry_id == SMB_ACL_FIRST_ENTRY) { + entry_id = SMB_ACL_NEXT_ENTRY; + } + + if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) { + DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_TAG_TYPE failed.\n")); + return False; + } + + if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) { + DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_PERMSET failed.\n")); + return False; + } + + perms |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? SMB_POSIX_ACL_READ : 0); + perms |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? SMB_POSIX_ACL_WRITE : 0); + perms |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? SMB_POSIX_ACL_EXECUTE : 0); + + SCVAL(pdata,1,perms); + + switch (tagtype) { + case SMB_ACL_USER_OBJ: + SCVAL(pdata,0,SMB_POSIX_ACL_USER_OBJ); + own_grp = (unsigned int)pst->st_uid; + SIVAL(pdata,2,own_grp); + SIVAL(pdata,6,0); + break; + case SMB_ACL_USER: + { + uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry); + if (!puid) { + DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n")); + } + own_grp = (unsigned int)*puid; + SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype); + SCVAL(pdata,0,SMB_POSIX_ACL_USER); + SIVAL(pdata,2,own_grp); + SIVAL(pdata,6,0); + break; + } + case SMB_ACL_GROUP_OBJ: + SCVAL(pdata,0,SMB_POSIX_ACL_GROUP_OBJ); + own_grp = (unsigned int)pst->st_gid; + SIVAL(pdata,2,own_grp); + SIVAL(pdata,6,0); + break; + case SMB_ACL_GROUP: + { + gid_t *pgid= (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry); + if (!pgid) { + DEBUG(0,("marshall_posix_acl: SMB_VFS_SYS_ACL_GET_QUALIFIER failed.\n")); + } + own_grp = (unsigned int)*pgid; + SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype); + SCVAL(pdata,0,SMB_POSIX_ACL_GROUP); + SIVAL(pdata,2,own_grp); + SIVAL(pdata,6,0); + break; + } + case SMB_ACL_MASK: + SCVAL(pdata,0,SMB_POSIX_ACL_MASK); + SIVAL(pdata,2,0xFFFFFFFF); + SIVAL(pdata,6,0xFFFFFFFF); + break; + case SMB_ACL_OTHER: + SCVAL(pdata,0,SMB_POSIX_ACL_OTHER); + SIVAL(pdata,2,0xFFFFFFFF); + SIVAL(pdata,6,0xFFFFFFFF); + break; + default: + DEBUG(0,("marshall_posix_acl: unknown tagtype.\n")); + return False; + } + } + + return True; +} + /**************************************************************************** Reply to a TRANS2_QFILEPATHINFO or TRANSACT2_QFILEINFO (query file info by file name or file id). @@ -2800,6 +2914,83 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char *inbuf, char * break; } + case SMB_QUERY_POSIX_ACL: + { + SMB_ACL_T file_acl = NULL; + SMB_ACL_T dir_acl = NULL; + uint16 num_file_acls = 0; + uint16 num_dir_acls = 0; + + if (fsp && !fsp->is_directory && (fsp->fd != -1)) { + file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fd); + } else { + file_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS); + } + + if (file_acl == NULL && no_acl_syscall_error(errno)) { + DEBUG(5,("call_trans2qfilepathinfo: ACLs not implemented on filesystem containing %s\n", + fname )); + return ERROR_NT(NT_STATUS_NOT_IMPLEMENTED); + } + + if (S_ISDIR(sbuf.st_mode)) { + if (fsp && fsp->is_directory) { + dir_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT); + } else { + dir_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fname, SMB_ACL_TYPE_DEFAULT); + } + dir_acl = free_empty_sys_acl(conn, dir_acl); + } + + num_file_acls = count_acl_entries(conn, file_acl); + num_dir_acls = count_acl_entries(conn, dir_acl); + + if ( data_size < (num_file_acls + num_dir_acls)*SMB_POSIX_ACL_ENTRY_SIZE + SMB_POSIX_ACL_HEADER_SIZE) { + DEBUG(5,("call_trans2qfilepathinfo: data_size too small (%u) need %u\n", + data_size, + (unsigned int)((num_file_acls + num_dir_acls)*SMB_POSIX_ACL_ENTRY_SIZE + + SMB_POSIX_ACL_HEADER_SIZE) )); + if (file_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl); + } + if (dir_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl); + } + return ERROR_NT(NT_STATUS_BUFFER_TOO_SMALL); + } + + SSVAL(pdata,0,SMB_POSIX_ACL_VERSION); + SSVAL(pdata,2,num_file_acls); + SSVAL(pdata,4,num_dir_acls); + if (!marshall_posix_acl(conn, pdata + SMB_POSIX_ACL_HEADER_SIZE, &sbuf, file_acl)) { + if (file_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl); + } + if (dir_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl); + } + return ERROR_NT(NT_STATUS_INTERNAL_ERROR); + } + if (!marshall_posix_acl(conn, pdata + SMB_POSIX_ACL_HEADER_SIZE + (num_file_acls*SMB_POSIX_ACL_ENTRY_SIZE), &sbuf, dir_acl)) { + if (file_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl); + } + if (dir_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl); + } + return ERROR_NT(NT_STATUS_INTERNAL_ERROR); + } + + if (file_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl); + } + if (dir_acl) { + SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl); + } + data_size = (num_file_acls + num_dir_acls)*SMB_POSIX_ACL_ENTRY_SIZE + SMB_POSIX_ACL_HEADER_SIZE; + break; + } + default: return ERROR_DOS(ERRDOS,ERRunknownlevel); } -- cgit