summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimo Sorce <idra@samba.org>2004-03-01 16:10:28 +0000
committerSimo Sorce <idra@samba.org>2004-03-01 16:10:28 +0000
commiteebc94d84af736bb1fdd8e0c511237b0da978e7a (patch)
tree3d39c50ac039a7fc56d677b90e60421f7d6fc878
parentf986f33aa13f8672e2154f29906f657c023777dc (diff)
downloadsamba-eebc94d84af736bb1fdd8e0c511237b0da978e7a.tar.gz
samba-eebc94d84af736bb1fdd8e0c511237b0da978e7a.tar.bz2
samba-eebc94d84af736bb1fdd8e0c511237b0da978e7a.zip
Ok here it is my latest work on privileges
This patch add privilege support for samba Currently it is implemented only for tdbsam backend but estending it to other sam backends is straightforward. I must make a big thank to JFM for his teachings on the matter and the functions at the base of this work. At thye moment only samr_create_user honours SeAddUsersPrivilege and SeMachineAccountPrivilege to permit any user to add machines and/or users to the server. The command "net priv" has been provided to manipulate the privileges database. There are still many things to do (like support in "net rpc vampire") but the working core is here. Feel free to comment/extend on this work. Of course I will deny that any bug may affect this code :-) Simo. This patch adds also my patch about add share command enhancements. (This used to be commit 7a78c3605e203bd8e0d7ae244605f076a5d0b0bc)
-rw-r--r--source3/Makefile.in2
-rw-r--r--source3/auth/auth_util.c22
-rw-r--r--source3/groupdb/mapping.c11
-rw-r--r--source3/include/auth.h1
-rw-r--r--source3/include/includes.h4
-rw-r--r--source3/include/passdb.h20
-rw-r--r--source3/include/privileges.h47
-rw-r--r--source3/include/smb.h3
-rw-r--r--source3/lib/privileges.c79
-rw-r--r--source3/param/loadparm.c2
-rw-r--r--source3/passdb/pdb_interface.c157
-rw-r--r--source3/passdb/pdb_tdb.c353
-rw-r--r--source3/rpc_server/srv_lsa_nt.c4
-rw-r--r--source3/rpc_server/srv_pipe_hnd.c2
-rw-r--r--source3/rpc_server/srv_samr_nt.c72
-rw-r--r--source3/rpc_server/srv_srvsvc_nt.c70
-rw-r--r--source3/sam/gums.c63
-rw-r--r--source3/smbd/chgpasswd.c2
-rw-r--r--source3/smbd/conn.c8
-rw-r--r--source3/smbd/password.c6
-rw-r--r--source3/smbd/sec_ctx.c27
-rw-r--r--source3/smbd/service.c4
-rw-r--r--source3/smbd/uid.c18
-rw-r--r--source3/utils/net.c1
24 files changed, 838 insertions, 140 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in
index 25a0e45f05..5792b00264 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -514,7 +514,7 @@ NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_ads_cldap.o utils/net_help.o \
utils/net_rap.o utils/net_rpc.o utils/net_rpc_samsync.o \
utils/net_rpc_join.o utils/net_time.o utils/net_lookup.o \
utils/net_cache.o utils/net_groupmap.o utils/net_idmap.o \
- utils/net_status.o
+ utils/net_status.o utils/net_privileges.o
NET_OBJ = $(NET_OBJ1) $(PARAM_OBJ) $(SECRETS_OBJ) $(LIBSMB_OBJ) \
$(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
diff --git a/source3/auth/auth_util.c b/source3/auth/auth_util.c
index 4a23593936..018d3b1851 100644
--- a/source3/auth/auth_util.c
+++ b/source3/auth/auth_util.c
@@ -827,6 +827,23 @@ static NTSTATUS add_user_groups(auth_serversupplied_info **server_info,
}
/***************************************************************************
+Fill a server_info struct from a SAM_ACCOUNT with its privileges
+***************************************************************************/
+
+static NTSTATUS add_privileges(auth_serversupplied_info **server_info)
+{
+ PRIVILEGE_SET *privs = NULL;
+
+ init_privilege(&privs);
+ if (!pdb_get_privilege_set((*server_info)->ptok, privs))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ (*server_info)->privs = privs;
+
+ return NT_STATUS_OK;
+}
+
+/***************************************************************************
Make (and fill) a user_info struct from a SAM_ACCOUNT
***************************************************************************/
@@ -860,6 +877,11 @@ NTSTATUS make_server_info_sam(auth_serversupplied_info **server_info,
return nt_status;
}
+ if (!NT_STATUS_IS_OK(nt_status = add_privileges(server_info))) {
+ free_server_info(server_info);
+ return nt_status;
+ }
+
(*server_info)->sam_fill_level = SAM_FILL_ALL;
DEBUG(5,("make_server_info_sam: made server info for user %s -> %s\n",
pdb_get_username(sampass),
diff --git a/source3/groupdb/mapping.c b/source3/groupdb/mapping.c
index c153ff258d..ef0708b888 100644
--- a/source3/groupdb/mapping.c
+++ b/source3/groupdb/mapping.c
@@ -29,17 +29,6 @@ static TDB_CONTEXT *tdb; /* used for driver files */
#define GROUP_PREFIX "UNIXGROUP/"
#define ALIASMEM_PREFIX "ALIASMEMBERS/"
-PRIVS privs[] = {
- {SE_PRIV_NONE, "no_privs", "No privilege" }, /* this one MUST be first */
- {SE_PRIV_ADD_MACHINES, "SeMachineAccountPrivilege", "Add workstations to the domain" },
- {SE_PRIV_SEC_PRIV, "SeSecurityPrivilege", "Manage the audit logs" },
- {SE_PRIV_TAKE_OWNER, "SeTakeOwnershipPrivilege", "Take ownership of file" },
- {SE_PRIV_ADD_USERS, "SaAddUsers", "Add users to the domain - Samba" },
- {SE_PRIV_PRINT_OPERATOR, "SaPrintOp", "Add or remove printers - Samba" },
- {SE_PRIV_ALL, "SaAllPrivs", "all privileges" }
-};
-
-
/****************************************************************************
dump the mapping group mapping to a text file
****************************************************************************/
diff --git a/source3/include/auth.h b/source3/include/auth.h
index ecf4d539d8..27cdc1e3f5 100644
--- a/source3/include/auth.h
+++ b/source3/include/auth.h
@@ -86,6 +86,7 @@ typedef struct auth_serversupplied_info
/* NT group information taken from the info3 structure */
NT_USER_TOKEN *ptok;
+ PRIVILEGE_SET *privs;
DATA_BLOB nt_session_key;
DATA_BLOB lm_session_key;
diff --git a/source3/include/includes.h b/source3/include/includes.h
index db060907e4..a594e309df 100644
--- a/source3/include/includes.h
+++ b/source3/include/includes.h
@@ -783,6 +783,8 @@ extern int errno;
#include "version.h"
+#include "privileges.h"
+
#include "smb.h"
#include "nameserv.h"
@@ -791,8 +793,6 @@ extern int errno;
#include "byteorder.h"
-#include "privileges.h"
-
#include "rpc_creds.h"
#include "mapping.h"
diff --git a/source3/include/passdb.h b/source3/include/passdb.h
index 668bbcc2de..d471eb53f3 100644
--- a/source3/include/passdb.h
+++ b/source3/include/passdb.h
@@ -362,6 +362,16 @@ typedef struct pdb_context
NTSTATUS (*pdb_update_trust_passwd)(struct pdb_context *context, SAM_TRUST_PASSWD* trust);
NTSTATUS (*pdb_delete_trust_passwd)(struct pdb_context *context, SAM_TRUST_PASSWD* trust);
+
+ /* privileges functions */
+
+ NTSTATUS (*pdb_add_sid_to_privilege)(struct pdb_context *context, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_remove_sid_from_privilege)(struct pdb_context *context, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*pdb_get_privilege_set)(struct pdb_context *context, NT_USER_TOKEN *token, PRIVILEGE_SET *privs);
+
+ NTSTATUS (*pdb_get_privilege_entry)(struct pdb_context *context, const char *privname, char **sid_list);
void (*free_fn)(struct pdb_context **);
@@ -467,6 +477,16 @@ typedef struct pdb_methods
NTSTATUS (*delete_trust_passwd)(struct pdb_methods *methods, const SAM_TRUST_PASSWD* trust);
+ /* privileges functions */
+
+ NTSTATUS (*add_sid_to_privilege)(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*remove_sid_from_privilege)(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid);
+
+ NTSTATUS (*get_privilege_set)(struct pdb_methods *methods, NT_USER_TOKEN *token, PRIVILEGE_SET *privs);
+
+ NTSTATUS (*get_privilege_entry)(struct pdb_methods *methods, const char *privname, char **sid_list);
+
} PDB_METHODS;
typedef NTSTATUS (*pdb_init_function)(struct pdb_context *,
diff --git a/source3/include/privileges.h b/source3/include/privileges.h
index b7e1b44c2a..289afa234e 100644
--- a/source3/include/privileges.h
+++ b/source3/include/privileges.h
@@ -23,15 +23,39 @@
#ifndef PRIVILEGES_H
#define PRIVILEGES_H
-#define PRIV_ALL_INDEX 5
+#define PRIV_ALL_INDEX 30
-#define SE_PRIV_NONE 0x0000
-#define SE_PRIV_ADD_MACHINES 0x0006
-#define SE_PRIV_SEC_PRIV 0x0008
-#define SE_PRIV_TAKE_OWNER 0x0009
-#define SE_PRIV_ADD_USERS 0xff01
-#define SE_PRIV_PRINT_OPERATOR 0xff03
-#define SE_PRIV_ALL 0xffff
+#define SE_NONE 0
+#define SE_ASSIGN_PRIMARY_TOKEN 1
+#define SE_CREATE_TOKEN 2
+#define SE_LOCK_MEMORY 3
+#define SE_INCREASE_QUOTA 4
+#define SE_UNSOLICITED_INPUT 5
+#define SE_MACHINE_ACCOUNT 6
+#define SE_TCB 7
+#define SE_SECURITY 8
+#define SE_TAKE_OWNERSHIP 9
+#define SE_LOAD_DRIVER 10
+#define SE_SYSTEM_PROFILE 11
+#define SE_SYSTEM_TIME 12
+#define SE_PROF_SINGLE_PROCESS 13
+#define SE_INC_BASE_PRIORITY 14
+#define SE_CREATE_PAGEFILE 15
+#define SE_CREATE_PERMANENT 16
+#define SE_BACKUP 17
+#define SE_RESTORE 18
+#define SE_SHUTDOWN 19
+#define SE_DEBUG 20
+#define SE_AUDIT 21
+#define SE_SYSTEM_ENVIRONMENT 22
+#define SE_CHANGE_NOTIFY 23
+#define SE_REMOTE_SHUTDOWN 24
+#define SE_UNDOCK 25
+#define SE_SYNC_AGENT 26
+#define SE_ENABLE_DELEGATION 27
+#define SE_PRINT_OPERATOR 28
+#define SE_ADD_USERS 29
+#define SE_ALL_PRIVS 0xffff
#define PR_NONE 0x0000
#define PR_LOG_ON_LOCALLY 0x0001
@@ -39,6 +63,11 @@
#define PR_LOG_ON_BATCH_JOB 0x0004
#define PR_LOG_ON_SERVICE 0x0010
+#ifndef _BOOL
+typedef int BOOL;
+#define _BOOL /* So we don't typedef BOOL again in vfs.h */
+#endif
+
typedef struct LUID
{
uint32 low;
@@ -49,7 +78,7 @@ typedef struct LUID_ATTR
{
LUID luid;
uint32 attr;
-} LUID_ATTR ;
+} LUID_ATTR;
typedef struct privilege_set
{
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 5cd5e71f74..ab6f4c70d6 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -500,6 +500,7 @@ typedef struct connection_struct
int ngroups;
gid_t *groups;
NT_USER_TOKEN *nt_user_token;
+ PRIVILEGE_SET *privs;
time_t lastused;
BOOL used;
@@ -519,6 +520,7 @@ struct current_user
int ngroups;
gid_t *groups;
NT_USER_TOKEN *nt_user_token;
+ PRIVILEGE_SET *privs;
};
/* Defines for the sent_oplock_break field above. */
@@ -1546,6 +1548,7 @@ typedef struct user_struct
gid_t *groups;
NT_USER_TOKEN *nt_user_token;
+ PRIVILEGE_SET *privs;
DATA_BLOB session_key;
diff --git a/source3/lib/privileges.c b/source3/lib/privileges.c
index b9d4df301d..4bcf5e3b36 100644
--- a/source3/lib/privileges.c
+++ b/source3/lib/privileges.c
@@ -26,6 +26,43 @@
#define ALLOC_CHECK(ptr, err, label, str) do { if ((ptr) == NULL) { DEBUG(0, ("%s: out of memory!\n", str)); err = NT_STATUS_NO_MEMORY; goto label; } } while(0)
#define NTSTATUS_CHECK(err, label, str1, str2) do { if (!NT_STATUS_IS_OK(err)) { DEBUG(0, ("%s: %s failed!\n", str1, str2)); } } while(0)
+
+PRIVS privs[] = {
+ {SE_NONE, "no_privs", "No privilege"}, /* this one MUST be first */
+ {SE_CREATE_TOKEN, "SeCreateTokenPrivilege", "Create Token"},
+ {SE_ASSIGN_PRIMARY_TOKEN, "SeAssignPrimaryTokenPrivilege", "Assign Primary Token"},
+ {SE_LOCK_MEMORY, "SeLockMemoryPrivilege", "Lock Memory"},
+ {SE_INCREASE_QUOTA, "SeIncreaseQuotaPrivilege", "Increase Quota"},
+ {SE_UNSOLICITED_INPUT, "eUnsolicitedInputPrivilege", "Unsolicited Input"},
+ {SE_MACHINE_ACCOUNT, "SeMachineAccountPrivilege", "Can add Machine Accounts to the Domain"},
+ {SE_TCB, "SeTcbPrivilege", "TCB"},
+ {SE_SECURITY, "SeSecurityPrivilege", "Security Privilege"},
+ {SE_TAKE_OWNERSHIP, "SeTakeOwnershipPrivilege", "Take Ownership Privilege"},
+ {SE_LOAD_DRIVER, "SeLocalDriverPrivilege", "Local Driver Privilege"},
+ {SE_SYSTEM_PROFILE, "SeSystemProfilePrivilege", "System Profile Privilege"},
+ {SE_SYSTEM_TIME, "SeSystemtimePrivilege", "System Time"},
+ {SE_PROF_SINGLE_PROCESS, "SeProfileSingleProcessPrivilege", "Profile Single Process Privilege"},
+ {SE_INC_BASE_PRIORITY, "SeIncreaseBasePriorityPrivilege", "Increase Base Priority Privilege"},
+ {SE_CREATE_PAGEFILE, "SeCreatePagefilePrivilege", "Create Pagefile Privilege"},
+ {SE_CREATE_PERMANENT, "SeCreatePermanentPrivilege", "Create Permanent"},
+ {SE_BACKUP, "SeBackupPrivilege", "Backup Privilege"},
+ {SE_RESTORE, "SeRestorePrivilege", "Restore Privilege"},
+ {SE_SHUTDOWN, "SeShutdownPrivilege", "Shutdown Privilege"},
+ {SE_DEBUG, "SeDebugPrivilege", "Debug Privilege"},
+ {SE_AUDIT, "SeAuditPrivilege", "Audit"},
+ {SE_SYSTEM_ENVIRONMENT, "SeSystemEnvironmentPrivilege", "System Environment Privilege"},
+ {SE_CHANGE_NOTIFY, "SeChangeNotifyPrivilege", "Change Notify"},
+ {SE_REMOTE_SHUTDOWN, "SeRemoteShutdownPrivilege", "Remote Shutdown Privilege"},
+ {SE_UNDOCK, "SeUndockPrivilege", "Undock"},
+ {SE_SYNC_AGENT, "SeSynchronizationAgentPrivilege", "Synchronization Agent"},
+ {SE_ENABLE_DELEGATION, "SeEnableDelegationPrivilege", "Enable Delegation"},
+ {SE_PRINT_OPERATOR, "SePrintOperatorPrivilege", "Printer Operator"},
+ {SE_ADD_USERS, "SeAddUsersPrivilege", "Add Users"},
+ {SE_ALL_PRIVS, "SeAllPrivileges", "All Privileges"}
+};
+
+
+
/****************************************************************************
Check if a user is a mapped group.
@@ -170,6 +207,27 @@ done:
return ret;
}
+NTSTATUS add_privilege_by_name(PRIVILEGE_SET *priv_set, const char *name)
+{
+ int e;
+
+ for (e = 0; privs[e].se_priv != SE_ALL_PRIVS; e++) {
+ if (StrCaseCmp(privs[e].priv, name) == 0) {
+ LUID_ATTR la;
+
+ la.attr = 0;
+ la.luid.high = 0;
+ la.luid.low = privs[e].se_priv;
+
+ return add_privilege(priv_set, la);
+ }
+ }
+
+ DEBUG(1, ("add_privilege_by_name: No Such Privilege Found (%s)\n", name));
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
/****************************************************************************
add all the privileges to a privilege array
****************************************************************************/
@@ -182,15 +240,15 @@ NTSTATUS add_all_privilege(PRIVILEGE_SET *priv_set)
set.luid.high = 0;
/* TODO: set a proper list of privileges */
- set.luid.low = SE_PRIV_ADD_USERS;
+ set.luid.low = SE_ADD_USERS;
result = add_privilege(priv_set, set);
NTSTATUS_CHECK(result, done, "add_all_privilege", "add_privilege");
- set.luid.low = SE_PRIV_ADD_MACHINES;
+ set.luid.low = SE_MACHINE_ACCOUNT;
result = add_privilege(priv_set, set);
NTSTATUS_CHECK(result, done, "add_all_privilege", "add_privilege");
- set.luid.low = SE_PRIV_PRINT_OPERATOR;
+ set.luid.low = SE_PRINT_OPERATOR;
result = add_privilege(priv_set, set);
NTSTATUS_CHECK(result, done, "add_all_privilege", "add_privilege");
@@ -329,7 +387,7 @@ NTSTATUS dup_priv_set(PRIVILEGE_SET *new_priv_set, PRIVILEGE_SET *priv_set)
old_set = priv_set->set;
- new_set = (LUID_ATTR *)talloc(new_priv_set->mem_ctx, (priv_set->count - 1) * (sizeof(LUID_ATTR)));
+ new_set = (LUID_ATTR *)talloc(new_priv_set->mem_ctx, (priv_set->count) * (sizeof(LUID_ATTR)));
ALLOC_CHECK(new_set, ret, done, "dup_priv_set");
for (i=0; i < priv_set->count; i++) {
@@ -348,3 +406,16 @@ NTSTATUS dup_priv_set(PRIVILEGE_SET *new_priv_set, PRIVILEGE_SET *priv_set)
done:
return ret;
}
+
+
+NTSTATUS user_has_privilege(struct current_user *user, uint32 privilege)
+{
+ LUID_ATTR set;
+
+ set.attr = 0;
+ set.luid.high = 0;
+ set.luid.low = privilege;
+
+ return check_priv_in_privilege(user->privs, set);
+}
+
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index ae9d3ae945..58f4919465 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -3857,7 +3857,7 @@ BOOL lp_load(const char *pszFname, BOOL global_only, BOOL save_defaults,
if (iServiceIndex >= 0)
bRetval = service_ok(iServiceIndex);
- if (lp_config_backend()) {
+ if (*(lp_config_backend())) {
modconf_init(lp_config_backend());
modconf_load(do_section, do_parameter);
}
diff --git a/source3/passdb/pdb_interface.c b/source3/passdb/pdb_interface.c
index 2005885c45..180db58c2d 100644
--- a/source3/passdb/pdb_interface.c
+++ b/source3/passdb/pdb_interface.c
@@ -587,6 +587,86 @@ static NTSTATUS context_delete_trust_passwd(struct pdb_context *context,
return trust->methods->delete_trust_passwd(trust->methods, trust);
}
+static NTSTATUS context_add_sid_to_privilege(struct pdb_context *context, const char *priv_name, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->add_sid_to_privilege(curmethods, priv_name, sid))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_remove_sid_from_privilege(struct pdb_context *context, const char *priv_name, const DOM_SID *sid)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->remove_sid_from_privilege(curmethods, priv_name, sid))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_get_privilege_set(struct pdb_context *context, NT_USER_TOKEN *token, PRIVILEGE_SET *privset)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->get_privilege_set(curmethods, token, privset))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
+static NTSTATUS context_get_privilege_entry(struct pdb_context *context, const char *privname, char **sid_list)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+
+ struct pdb_methods *curmethods;
+ if ((!context)) {
+ DEBUG(0, ("invalid pdb_context specified!\n"));
+ return ret;
+ }
+ curmethods = context->pdb_methods;
+ while (curmethods){
+ if (NT_STATUS_IS_OK(ret = curmethods->get_privilege_entry(curmethods, privname, sid_list))) {
+ return ret;
+ }
+ curmethods = curmethods->next;
+ }
+
+ return ret;
+}
+
/******************************************************************
Free and cleanup a pdb context, any associated data and anything
that the attached modules might have associated.
@@ -711,6 +791,10 @@ static NTSTATUS make_pdb_context(struct pdb_context **context)
(*context)->pdb_add_trust_passwd = context_add_trust_passwd;
(*context)->pdb_update_trust_passwd = context_update_trust_passwd;
(*context)->pdb_delete_trust_passwd = context_delete_trust_passwd;
+ (*context)->pdb_add_sid_to_privilege = context_add_sid_to_privilege;
+ (*context)->pdb_remove_sid_from_privilege = context_remove_sid_from_privilege;
+ (*context)->pdb_get_privilege_set = context_get_privilege_set;
+ (*context)->pdb_get_privilege_entry = context_get_privilege_entry;
(*context)->free_fn = free_pdb_context;
@@ -1072,6 +1156,54 @@ BOOL pdb_enum_alias_memberships(const DOM_SID *sid,
aliases, num));
}
+BOOL pdb_add_sid_to_privilege(char *priv_name, DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_add_sid_to_privilege(pdb_context, priv_name, sid));
+}
+
+BOOL pdb_remove_sid_from_privilege(char *priv_name, DOM_SID *sid)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_remove_sid_from_privilege(pdb_context, priv_name, sid));
+}
+
+BOOL pdb_get_privilege_set(NT_USER_TOKEN *token, PRIVILEGE_SET *privset)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_get_privilege_set(pdb_context, token, privset));
+}
+
+BOOL pdb_get_privilege_entry(const char *privname, char **sid_list)
+{
+ struct pdb_context *pdb_context = pdb_get_static_context(False);
+
+ if (!pdb_context) {
+ return False;
+ }
+
+ return NT_STATUS_IS_OK(pdb_context->
+ pdb_get_privilege_entry(pdb_context, privname, sid_list));
+}
+
/***************************************************************
Initialize the static context (at smbd startup etc).
@@ -1155,6 +1287,26 @@ static NTSTATUS pdb_default_delete_trust_passwd(struct pdb_methods *methods, con
return NT_STATUS_NOT_IMPLEMENTED;
}
+static NTSTATUS pdb_default_add_sid_to_privilege(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_remove_sid_from_privilege(struct pdb_methods *methods, const char *priv_name, const DOM_SID *sid)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_get_privilege_set(struct pdb_methods *methods, NT_USER_TOKEN *token, PRIVILEGE_SET *privset)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+static NTSTATUS pdb_default_get_privilege_entry(struct pdb_methods *methods, const char *privname, char **sid_list)
+{
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
NTSTATUS make_pdb_methods(TALLOC_CTX *mem_ctx, PDB_METHODS **methods)
{
@@ -1193,5 +1345,10 @@ NTSTATUS make_pdb_methods(TALLOC_CTX *mem_ctx, PDB_METHODS **methods)
(*methods)->update_trust_passwd = pdb_default_update_trust_passwd;
(*methods)->delete_trust_passwd = pdb_default_delete_trust_passwd;
+ (*methods)->add_sid_to_privilege = pdb_default_add_sid_to_privilege;
+ (*methods)->remove_sid_from_privilege = pdb_default_remove_sid_from_privilege;
+ (*methods)->get_privilege_set = pdb_default_get_privilege_set;
+ (*methods)->get_privilege_entry = pdb_default_get_privilege_entry;
+
return NT_STATUS_OK;
}
diff --git a/source3/passdb/pdb_tdb.c b/source3/passdb/pdb_tdb.c
index f54463e158..39de791b07 100644
--- a/source3/passdb/pdb_tdb.c
+++ b/source3/passdb/pdb_tdb.c
@@ -42,6 +42,7 @@ static int tdbsam_debug_level = DBGC_ALL;
#define PASSDB_FILE_NAME "passdb.tdb"
#define USERPREFIX "USER_"
#define RIDPREFIX "RID_"
+#define PRIVPREFIX "PRIV_"
#define tdbsamver_t int32
struct tdbsam_privates {
@@ -895,6 +896,354 @@ static NTSTATUS tdbsam_delete_trust_passwd(struct pdb_methods *methods, const SA
}
+/***************************************************************************
+ Add sid to privilege
+****************************************************************************/
+
+static NTSTATUS tdbsam_add_sid_to_privilege(struct pdb_methods *my_methods, const char *priv_name, const DOM_SID *sid)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ fstring keystr;
+ fstring name;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ fstring sid_str;
+ char *sid_list = NULL, *s = NULL;
+ size_t str_size;
+ int flag;
+
+ /* invalidate the existing TDB iterator if it is open */
+
+ if (tdb_state->passwd_tdb) {
+ tdb_close(tdb_state->passwd_tdb);
+ tdb_state->passwd_tdb = NULL;
+ }
+
+ /* open the account TDB passwd*/
+
+ pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR | O_CREAT);
+
+ if (!pwd_tdb) {
+ DEBUG(0, ("tdb_add_sid_to_privilege: Unable to open TDB passwd (%s)!\n",
+ tdb_state->tdbsam_location));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* setup the PRIV index key */
+ fstrcpy(name, priv_name);
+ strlower_m(name);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", PRIVPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* check if the privilege already exist in the database */
+
+ /* get the record */
+ data = tdb_fetch (pwd_tdb, key);
+
+ if (data.dptr) {
+ /* check the list is not empty */
+ if (*(data.dptr)) {
+ sid_list = strdup(data.dptr);
+ if (!sid_list) {
+ DEBUG(0, ("tdbsam_add_sid_to_privilege: Out of Memory!\n"));
+ goto done;
+ }
+ }
+ SAFE_FREE(data.dptr);
+
+ flag = TDB_MODIFY;
+ } else {
+ /* if privilege does not exist create one */
+ flag = TDB_INSERT;
+ }
+
+ /* add the given sid */
+ sid_to_string(sid_str, sid);
+
+ if (sid_list) {
+ str_size = strlen(sid_list) + strlen(sid_str) + 2;
+ s = realloc(sid_list, str_size);
+ if (!s) {
+ DEBUG(0, ("tdbsam_add_sid_to_privilege: Out of Memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ sid_list = s;
+ s = &sid_list[strlen(sid_list)];
+ snprintf(s, strlen(sid_str) + 2, ",%s", sid_str);
+
+ } else {
+ sid_list = strdup(sid_str);
+ if (!sid_list) {
+ DEBUG(0, ("tdbsam_add_sid_to_privilege: Out of Memory!\n"));
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ }
+
+ /* copy the PRIVILEGE struct into a BYTE buffer for storage */
+ data.dsize = strlen(sid_list) + 1;
+ data.dptr = sid_list;
+
+ /* add the account */
+ if (tdb_store(pwd_tdb, key, data, flag) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify passwd TDB!"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ /* cleanup */
+ tdb_close (pwd_tdb);
+ SAFE_FREE(sid_list);
+
+ return (ret);
+}
+
+/***************************************************************************
+ Reomve sid to privilege
+****************************************************************************/
+
+static NTSTATUS tdbsam_remove_sid_from_privilege(struct pdb_methods *my_methods, const char *priv_name, const DOM_SID *sid)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ fstring keystr;
+ fstring name;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ fstring sid_str;
+ char *sid_list = NULL, *s = NULL;
+
+ /* invalidate the existing TDB iterator if it is open */
+
+ if (tdb_state->passwd_tdb) {
+ tdb_close(tdb_state->passwd_tdb);
+ tdb_state->passwd_tdb = NULL;
+ }
+
+ /* open the account TDB passwd*/
+
+ pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDWR | O_CREAT);
+
+ if (!pwd_tdb) {
+ DEBUG(0, ("tdbsam_remove_sid_from_privilege: Unable to open TDB passwd (%s)!\n",
+ tdb_state->tdbsam_location));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* setup the PRIV index key */
+ fstrcpy(name, priv_name);
+ strlower_m(name);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", PRIVPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ /* check if the privilege already exist in the database */
+
+ /* get the record */
+ data = tdb_fetch (pwd_tdb, key);
+
+ /* if privilege does not exist, just leave */
+ if (!data.dptr) {
+ ret = NT_STATUS_OK;
+ goto done;
+ }
+
+ if (data.dptr) {
+ sid_list = strdup(data.dptr);
+ if (!sid_list) {
+ DEBUG(0, ("tdbsam_remove_sid_from_privilege: Out of Memory!\n"));
+ goto done;
+ }
+ SAFE_FREE(data.dptr);
+ }
+
+ /* remove the given sid */
+ sid_to_string(sid_str, sid);
+
+ s = strstr(sid_list, sid_str);
+ if (s) {
+ char *p;
+ p = strstr(s, ",");
+ if (p) {
+ size_t l = strlen(sid_list) + 1 - (s - sid_list);
+ memmove(s, ++p, l);
+ } else {
+ if (s != sid_list)
+ s--;
+ *s = '\0';
+ }
+ } else {
+ /* sid not found */
+ ret = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* copy the PRIVILEGE struct into a BYTE buffer for storage */
+ data.dsize = strlen(sid_list) + 1;
+ data.dptr = sid_list;
+
+ /* add the account */
+ if (tdb_store(pwd_tdb, key, data, TDB_MODIFY) != TDB_SUCCESS) {
+ DEBUG(0, ("Unable to modify passwd TDB!"));
+ DEBUGADD(0, (" Error: %s", tdb_errorstr(pwd_tdb)));
+ DEBUGADD(0, (" occured while storing the main record (%s)\n", keystr));
+ goto done;
+ }
+
+ ret = NT_STATUS_OK;
+
+done:
+ /* cleanup */
+ tdb_close (pwd_tdb);
+ SAFE_FREE(sid_list);
+
+ return (ret);
+}
+
+/***************************************************************************
+ get the privilege list for the given token
+****************************************************************************/
+
+struct priv_traverse {
+ char **sid_list;
+ PRIVILEGE_SET *privset;
+};
+
+static int tdbsam_traverse_privilege(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *state)
+{
+ struct priv_traverse *pt = (struct priv_traverse *)state;
+ int prefixlen = strlen(PRIVPREFIX);
+
+ if (strncmp(key.dptr, PRIVPREFIX, prefixlen) == 0) {
+
+ /* add to privilege_set if any of the sid in the token
+ * is contained in the privilege */
+ int i;
+
+ for(i=0; pt->sid_list[i] != NULL; i++) {
+ char *c, *s;
+ int len;
+
+ s = data.dptr;
+ while (c = strchr(s, ',')) {
+ len = MAX((c - s), strlen(pt->sid_list[i]));
+ if (strncmp(s, pt->sid_list[i], len) == 0) {
+ DEBUG(10, ("sid [%s] found in users sid list\n", pt->sid_list[i]));
+ DEBUG(10, ("adding privilege [%s] to the users privilege list\n", &(key.dptr[prefixlen])));
+ add_privilege_by_name(pt->privset, &(key.dptr[prefixlen]));
+ return 0;
+ }
+ s = c + 1;
+ }
+ len = MAX(strlen(s), strlen(pt->sid_list[i]));
+ if (strncmp(s, pt->sid_list[i], len) == 0) {
+ DEBUG(10, ("sid [%s] found in users sid list\n", pt->sid_list[i]));
+ DEBUG(10, ("adding privilege [%s] to the users privilege list\n", &(key.dptr[prefixlen])));
+ add_privilege_by_name(pt->privset, &(key.dptr[prefixlen]));
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static NTSTATUS tdbsam_get_privilege_set(struct pdb_methods *my_methods, NT_USER_TOKEN *token, PRIVILEGE_SET *privset)
+{
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ struct priv_traverse pt;
+ fstring sid_str;
+ char **sid_list;
+ int i;
+
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY )))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ sid_list = (char **)malloc(sizeof(char *) * (token->num_sids + 1));
+ for (i = 0; i < token->num_sids; i++) {
+ sid_to_string(sid_str, &token->user_sids[i]);
+ sid_list[i] = strdup(sid_str);
+ if ( ! sid_list[i]) {
+ ret = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ sid_list[i] = NULL;
+
+ pt.sid_list = sid_list;
+ pt.privset = privset;
+ tdb_traverse(pwd_tdb, tdbsam_traverse_privilege, &pt);
+
+ ret = NT_STATUS_OK;
+
+done:
+ i = 0;
+ while (sid_list[i]) {
+ free(sid_list[i]);
+ i++;
+ }
+ free(sid_list);
+
+ tdb_close(pwd_tdb);
+
+ return ret;
+}
+
+static NTSTATUS tdbsam_get_privilege_entry(struct pdb_methods *my_methods, const char *privname, char **sid_list)
+{
+ NTSTATUS ret = NT_STATUS_UNSUCCESSFUL;
+ TDB_CONTEXT *pwd_tdb = NULL;
+ TDB_DATA key, data;
+ fstring name;
+ fstring keystr;
+
+ struct tdbsam_privates *tdb_state = (struct tdbsam_privates *)my_methods->private_data;
+
+ if (!(pwd_tdb = tdbsam_tdbopen(tdb_state->tdbsam_location, O_RDONLY)))
+ return ret;
+
+ /* setup the PRIV index key */
+ fstrcpy(name, privname);
+ strlower_m(name);
+
+ slprintf(keystr, sizeof(keystr)-1, "%s%s", PRIVPREFIX, name);
+ key.dptr = keystr;
+ key.dsize = strlen(keystr) + 1;
+
+ data = tdb_fetch(pwd_tdb, key);
+ if (!data.dptr)
+ goto done;
+
+ *sid_list = strdup(data.dptr);
+ SAFE_FREE(data.dptr);
+
+ if (!*sid_list)
+ goto done;
+
+ ret = NT_STATUS_OK;
+done:
+ tdb_close(pwd_tdb);
+ return ret;
+}
+
+
+
+
+
+
+
static NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_method, const char *location)
{
NTSTATUS nt_status;
@@ -919,6 +1268,10 @@ static NTSTATUS pdb_init_tdbsam(PDB_CONTEXT *pdb_context, PDB_METHODS **pdb_meth
(*pdb_method)->add_trust_passwd = tdbsam_add_trust_passwd;
(*pdb_method)->update_trust_passwd = tdbsam_update_trust_passwd;
(*pdb_method)->delete_trust_passwd = tdbsam_delete_trust_passwd;
+ (*pdb_method)->add_sid_to_privilege = tdbsam_add_sid_to_privilege;
+ (*pdb_method)->remove_sid_from_privilege = tdbsam_remove_sid_from_privilege;
+ (*pdb_method)->get_privilege_set = tdbsam_get_privilege_set;
+ (*pdb_method)->get_privilege_entry = tdbsam_get_privilege_entry;
tdb_state = talloc_zero(pdb_context->mem_ctx, sizeof(struct tdbsam_privates));
diff --git a/source3/rpc_server/srv_lsa_nt.c b/source3/rpc_server/srv_lsa_nt.c
index aa933f9d0d..1e3c6c2a9d 100644
--- a/source3/rpc_server/srv_lsa_nt.c
+++ b/source3/rpc_server/srv_lsa_nt.c
@@ -819,10 +819,10 @@ NTSTATUS _lsa_priv_get_dispname(pipes_struct *p, LSA_Q_PRIV_GET_DISPNAME *q_u, L
DEBUG(10,("_lsa_priv_get_dispname: %s", name_asc));
- while (privs[i].se_priv!=SE_PRIV_ALL && strcmp(name_asc, privs[i].priv))
+ while (privs[i].se_priv!=SE_ALL_PRIVS && strcmp(name_asc, privs[i].priv))
i++;
- if (privs[i].se_priv!=SE_PRIV_ALL) {
+ if (privs[i].se_priv!=SE_ALL_PRIVS) {
DEBUG(10,(": %s\n", privs[i].description));
init_unistr2(&r_u->desc, privs[i].description, UNI_FLAGS_NONE);
init_uni_hdr(&r_u->hdr_desc, &r_u->desc);
diff --git a/source3/rpc_server/srv_pipe_hnd.c b/source3/rpc_server/srv_pipe_hnd.c
index 514c22d471..64ca8388d7 100644
--- a/source3/rpc_server/srv_pipe_hnd.c
+++ b/source3/rpc_server/srv_pipe_hnd.c
@@ -344,6 +344,8 @@ static void *make_internal_rpc_pipe_p(char *pipe_name,
if (vuser) {
p->session_key = data_blob(vuser->session_key.data, vuser->session_key.length);
p->pipe_user.nt_user_token = dup_nt_token(vuser->nt_user_token);
+ init_privilege(&p->pipe_user.privs);
+ dup_priv_set(p->pipe_user.privs, vuser->privs);
}
/*
diff --git a/source3/rpc_server/srv_samr_nt.c b/source3/rpc_server/srv_samr_nt.c
index b50d44d9e3..1959674f0a 100644
--- a/source3/rpc_server/srv_samr_nt.c
+++ b/source3/rpc_server/srv_samr_nt.c
@@ -40,6 +40,7 @@ extern rid_name domain_group_rids[];
extern rid_name domain_alias_rids[];
extern rid_name builtin_alias_rids[];
+extern PRIVS privs[];
typedef struct _disp_info {
BOOL user_dbloaded;
@@ -2137,7 +2138,15 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREA
return NT_STATUS_INVALID_HANDLE;
if (!NT_STATUS_IS_OK(nt_status = access_check_samr_function(acc_granted, SA_RIGHT_DOMAIN_CREATE_USER, "_samr_create_user"))) {
- return nt_status;
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_MACHINE_ACCOUNT))) {
+ DEBUG(3, ("_samr_create_user: User should be denied access but was overridden by %s\n", privs[SE_MACHINE_ACCOUNT].priv));
+ } else {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_ADD_USERS))) {
+ DEBUG(3, ("_samr_create_user: User should be denied access but was overridden by %s\n", privs[SE_ADD_USERS].priv));
+ } else {
+ return nt_status;
+ }
+ }
}
if (!(acb_info == ACB_NORMAL || acb_info == ACB_DOMTRUST || acb_info == ACB_WSTRUST || acb_info == ACB_SVRTRUST)) {
@@ -2200,6 +2209,33 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREA
/* the passdb lookup has failed; check to see if we need to run the
add user/machine script */
+
+ /*
+ * we can't check both the ending $ and the acb_info.
+ *
+ * UserManager creates trust accounts (ending in $,
+ * normal that hidden accounts) with the acb_info equals to ACB_NORMAL.
+ * JFM, 11/29/2001
+ */
+ if (account[strlen(account)-1] == '$') {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_MACHINE_ACCOUNT)) || geteuid() == 0) {
+ DEBUG(3, ("user [%s] has been granted Add Machines privilege!\n", p->user_name));
+ become_root();
+ pstrcpy(add_script, lp_addmachine_script());
+ } else {
+ DEBUG(3, ("user [%s] doesn't have Add Machines privilege!\n", p->user_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ if (NT_STATUS_IS_OK(user_has_privilege(&(p->pipe_user), SE_ADD_USERS)) || geteuid() == 0) {
+ DEBUG(3, ("user [%s] has been granted Add Users privilege!\n", p->user_name));
+ become_root();
+ pstrcpy(add_script, lp_adduser_script());
+ } else {
+ DEBUG(3, ("user [%s] doesn't have Add Users privilege!\n", p->user_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
pw = Get_Pwnam(account);
@@ -2215,17 +2251,6 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREA
*********************************************************************/
if ( !pw ) {
- /*
- * we can't check both the ending $ and the acb_info.
- *
- * UserManager creates trust accounts (ending in $,
- * normal that hidden accounts) with the acb_info equals to ACB_NORMAL.
- * JFM, 11/29/2001
- */
- if (account[strlen(account)-1] == '$')
- pstrcpy(add_script, lp_addmachine_script());
- else
- pstrcpy(add_script, lp_adduser_script());
if (*add_script) {
int add_ret;
@@ -2235,7 +2260,7 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREA
}
else /* no add user script -- ask winbindd to do it */
{
- if ( !winbind_create_user( account, &new_rid ) ) {
+ if (!winbind_create_user(account, &new_rid)) {
DEBUG(3,("_samr_create_user: winbind_create_user(%s) failed\n",
account));
}
@@ -2246,15 +2271,16 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREA
/* implicit call to getpwnam() next. we have a valid SID coming out of this call */
if ( !NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pass, account, new_rid)) )
- return nt_status;
+ goto done;
pdb_set_acct_ctrl(sam_pass, acb_info, PDB_CHANGED);
if (!pdb_add_sam_account(sam_pass)) {
pdb_free_sam(&sam_pass);
- DEBUG(0, ("could not add user/computer %s to passdb. Check permissions?\n",
+ DEBUG(0, ("could not add user/computer %s to passdb !?\n",
account));
- return NT_STATUS_ACCESS_DENIED;
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ goto done;
}
/* Get the user's SID */
@@ -2265,13 +2291,14 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREA
if (!NT_STATUS_IS_OK(nt_status =
access_check_samr_object(psd, p->pipe_user.nt_user_token,
des_access, &acc_granted, "_samr_create_user"))) {
- return nt_status;
+ goto done;
}
/* associate the user's SID with the new handle. */
if ((info = get_samr_info_by_sid(&sid)) == NULL) {
pdb_free_sam(&sam_pass);
- return NT_STATUS_NO_MEMORY;
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
}
ZERO_STRUCTP(info);
@@ -2281,7 +2308,8 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREA
/* get a (unique) handle. open a policy on it. */
if (!create_policy_hnd(p, user_pol, free_samr_info, (void *)info)) {
pdb_free_sam(&sam_pass);
- return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ nt_status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto done;
}
r_u->user_rid=pdb_get_user_rid(sam_pass);
@@ -2290,7 +2318,11 @@ NTSTATUS _samr_create_user(pipes_struct *p, SAMR_Q_CREATE_USER *q_u, SAMR_R_CREA
pdb_free_sam(&sam_pass);
- return NT_STATUS_OK;
+ nt_status = NT_STATUS_OK;
+
+done:
+ unbecome_root();
+ return nt_status;
}
/*******************************************************************
diff --git a/source3/rpc_server/srv_srvsvc_nt.c b/source3/rpc_server/srv_srvsvc_nt.c
index 40d3a43bef..7487e106bc 100644
--- a/source3/rpc_server/srv_srvsvc_nt.c
+++ b/source3/rpc_server/srv_srvsvc_nt.c
@@ -1405,10 +1405,7 @@ WERROR _srv_net_share_get_info(pipes_struct *p, SRV_Q_NET_SHARE_GET_INFO *q_u, S
static char *valid_share_pathname(char *dos_pathname)
{
- pstring saved_pathname;
- pstring unix_pathname;
char *ptr;
- int ret;
/* Convert any '\' paths to '/' */
unix_format(dos_pathname);
@@ -1423,21 +1420,29 @@ static char *valid_share_pathname(char *dos_pathname)
if (*ptr != '/')
return NULL;
+ return ptr;
+}
+
+static BOOL exist_share_pathname(char *unix_pathname)
+{
+ pstring saved_pathname;
+ int ret;
+
/* Can we cd to it ? */
/* First save our current directory. */
if (getcwd(saved_pathname, sizeof(saved_pathname)) == NULL)
return False;
- pstrcpy(unix_pathname, ptr);
-
ret = chdir(unix_pathname);
/* We *MUST* be able to chdir back. Abort if we can't. */
if (chdir(saved_pathname) == -1)
smb_panic("valid_share_pathname: Unable to restore current directory.\n");
- return (ret != -1) ? ptr : NULL;
+ if (ret == -1) return False;
+
+ return True;
}
/*******************************************************************
@@ -1454,7 +1459,7 @@ WERROR _srv_net_share_set_info(pipes_struct *p, SRV_Q_NET_SHARE_SET_INFO *q_u, S
int type;
int snum;
int ret;
- char *ptr;
+ char *path;
SEC_DESC *psd = NULL;
DEBUG(5,("_srv_net_share_set_info: %d\n", __LINE__));
@@ -1549,12 +1554,12 @@ WERROR _srv_net_share_set_info(pipes_struct *p, SRV_Q_NET_SHARE_SET_INFO *q_u, S
return WERR_ACCESS_DENIED;
/* Check if the pathname is valid. */
- if (!(ptr = valid_share_pathname( pathname )))
+ if (!(path = valid_share_pathname( pathname )))
return WERR_OBJECT_PATH_INVALID;
/* Ensure share name, pathname and comment don't contain '"' characters. */
string_replace(share_name, '"', ' ');
- string_replace(ptr, '"', ' ');
+ string_replace(path, '"', ' ');
string_replace(comment, '"', ' ');
DEBUG(10,("_srv_net_share_set_info: change share command = %s\n",
@@ -1562,12 +1567,12 @@ WERROR _srv_net_share_set_info(pipes_struct *p, SRV_Q_NET_SHARE_SET_INFO *q_u, S
/* Only call modify function if something changed. */
- if (strcmp(ptr, lp_pathname(snum)) || strcmp(comment, lp_comment(snum)) ) {
+ if (strcmp(path, lp_pathname(snum)) || strcmp(comment, lp_comment(snum)) ) {
if (!lp_change_share_cmd() || !*lp_change_share_cmd())
return WERR_ACCESS_DENIED;
slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
- lp_change_share_cmd(), dyn_CONFIGFILE, share_name, ptr, comment);
+ lp_change_share_cmd(), dyn_CONFIGFILE, share_name, path, comment);
DEBUG(10,("_srv_net_share_set_info: Running [%s]\n", command ));
if ((ret = smbrun(command, NULL)) != 0) {
@@ -1575,6 +1580,12 @@ WERROR _srv_net_share_set_info(pipes_struct *p, SRV_Q_NET_SHARE_SET_INFO *q_u, S
return WERR_ACCESS_DENIED;
}
+ /* Check if the new share pathname exist, if not return an error */
+ if (!exist_share_pathname(path)) {
+ DEBUG(1, ("_srv_net_share_set_info: change share command was ok but path (%s) has not been created!\n", path));
+ return WERR_OBJECT_PATH_INVALID;
+ }
+
/* Tell everyone we updated smb.conf. */
message_send_all(conn_tdb_ctx(), MSG_SMB_CONF_UPDATED, NULL, 0, False, NULL);
@@ -1615,7 +1626,7 @@ WERROR _srv_net_share_add(pipes_struct *p, SRV_Q_NET_SHARE_ADD *q_u, SRV_R_NET_S
int type;
int snum;
int ret;
- char *ptr;
+ char *path;
SEC_DESC *psd = NULL;
DEBUG(5,("_srv_net_share_add: %d\n", __LINE__));
@@ -1689,16 +1700,16 @@ WERROR _srv_net_share_add(pipes_struct *p, SRV_Q_NET_SHARE_ADD *q_u, SRV_R_NET_S
return WERR_ACCESS_DENIED;
/* Check if the pathname is valid. */
- if (!(ptr = valid_share_pathname( pathname )))
+ if (!(path = valid_share_pathname( pathname )))
return WERR_OBJECT_PATH_INVALID;
/* Ensure share name, pathname and comment don't contain '"' characters. */
string_replace(share_name, '"', ' ');
- string_replace(ptr, '"', ' ');
+ string_replace(path, '"', ' ');
string_replace(comment, '"', ' ');
slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\" \"%s\" \"%s\"",
- lp_add_share_cmd(), dyn_CONFIGFILE, share_name, ptr, comment);
+ lp_add_share_cmd(), dyn_CONFIGFILE, share_name, path, comment);
DEBUG(10,("_srv_net_share_add: Running [%s]\n", command ));
if ((ret = smbrun(command, NULL)) != 0) {
@@ -1706,10 +1717,33 @@ WERROR _srv_net_share_add(pipes_struct *p, SRV_Q_NET_SHARE_ADD *q_u, SRV_R_NET_S
return WERR_ACCESS_DENIED;
}
+ /* Check if the new share pathname exist, if not try to delete the
+ * share and return an error */
+ if (!exist_share_pathname(path)) {
+ DEBUG(1, ("_srv_net_share_add: add share command was ok but path (%s) has not been created!\n", path));
+ DEBUG(1, ("_srv_net_share_add: trying to rollback and delete the share\n"));
+
+ if (!lp_delete_share_cmd() || !*lp_delete_share_cmd()) {
+ DEBUG(1, ("_srv_net_share_add: Error! delete share command is not defined! Please check share (%s) in the config file\n", share_name));
+ return WERR_OBJECT_PATH_INVALID;
+ }
+
+ slprintf(command, sizeof(command)-1, "%s \"%s\" \"%s\"",
+ lp_delete_share_cmd(), dyn_CONFIGFILE, share_name);
+
+ DEBUG(10,("_srv_net_share_add: Running [%s]\n", command ));
+ if ((ret = smbrun(command, NULL)) != 0) {
+ DEBUG(0,("_srv_net_share_add: Running [%s] returned (%d)\n", command, ret ));
+ DEBUG(1, ("_srv_net_share_add: Error! delete share command failed! Please check share (%s) in the config file\n", share_name));
+ }
+
+ return WERR_OBJECT_PATH_INVALID;
+ }
+
if (psd) {
- if (!set_share_security(p->mem_ctx, share_name, psd))
- DEBUG(0,("_srv_net_share_add: Failed to add security info to share %s.\n",
- share_name ));
+ if (!set_share_security(p->mem_ctx, share_name, psd)) {
+ DEBUG(0,("_srv_net_share_add: Failed to add security info to share %s.\n", share_name ));
+ }
}
/* Tell everyone we updated smb.conf. */
diff --git a/source3/sam/gums.c b/source3/sam/gums.c
index 91009f8b5d..b719153584 100644
--- a/source3/sam/gums.c
+++ b/source3/sam/gums.c
@@ -26,71 +26,8 @@
#define GMV_MAJOR 0
#define GMV_MINOR 1
-#define PRIV_NONE 0
-#define PRIV_CREATE_TOKEN 1
-#define PRIV_ASSIGNPRIMARYTOKEN 2
-#define PRIV_LOCK_MEMORY 3
-#define PRIV_INCREASE_QUOTA 4
-#define PRIV_MACHINE_ACCOUNT 5
-#define PRIV_TCB 6
-#define PRIV_SECURITY 7
-#define PRIV_TAKE_OWNERSHIP 8
-#define PRIV_LOAD_DRIVER 9
-#define PRIV_SYSTEM_PROFILE 10
-#define PRIV_SYSTEMTIME 11
-#define PRIV_PROF_SINGLE_PROCESS 12
-#define PRIV_INC_BASE_PRIORITY 13
-#define PRIV_CREATE_PAGEFILE 14
-#define PRIV_CREATE_PERMANENT 15
-#define PRIV_BACKUP 16
-#define PRIV_RESTORE 17
-#define PRIV_SHUTDOWN 18
-#define PRIV_DEBUG 19
-#define PRIV_AUDIT 20
-#define PRIV_SYSTEM_ENVIRONMENT 21
-#define PRIV_CHANGE_NOTIFY 22
-#define PRIV_REMOTE_SHUTDOWN 23
-#define PRIV_UNDOCK 24
-#define PRIV_SYNC_AGENT 25
-#define PRIV_ENABLE_DELEGATION 26
-#define PRIV_ALL 255
-
-
static GUMS_FUNCTIONS *gums_backend = NULL;
-#if 0
-static PRIVS gums_privs[] = {
- {PRIV_NONE, "no_privs", "No privilege"}, /* this one MUST be first */
- {PRIV_CREATE_TOKEN, "SeCreateToken", "Create Token"},
- {PRIV_ASSIGNPRIMARYTOKEN, "SeAssignPrimaryToken", "Assign Primary Token"},
- {PRIV_LOCK_MEMORY, "SeLockMemory", "Lock Memory"},
- {PRIV_INCREASE_QUOTA, "SeIncreaseQuotaPrivilege", "Increase Quota Privilege"},
- {PRIV_MACHINE_ACCOUNT, "SeMachineAccount", "Machine Account"},
- {PRIV_TCB, "SeTCB", "TCB"},
- {PRIV_SECURITY, "SeSecurityPrivilege", "Security Privilege"},
- {PRIV_TAKE_OWNERSHIP, "SeTakeOwnershipPrivilege", "Take Ownership Privilege"},
- {PRIV_LOAD_DRIVER, "SeLocalDriverPrivilege", "Local Driver Privilege"},
- {PRIV_SYSTEM_PROFILE, "SeSystemProfilePrivilege", "System Profile Privilege"},
- {PRIV_SYSTEMTIME, "SeSystemtimePrivilege", "System Time"},
- {PRIV_PROF_SINGLE_PROCESS, "SeProfileSingleProcessPrivilege", "Profile Single Process Privilege"},
- {PRIV_INC_BASE_PRIORITY, "SeIncreaseBasePriorityPrivilege", "Increase Base Priority Privilege"},
- {PRIV_CREATE_PAGEFILE, "SeCreatePagefilePrivilege", "Create Pagefile Privilege"},
- {PRIV_CREATE_PERMANENT, "SeCreatePermanent", "Create Permanent"},
- {PRIV_BACKUP, "SeBackupPrivilege", "Backup Privilege"},
- {PRIV_RESTORE, "SeRestorePrivilege", "Restore Privilege"},
- {PRIV_SHUTDOWN, "SeShutdownPrivilege", "Shutdown Privilege"},
- {PRIV_DEBUG, "SeDebugPrivilege", "Debug Privilege"},
- {PRIV_AUDIT, "SeAudit", "Audit"},
- {PRIV_SYSTEM_ENVIRONMENT, "SeSystemEnvironmentPrivilege", "System Environment Privilege"},
- {PRIV_CHANGE_NOTIFY, "SeChangeNotify", "Change Notify"},
- {PRIV_REMOTE_SHUTDOWN, "SeRemoteShutdownPrivilege", "Remote Shutdown Privilege"},
- {PRIV_UNDOCK, "SeUndock", "Undock"},
- {PRIV_SYNC_AGENT, "SeSynchronizationAgent", "Synchronization Agent"},
- {PRIV_ENABLE_DELEGATION, "SeEnableDelegation", "Enable Delegation"},
- {PRIV_ALL, "SaAllPrivs", "All Privileges"}
-};
-#endif
-
static struct gums_init_function_entry *backends = NULL;
static void lazy_initialize_gums(void)
diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c
index e3df8a11d0..8e4df1a464 100644
--- a/source3/smbd/chgpasswd.c
+++ b/source3/smbd/chgpasswd.c
@@ -991,7 +991,7 @@ NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passw
if (!push_sec_ctx())
return NT_STATUS_UNSUCCESSFUL;
- set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
+ set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL, NULL);
set_re_uid();
}
diff --git a/source3/smbd/conn.c b/source3/smbd/conn.c
index 9bac0acdb9..0805f8e690 100644
--- a/source3/smbd/conn.c
+++ b/source3/smbd/conn.c
@@ -249,6 +249,14 @@ void conn_free(connection_struct *conn)
conn->ngroups = 0;
}
+ if (conn->nt_user_token) {
+ delete_nt_token(&(conn->nt_user_token));
+ }
+
+ if (conn->privs) {
+ destroy_privilege(&(conn->privs));
+ }
+
free_namearray(conn->veto_list);
free_namearray(conn->hide_list);
free_namearray(conn->veto_oplock_list);
diff --git a/source3/smbd/password.c b/source3/smbd/password.c
index 10c6aadb1f..8438f2a593 100644
--- a/source3/smbd/password.c
+++ b/source3/smbd/password.c
@@ -87,6 +87,7 @@ void invalidate_vuid(uint16 vuid)
SAFE_FREE(vuser->groups);
delete_nt_token(&vuser->nt_user_token);
+ destroy_privilege(&vuser->privs);
SAFE_FREE(vuser);
num_validated_vuids--;
}
@@ -234,6 +235,11 @@ int register_vuid(auth_serversupplied_info *server_info, DATA_BLOB session_key,
return UID_FIELD_INVALID;
}
+ if (server_info->privs) {
+ init_privilege(&(vuser->privs));
+ dup_priv_set(vuser->privs, server_info->privs);
+ }
+
/* use this to keep tabs on all our info from the authentication */
vuser->server_info = server_info;
diff --git a/source3/smbd/sec_ctx.c b/source3/smbd/sec_ctx.c
index 8a85792ead..9244f34394 100644
--- a/source3/smbd/sec_ctx.c
+++ b/source3/smbd/sec_ctx.c
@@ -28,6 +28,7 @@ struct sec_ctx {
int ngroups;
gid_t *groups;
NT_USER_TOKEN *token;
+ PRIVILEGE_SET *privs;
};
/* A stack of security contexts. We include the current context as being
@@ -256,6 +257,10 @@ BOOL push_sec_ctx(void)
(unsigned int)ctx_p->uid, (unsigned int)ctx_p->gid, sec_ctx_stack_ndx ));
ctx_p->token = dup_nt_token(sec_ctx_stack[sec_ctx_stack_ndx-1].token);
+ if (! ctx_p->token) {
+ DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
+ return False;
+ }
ctx_p->ngroups = sys_getgroups(0, NULL);
@@ -271,6 +276,14 @@ BOOL push_sec_ctx(void)
ctx_p->groups = NULL;
}
+ init_privilege(&ctx_p->privs);
+ if (! NT_STATUS_IS_OK(dup_priv_set(ctx_p->privs, sec_ctx_stack[sec_ctx_stack_ndx-1].privs))) {
+ DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
+ delete_nt_token(&ctx_p->token);
+ destroy_privilege(&ctx_p->privs);
+ return False;
+ }
+
return True;
}
@@ -278,7 +291,7 @@ BOOL push_sec_ctx(void)
Set the current security context to a given user.
****************************************************************************/
-void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token)
+void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token, PRIVILEGE_SET *privs)
{
struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
@@ -303,9 +316,14 @@ void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN
smb_panic("DUPLICATE_TOKEN");
delete_nt_token(&ctx_p->token);
+ if (ctx_p->privs)
+ reset_privilege(ctx_p->privs);
+ else
+ init_privilege(&ctx_p->privs);
ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups);
ctx_p->token = dup_nt_token(token);
+ dup_priv_set(ctx_p->privs, privs);
become_id(uid, gid);
@@ -319,6 +337,7 @@ void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN
current_user.ngroups = ngroups;
current_user.groups = groups;
current_user.nt_user_token = ctx_p->token;
+ current_user.privs = ctx_p->privs;
}
/****************************************************************************
@@ -329,7 +348,7 @@ void set_root_sec_ctx(void)
{
/* May need to worry about supplementary groups at some stage */
- set_sec_ctx(0, 0, 0, NULL, NULL);
+ set_sec_ctx(0, 0, 0, NULL, NULL, NULL);
}
/****************************************************************************
@@ -359,6 +378,7 @@ BOOL pop_sec_ctx(void)
ctx_p->ngroups = 0;
delete_nt_token(&ctx_p->token);
+ destroy_privilege(&ctx_p->privs);
/* Pop back previous user */
@@ -381,6 +401,7 @@ BOOL pop_sec_ctx(void)
current_user.ngroups = prev_ctx_p->ngroups;
current_user.groups = prev_ctx_p->groups;
current_user.nt_user_token = prev_ctx_p->token;
+ current_user.privs = prev_ctx_p->privs;
DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n",
(unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
@@ -413,6 +434,7 @@ void init_sec_ctx(void)
get_current_groups(ctx_p->gid, &ctx_p->ngroups, &ctx_p->groups);
ctx_p->token = NULL; /* Maps to guest user. */
+ ctx_p->privs = NULL;
/* Initialise current_user global */
@@ -427,4 +449,5 @@ void init_sec_ctx(void)
current_user.conn = NULL;
current_user.vuid = UID_FIELD_INVALID;
current_user.nt_user_token = NULL;
+ current_user.privs = NULL;
}
diff --git a/source3/smbd/service.c b/source3/smbd/service.c
index 78b610ae37..caa2872f04 100644
--- a/source3/smbd/service.c
+++ b/source3/smbd/service.c
@@ -371,6 +371,7 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser,
string_set(&conn->dirpath,"");
string_set(&conn->user,user);
conn->nt_user_token = NULL;
+ conn->privs = NULL;
conn->read_only = lp_readonly(conn->service);
conn->admin_user = False;
@@ -479,6 +480,9 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser,
conn->nt_user_token = create_nt_token(conn->uid, conn->gid,
conn->ngroups, conn->groups,
guest);
+
+ init_privilege(&(conn->privs));
+ pdb_get_privilege_set(conn->nt_user_token, conn->privs);
}
/*
diff --git a/source3/smbd/uid.c b/source3/smbd/uid.c
index 3859298055..d43bf301e8 100644
--- a/source3/smbd/uid.c
+++ b/source3/smbd/uid.c
@@ -44,7 +44,7 @@ BOOL change_to_guest(void)
initgroups(pass->pw_name, pass->pw_gid);
#endif
- set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL);
+ set_sec_ctx(pass->pw_uid, pass->pw_gid, 0, NULL, NULL, NULL);
current_user.conn = NULL;
current_user.vuid = UID_FIELD_INVALID;
@@ -161,8 +161,9 @@ BOOL change_to_user(connection_struct *conn, uint16 vuid)
gid_t gid;
uid_t uid;
char group_c;
- BOOL must_free_token = False;
+ BOOL must_free_token_priv = False;
NT_USER_TOKEN *token = NULL;
+ PRIVILEGE_SET *privs = NULL;
if (!conn) {
DEBUG(2,("change_to_user: Connection not open\n"));
@@ -195,12 +196,14 @@ BOOL change_to_user(connection_struct *conn, uint16 vuid)
current_user.groups = conn->groups;
current_user.ngroups = conn->ngroups;
token = conn->nt_user_token;
+ privs = conn->privs;
} else if ((vuser) && check_user_ok(conn, vuser, snum)) {
uid = conn->admin_user ? 0 : vuser->uid;
gid = vuser->gid;
current_user.ngroups = vuser->n_groups;
current_user.groups = vuser->groups;
token = vuser->nt_user_token;
+ privs = vuser->privs;
} else {
DEBUG(2,("change_to_user: Invalid vuid used %d or vuid not permitted access to share.\n",vuid));
return False;
@@ -248,17 +251,20 @@ BOOL change_to_user(connection_struct *conn, uint16 vuid)
DEBUG(1, ("change_to_user: create_nt_token failed!\n"));
return False;
}
- must_free_token = True;
+ pdb_get_privilege_set(token, privs);
+ must_free_token_priv = True;
}
- set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token);
+ set_sec_ctx(uid, gid, current_user.ngroups, current_user.groups, token, privs);
/*
* Free the new token (as set_sec_ctx copies it).
*/
- if (must_free_token)
+ if (must_free_token_priv) {
delete_nt_token(&token);
+ destroy_privilege(&privs);
+ }
current_user.conn = conn;
current_user.vuid = vuid;
@@ -299,7 +305,7 @@ BOOL become_authenticated_pipe_user(pipes_struct *p)
return False;
set_sec_ctx(p->pipe_user.uid, p->pipe_user.gid,
- p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token);
+ p->pipe_user.ngroups, p->pipe_user.groups, p->pipe_user.nt_user_token, p->pipe_user.privs);
return True;
}
diff --git a/source3/utils/net.c b/source3/utils/net.c
index 8004ced43e..fcc8b4abe1 100644
--- a/source3/utils/net.c
+++ b/source3/utils/net.c
@@ -648,6 +648,7 @@ static struct functable net_func[] = {
#ifdef WITH_FAKE_KASERVER
{"AFSKEY", net_afskey},
#endif
+ {"PRIV", net_priv},
{"HELP", net_help},
{NULL, NULL}