Ok here it is my latest work on privileges
authorSimo Sorce <idra@samba.org>
Mon, 1 Mar 2004 16:10:28 +0000 (16:10 +0000)
committerSimo Sorce <idra@samba.org>
Mon, 1 Mar 2004 16:10:28 +0000 (16:10 +0000)
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)

24 files changed:
source3/Makefile.in
source3/auth/auth_util.c
source3/groupdb/mapping.c
source3/include/auth.h
source3/include/includes.h
source3/include/passdb.h
source3/include/privileges.h
source3/include/smb.h
source3/lib/privileges.c
source3/param/loadparm.c
source3/passdb/pdb_interface.c
source3/passdb/pdb_tdb.c
source3/rpc_server/srv_lsa_nt.c
source3/rpc_server/srv_pipe_hnd.c
source3/rpc_server/srv_samr_nt.c
source3/rpc_server/srv_srvsvc_nt.c
source3/sam/gums.c
source3/smbd/chgpasswd.c
source3/smbd/conn.c
source3/smbd/password.c
source3/smbd/sec_ctx.c
source3/smbd/service.c
source3/smbd/uid.c
source3/utils/net.c

index 25a0e45f059e65bf03181cb74f0fbe74137485b9..5792b00264fc9b98b498569678f834df690c4b9a 100644 (file)
@@ -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) \
index 4a23593936a521c67ca61d26323d930c0ffbd94a..018d3b185148b88acc20faa8bb91f2d5be636703 100644 (file)
@@ -826,6 +826,23 @@ static NTSTATUS add_user_groups(auth_serversupplied_info **server_info,
        return nt_status;
 }
 
+/***************************************************************************
+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),
index c153ff258d5c682c024327ac0123dcfed5b81718..ef0708b88857d37500444fbb22c6312a57712dff 100644 (file)
@@ -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
 ****************************************************************************/
index ecf4d539d8c032826fc14c0eb4f07742a9eb3f00..27cdc1e3f5fef5e136c0234051ac7fe032b7d60d 100644 (file)
@@ -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;
index db060907e4d8f81b4e225f21b44952edcf43577a..a594e309df845a8dfc1dafe68dee3f53d1657788 100644 (file)
@@ -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"
index 668bbcc2de68625d47020b3860782f80e91a102a..d471eb53f3b69542afad372ca75964ed63d26b8f 100644 (file)
@@ -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 *, 
index b7e1b44c2aff91c2debfc11159808344da288fa0..289afa234ec861d158f2368576732d151dd35ed3 100644 (file)
 #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
 #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
 {
index 5cd5e71f74778baf2f624ab890851515090bfd51..ab6f4c70d662d0979c2bf96dcd0a7c9ed063b4a6 100644 (file)
@@ -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;
 
index b9d4df301d95e24b12df8378af21a3aad4b8d1b9..4bcf5e3b36ba9cd6eb40235fdd4a5c976b3c5fbd 100644 (file)
 #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);
+}
+
index ae9d3ae9454a45ecc74dd789cec4962a90a392c6..58f4919465e4956bb0441d8a0059496a5488b333 100644 (file)
@@ -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);
        }       
index 2005885c45fef36e2013bd356e813b178347b4b7..180db58c2d9bd93dc3f51951e8b3b26cc67db2ca 100644 (file)
@@ -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;
 }
index f54463e158eb52878384871c219ece1e090c08d7..39de791b07997e69c3edc51c6e8b31903c1659b4 100644 (file)
@@ -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));
 
index aa933f9d0dcf54bce3d10ff152d42984cf1a4234..1e3c6c2a9d7c08552dc3d1ac3d4e06f61988e3d8 100644 (file)
@@ -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);
index 514c22d471e3dd74547486723d09f0c0299132b4..64ca8388d77cd844b61b49f49deb43b5fce582fa 100644 (file)
@@ -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);
        }
 
        /*
index b50d44d9e363b2ed941cac237621dfb351dcd5e7..1959674f0add2c1bd3eda53a4a8341302c33b21b 100644 (file)
@@ -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;
 }
 
 /*******************************************************************
index 40d3a43bef9df020823b7afb1b8bbb473f73e1bd..7487e106bcfdbebd5ae6f8e0097c35c217c87a2c 100644 (file)
@@ -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. */
index 91009f8b5d3c5bf48d30e87e620a1cbac1e61e55..b71915358451cd94da62b17b1313c8da41141fd2 100644 (file)
 #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)
index e3df8a11d02e459f3a042dfd0fdcfb42bfa09588..8e4df1a46437ddb04a46ec5f50587a979b7cc3f3 100644 (file)
@@ -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();
        }
 
index 9bac0acdb9fd3b6e622e26a6f2211bffade5e896..0805f8e690296ea50e0264bccff17e76ddbdb694 100644 (file)
@@ -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);
index 10c6aadb1fc49d66c34461d9b252134853646653..8438f2a59365dcb3e74cad7b791dda7c62025198 100644 (file)
@@ -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;
 
index 8a85792ead55d4f70e24d3a3ec455eaa5ee90588..9244f34394e5bdce60130d3cbb1590be32977d81 100644 (file)
@@ -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;
 }
index 78b610ae37c7c2a1bcd7f7c4f1efcb44e6965217..caa2872f04fa006b32f5781e07720d152c898ffc 100644 (file)
@@ -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);
        }
 
        /*
index 3859298055bf82fdd49bdcf157fdec0b78bcb373..d43bf301e8855719550b20fbec8d5856c55c73cf 100644 (file)
@@ -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;
 }
index 8004ced43ebd9fa3fdb74e57c2add3e0e06cca7e..fcc8b4abe1d8a6adc0eeacc8cab7f9ff9f40e3ee 100644 (file)
@@ -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}