mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
r22135: Check in most of Michael Adam's net conf utility. A good share of this patch
is moving functions around to fix some linker dependencies for the registry. Michael, I've renamed your auth_utils2.c to token_utils.c. Thanks! Volker
This commit is contained in:
parent
85df3fca68
commit
9de16f25c1
@ -341,7 +341,8 @@ REGOBJS_OBJ = registry/reg_objects.o
|
||||
REGISTRY_OBJ = registry/reg_frontend.o registry/reg_cachehook.o registry/reg_printing.o \
|
||||
registry/reg_db.o registry/reg_eventlog.o registry/reg_shares.o \
|
||||
registry/reg_util.o registry/reg_dynamic.o registry/reg_perfcount.o \
|
||||
registry/reg_smbconf.o registry/reg_api.o
|
||||
registry/reg_smbconf.o registry/reg_api.o \
|
||||
registry/reg_frontend_hilvl.o
|
||||
|
||||
RPC_LSA_OBJ = rpc_server/srv_lsa.o rpc_server/srv_lsa_nt.o librpc/gen_ndr/srv_lsa.o
|
||||
|
||||
@ -462,8 +463,8 @@ AUTH_UNIX_OBJ = auth/auth_unix.o
|
||||
AUTH_WINBIND_OBJ = auth/auth_winbind.o
|
||||
AUTH_SCRIPT_OBJ = auth/auth_script.o
|
||||
|
||||
AUTH_OBJ = auth/auth.o @AUTH_STATIC@ auth/auth_util.o auth/auth_compat.o \
|
||||
auth/auth_ntlmssp.o \
|
||||
AUTH_OBJ = auth/auth.o @AUTH_STATIC@ auth/auth_util.o auth/token_util.o \
|
||||
auth/auth_compat.o auth/auth_ntlmssp.o \
|
||||
$(PLAINTEXT_AUTH_OBJ) $(SLCACHE_OBJ) $(DCUTIL_OBJ)
|
||||
|
||||
MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o
|
||||
@ -639,8 +640,22 @@ NET_OBJ1 = utils/net.o utils/net_ads.o utils/net_domain.o utils/net_help.o \
|
||||
utils/net_rpc_service.o utils/net_rpc_registry.o utils/net_usershare.o \
|
||||
utils/netlookup.o utils/net_sam.o utils/net_rpc_shell.o \
|
||||
utils/net_util.o utils/net_rpc_sh_acct.o utils/net_rpc_audit.o \
|
||||
$(PASSWD_UTIL_OBJ) utils/net_dns.o utils/net_ads_gpo.o
|
||||
$(PASSWD_UTIL_OBJ) utils/net_dns.o utils/net_ads_gpo.o \
|
||||
utils/net_conf.o
|
||||
|
||||
NET_REG_OBJ = registry/reg_api.o \
|
||||
registry/reg_frontend_hilvl.o \
|
||||
registry/reg_smbconf.o \
|
||||
registry/reg_db.o \
|
||||
registry/reg_util.o \
|
||||
\
|
||||
registry/reg_cachehook.o \
|
||||
registry/reg_eventlog.o \
|
||||
registry/reg_perfcount.o \
|
||||
registry/reg_dynamic.o \
|
||||
\
|
||||
auth/token_util.o
|
||||
|
||||
NET_OBJ = $(NET_OBJ1) $(PARAM_OBJ) $(SECRETS_OBJ) $(LIBSMB_OBJ) \
|
||||
$(RPC_PARSE_OBJ) $(PASSDB_OBJ) $(GROUPDB_OBJ) \
|
||||
$(KRBCLIENT_OBJ) $(LIB_NONSMBD_OBJ) $(LIBADDNS_OBJ0) \
|
||||
@ -648,7 +663,8 @@ NET_OBJ = $(NET_OBJ1) $(PARAM_OBJ) $(SECRETS_OBJ) $(LIBSMB_OBJ) \
|
||||
$(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) $(POPT_LIB_OBJ) \
|
||||
$(SMBLDAP_OBJ) $(DCUTIL_OBJ) $(SERVER_MUTEX_OBJ) \
|
||||
$(AFS_OBJ) $(AFS_SETTOKEN_OBJ) $(REGFIO_OBJ) $(READLINE_OBJ) \
|
||||
$(LDB_OBJ) $(LIBGPO_OBJ) $(INIPARSER_OBJ) $(DISPLAY_SEC_OBJ)
|
||||
$(LDB_OBJ) $(LIBGPO_OBJ) $(INIPARSER_OBJ) $(DISPLAY_SEC_OBJ) \
|
||||
$(NET_REG_OBJ)
|
||||
|
||||
CUPS_OBJ = client/smbspool.o $(PARAM_OBJ) $(LIBSMB_OBJ) \
|
||||
$(LIB_NONSMBD_OBJ) $(KRBCLIENT_OBJ) $(SECRETS_OBJ)
|
||||
|
@ -27,12 +27,6 @@
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_AUTH
|
||||
|
||||
static struct nt_user_token *create_local_nt_token(TALLOC_CTX *mem_ctx,
|
||||
const DOM_SID *user_sid,
|
||||
BOOL is_guest,
|
||||
int num_groupsids,
|
||||
const DOM_SID *groupsids);
|
||||
|
||||
/****************************************************************************
|
||||
Create a UNIX user on demand.
|
||||
****************************************************************************/
|
||||
@ -481,38 +475,6 @@ void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid,
|
||||
(long int)groups[i]));
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Create a token for the root user to be used internally by smbd.
|
||||
This is similar to running under the context of the LOCAL_SYSTEM account
|
||||
in Windows. This is a read-only token. Do not modify it or free() it.
|
||||
Create a copy if your need to change it.
|
||||
******************************************************************************/
|
||||
|
||||
NT_USER_TOKEN *get_root_nt_token( void )
|
||||
{
|
||||
static NT_USER_TOKEN *token = NULL;
|
||||
DOM_SID u_sid, g_sid;
|
||||
struct passwd *pw;
|
||||
|
||||
if ( token )
|
||||
return token;
|
||||
|
||||
if ( !(pw = sys_getpwnam( "root" )) ) {
|
||||
DEBUG(0,("get_root_nt_token: getpwnam\"root\") failed!\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get the user and primary group SIDs; although the
|
||||
BUILTIN\Administrators SId is really the one that matters here */
|
||||
|
||||
uid_to_sid(&u_sid, pw->pw_uid);
|
||||
gid_to_sid(&g_sid, pw->pw_gid);
|
||||
|
||||
token = create_local_nt_token(NULL, &u_sid, False,
|
||||
1, &global_sid_Builtin_Administrators);
|
||||
return token;
|
||||
}
|
||||
|
||||
static int server_info_dtor(auth_serversupplied_info *server_info)
|
||||
{
|
||||
TALLOC_FREE(server_info->sam_account);
|
||||
@ -631,53 +593,6 @@ NTSTATUS make_server_info_sam(auth_serversupplied_info **server_info,
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add alias SIDs from memberships within the partially created token SID list
|
||||
*/
|
||||
|
||||
static NTSTATUS add_aliases(const DOM_SID *domain_sid,
|
||||
struct nt_user_token *token)
|
||||
{
|
||||
uint32 *aliases;
|
||||
size_t i, num_aliases;
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
|
||||
if (!(tmp_ctx = talloc_init("add_aliases"))) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
aliases = NULL;
|
||||
num_aliases = 0;
|
||||
|
||||
status = pdb_enum_alias_memberships(tmp_ctx, domain_sid,
|
||||
token->user_sids,
|
||||
token->num_sids,
|
||||
&aliases, &num_aliases);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(10, ("pdb_enum_alias_memberships failed: %s\n",
|
||||
nt_errstr(status)));
|
||||
TALLOC_FREE(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i=0; i<num_aliases; i++) {
|
||||
DOM_SID alias_sid;
|
||||
sid_compose(&alias_sid, domain_sid, aliases[i]);
|
||||
if (!add_sid_to_array_unique(token, &alias_sid,
|
||||
&token->user_sids,
|
||||
&token->num_sids)) {
|
||||
DEBUG(0, ("add_sid_to_array failed\n"));
|
||||
TALLOC_FREE(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
TALLOC_FREE(tmp_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS log_nt_token(TALLOC_CTX *tmp_ctx, NT_USER_TOKEN *token)
|
||||
{
|
||||
char *command;
|
||||
@ -714,270 +629,6 @@ static NTSTATUS log_nt_token(TALLOC_CTX *tmp_ctx, NT_USER_TOKEN *token)
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
*******************************************************************/
|
||||
|
||||
static NTSTATUS add_builtin_administrators( struct nt_user_token *token )
|
||||
{
|
||||
DOM_SID domadm;
|
||||
|
||||
/* nothing to do if we aren't in a domain */
|
||||
|
||||
if ( !(IS_DC || lp_server_role()==ROLE_DOMAIN_MEMBER) ) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Find the Domain Admins SID */
|
||||
|
||||
if ( IS_DC ) {
|
||||
sid_copy( &domadm, get_global_sam_sid() );
|
||||
} else {
|
||||
if ( !secrets_fetch_domain_sid( lp_workgroup(), &domadm ) )
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
sid_append_rid( &domadm, DOMAIN_GROUP_RID_ADMINS );
|
||||
|
||||
/* Add Administrators if the user beloongs to Domain Admins */
|
||||
|
||||
if ( nt_token_check_sid( &domadm, token ) ) {
|
||||
if (!add_sid_to_array(token, &global_sid_Builtin_Administrators,
|
||||
&token->user_sids, &token->num_sids)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
*******************************************************************/
|
||||
|
||||
static NTSTATUS create_builtin_users( void )
|
||||
{
|
||||
NTSTATUS status;
|
||||
DOM_SID dom_users;
|
||||
|
||||
status = pdb_create_builtin_alias( BUILTIN_ALIAS_RID_USERS );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_users: Failed to create Users\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* add domain users */
|
||||
if ((IS_DC || (lp_server_role() == ROLE_DOMAIN_MEMBER))
|
||||
&& secrets_fetch_domain_sid(lp_workgroup(), &dom_users))
|
||||
{
|
||||
sid_append_rid(&dom_users, DOMAIN_GROUP_RID_USERS );
|
||||
status = pdb_add_aliasmem( &global_sid_Builtin_Users, &dom_users);
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_users: Failed to add Domain Users to"
|
||||
" Users\n"));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
*******************************************************************/
|
||||
|
||||
static NTSTATUS create_builtin_administrators( void )
|
||||
{
|
||||
NTSTATUS status;
|
||||
DOM_SID dom_admins, root_sid;
|
||||
fstring root_name;
|
||||
enum lsa_SidType type;
|
||||
TALLOC_CTX *ctx;
|
||||
BOOL ret;
|
||||
|
||||
status = pdb_create_builtin_alias( BUILTIN_ALIAS_RID_ADMINS );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_administrators: Failed to create Administrators\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* add domain admins */
|
||||
if ((IS_DC || (lp_server_role() == ROLE_DOMAIN_MEMBER))
|
||||
&& secrets_fetch_domain_sid(lp_workgroup(), &dom_admins))
|
||||
{
|
||||
sid_append_rid(&dom_admins, DOMAIN_GROUP_RID_ADMINS);
|
||||
status = pdb_add_aliasmem( &global_sid_Builtin_Administrators, &dom_admins );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_administrators: Failed to add Domain Admins"
|
||||
" Administrators\n"));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* add root */
|
||||
if ( (ctx = talloc_init("create_builtin_administrators")) == NULL ) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
fstr_sprintf( root_name, "%s\\root", get_global_sam_name() );
|
||||
ret = lookup_name( ctx, root_name, 0, NULL, NULL, &root_sid, &type );
|
||||
TALLOC_FREE( ctx );
|
||||
|
||||
if ( ret ) {
|
||||
status = pdb_add_aliasmem( &global_sid_Builtin_Administrators, &root_sid );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_administrators: Failed to add root"
|
||||
" Administrators\n"));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
Create a NT token for the user, expanding local aliases
|
||||
*******************************************************************/
|
||||
|
||||
static struct nt_user_token *create_local_nt_token(TALLOC_CTX *mem_ctx,
|
||||
const DOM_SID *user_sid,
|
||||
BOOL is_guest,
|
||||
int num_groupsids,
|
||||
const DOM_SID *groupsids)
|
||||
{
|
||||
struct nt_user_token *result = NULL;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
gid_t gid;
|
||||
|
||||
DEBUG(10, ("Create local NT token for %s\n", sid_string_static(user_sid)));
|
||||
|
||||
if (!(result = TALLOC_ZERO_P(mem_ctx, NT_USER_TOKEN))) {
|
||||
DEBUG(0, ("talloc failed\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add the user and primary group sid */
|
||||
|
||||
if (!add_sid_to_array(result, user_sid,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* For guest, num_groupsids may be zero. */
|
||||
if (num_groupsids) {
|
||||
if (!add_sid_to_array(result, &groupsids[0],
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add in BUILTIN sids */
|
||||
|
||||
if (!add_sid_to_array(result, &global_sid_World,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
if (!add_sid_to_array(result, &global_sid_Network,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (is_guest) {
|
||||
if (!add_sid_to_array(result, &global_sid_Builtin_Guests,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (!add_sid_to_array(result, &global_sid_Authenticated_Users,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now the SIDs we got from authentication. These are the ones from
|
||||
* the info3 struct or from the pdb_enum_group_memberships, depending
|
||||
* on who authenticated the user.
|
||||
* Note that we start the for loop at "1" here, we already added the
|
||||
* first group sid as primary above. */
|
||||
|
||||
for (i=1; i<num_groupsids; i++) {
|
||||
if (!add_sid_to_array_unique(result, &groupsids[i],
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deal with the BUILTIN\Administrators group. If the SID can
|
||||
be resolved then assume that the add_aliasmem( S-1-5-32 )
|
||||
handled it. */
|
||||
|
||||
if ( !sid_to_gid( &global_sid_Builtin_Administrators, &gid ) ) {
|
||||
/* We can only create a mapping if winbind is running
|
||||
and the nested group functionality has been enabled */
|
||||
|
||||
if ( lp_winbind_nested_groups() && winbind_ping() ) {
|
||||
become_root();
|
||||
status = create_builtin_administrators( );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(2,("create_local_nt_token: Failed to create BUILTIN\\Administrators group!\n"));
|
||||
/* don't fail, just log the message */
|
||||
}
|
||||
unbecome_root();
|
||||
}
|
||||
else {
|
||||
status = add_builtin_administrators( result );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
/* just log a complaint but do not fail */
|
||||
DEBUG(3,("create_local_nt_token: failed to check for local Administrators"
|
||||
" membership (%s)\n", nt_errstr(status)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Deal with the BUILTIN\Users group. If the SID can
|
||||
be resolved then assume that the add_aliasmem( S-1-5-32 )
|
||||
handled it. */
|
||||
|
||||
if ( !sid_to_gid( &global_sid_Builtin_Users, &gid ) ) {
|
||||
/* We can only create a mapping if winbind is running
|
||||
and the nested group functionality has been enabled */
|
||||
|
||||
if ( lp_winbind_nested_groups() && winbind_ping() ) {
|
||||
become_root();
|
||||
status = create_builtin_users( );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(2,("create_local_nt_token: Failed to create BUILTIN\\Users group!\n"));
|
||||
/* don't fail, just log the message */
|
||||
}
|
||||
unbecome_root();
|
||||
}
|
||||
}
|
||||
|
||||
/* Deal with local groups */
|
||||
|
||||
if (lp_winbind_nested_groups()) {
|
||||
|
||||
/* Now add the aliases. First the one from our local SAM */
|
||||
|
||||
status = add_aliases(get_global_sam_sid(), result);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
TALLOC_FREE(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Finally the builtin ones */
|
||||
|
||||
status = add_aliases(&global_sid_Builtin, result);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
TALLOC_FREE(result);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_privileges_for_sids(&result->privileges, result->user_sids,
|
||||
result->num_sids);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the token to use from server_info->sam_account and
|
||||
* server_info->sids (the info3/sam groups). Find the unix gids.
|
||||
@ -2039,89 +1690,6 @@ BOOL make_auth_methods(struct auth_context *auth_context, auth_methods **auth_me
|
||||
return True;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Duplicate a SID token.
|
||||
****************************************************************************/
|
||||
|
||||
NT_USER_TOKEN *dup_nt_token(TALLOC_CTX *mem_ctx, const NT_USER_TOKEN *ptoken)
|
||||
{
|
||||
NT_USER_TOKEN *token;
|
||||
|
||||
if (!ptoken)
|
||||
return NULL;
|
||||
|
||||
token = TALLOC_P(mem_ctx, NT_USER_TOKEN);
|
||||
if (token == NULL) {
|
||||
DEBUG(0, ("talloc failed\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZERO_STRUCTP(token);
|
||||
|
||||
if (ptoken->user_sids && ptoken->num_sids) {
|
||||
token->user_sids = (DOM_SID *)talloc_memdup(
|
||||
token, ptoken->user_sids, sizeof(DOM_SID) * ptoken->num_sids );
|
||||
|
||||
if (token->user_sids == NULL) {
|
||||
DEBUG(0, ("talloc_memdup failed\n"));
|
||||
TALLOC_FREE(token);
|
||||
return NULL;
|
||||
}
|
||||
token->num_sids = ptoken->num_sids;
|
||||
}
|
||||
|
||||
/* copy the privileges; don't consider failure to be critical here */
|
||||
|
||||
if ( !se_priv_copy( &token->privileges, &ptoken->privileges ) ) {
|
||||
DEBUG(0,("dup_nt_token: Failure to copy SE_PRIV!. "
|
||||
"Continuing with 0 privileges assigned.\n"));
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Check for a SID in an NT_USER_TOKEN
|
||||
****************************************************************************/
|
||||
|
||||
BOOL nt_token_check_sid ( const DOM_SID *sid, const NT_USER_TOKEN *token )
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( !sid || !token )
|
||||
return False;
|
||||
|
||||
for ( i=0; i<token->num_sids; i++ ) {
|
||||
if ( sid_equal( sid, &token->user_sids[i] ) )
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL nt_token_check_domain_rid( NT_USER_TOKEN *token, uint32 rid )
|
||||
{
|
||||
DOM_SID domain_sid;
|
||||
|
||||
/* if we are a domain member, the get the domain SID, else for
|
||||
a DC or standalone server, use our own SID */
|
||||
|
||||
if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
|
||||
if ( !secrets_fetch_domain_sid( lp_workgroup(),
|
||||
&domain_sid ) ) {
|
||||
DEBUG(1,("nt_token_check_domain_rid: Cannot lookup "
|
||||
"SID for domain [%s]\n", lp_workgroup()));
|
||||
return False;
|
||||
}
|
||||
}
|
||||
else
|
||||
sid_copy( &domain_sid, get_global_sam_sid() );
|
||||
|
||||
sid_append_rid( &domain_sid, rid );
|
||||
|
||||
return nt_token_check_sid( &domain_sid, token );\
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify whether or not given domain is trusted.
|
||||
*
|
||||
|
458
source/auth/token_util.c
Normal file
458
source/auth/token_util.c
Normal file
@ -0,0 +1,458 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* Authentication utility functions
|
||||
* Copyright (C) Andrew Tridgell 1992-1998
|
||||
* Copyright (C) Andrew Bartlett 2001
|
||||
* Copyright (C) Jeremy Allison 2000-2001
|
||||
* Copyright (C) Rafal Szczesniak 2002
|
||||
* Copyright (C) Volker Lendecke 2006
|
||||
* Copyright (C) Michael Adam 2007
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* functions moved from auth/auth_util.c to minimize linker deps */
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
/****************************************************************************
|
||||
Duplicate a SID token.
|
||||
****************************************************************************/
|
||||
|
||||
NT_USER_TOKEN *dup_nt_token(TALLOC_CTX *mem_ctx, const NT_USER_TOKEN *ptoken)
|
||||
{
|
||||
NT_USER_TOKEN *token;
|
||||
|
||||
if (!ptoken)
|
||||
return NULL;
|
||||
|
||||
token = TALLOC_P(mem_ctx, NT_USER_TOKEN);
|
||||
if (token == NULL) {
|
||||
DEBUG(0, ("talloc failed\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZERO_STRUCTP(token);
|
||||
|
||||
if (ptoken->user_sids && ptoken->num_sids) {
|
||||
token->user_sids = (DOM_SID *)talloc_memdup(
|
||||
token, ptoken->user_sids, sizeof(DOM_SID) * ptoken->num_sids );
|
||||
|
||||
if (token->user_sids == NULL) {
|
||||
DEBUG(0, ("talloc_memdup failed\n"));
|
||||
TALLOC_FREE(token);
|
||||
return NULL;
|
||||
}
|
||||
token->num_sids = ptoken->num_sids;
|
||||
}
|
||||
|
||||
/* copy the privileges; don't consider failure to be critical here */
|
||||
|
||||
if ( !se_priv_copy( &token->privileges, &ptoken->privileges ) ) {
|
||||
DEBUG(0,("dup_nt_token: Failure to copy SE_PRIV!. "
|
||||
"Continuing with 0 privileges assigned.\n"));
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Check for a SID in an NT_USER_TOKEN
|
||||
****************************************************************************/
|
||||
|
||||
BOOL nt_token_check_sid ( const DOM_SID *sid, const NT_USER_TOKEN *token )
|
||||
{
|
||||
int i;
|
||||
|
||||
if ( !sid || !token )
|
||||
return False;
|
||||
|
||||
for ( i=0; i<token->num_sids; i++ ) {
|
||||
if ( sid_equal( sid, &token->user_sids[i] ) )
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
BOOL nt_token_check_domain_rid( NT_USER_TOKEN *token, uint32 rid )
|
||||
{
|
||||
DOM_SID domain_sid;
|
||||
|
||||
/* if we are a domain member, the get the domain SID, else for
|
||||
a DC or standalone server, use our own SID */
|
||||
|
||||
if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
|
||||
if ( !secrets_fetch_domain_sid( lp_workgroup(),
|
||||
&domain_sid ) ) {
|
||||
DEBUG(1,("nt_token_check_domain_rid: Cannot lookup "
|
||||
"SID for domain [%s]\n", lp_workgroup()));
|
||||
return False;
|
||||
}
|
||||
}
|
||||
else
|
||||
sid_copy( &domain_sid, get_global_sam_sid() );
|
||||
|
||||
sid_append_rid( &domain_sid, rid );
|
||||
|
||||
return nt_token_check_sid( &domain_sid, token );\
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Create a token for the root user to be used internally by smbd.
|
||||
This is similar to running under the context of the LOCAL_SYSTEM account
|
||||
in Windows. This is a read-only token. Do not modify it or free() it.
|
||||
Create a copy if your need to change it.
|
||||
******************************************************************************/
|
||||
|
||||
NT_USER_TOKEN *get_root_nt_token( void )
|
||||
{
|
||||
static NT_USER_TOKEN *token = NULL;
|
||||
DOM_SID u_sid, g_sid;
|
||||
struct passwd *pw;
|
||||
|
||||
if ( token )
|
||||
return token;
|
||||
|
||||
if ( !(pw = sys_getpwnam( "root" )) ) {
|
||||
DEBUG(0,("get_root_nt_token: getpwnam\"root\") failed!\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get the user and primary group SIDs; although the
|
||||
BUILTIN\Administrators SId is really the one that matters here */
|
||||
|
||||
uid_to_sid(&u_sid, pw->pw_uid);
|
||||
gid_to_sid(&g_sid, pw->pw_gid);
|
||||
|
||||
token = create_local_nt_token(NULL, &u_sid, False,
|
||||
1, &global_sid_Builtin_Administrators);
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add alias SIDs from memberships within the partially created token SID list
|
||||
*/
|
||||
|
||||
static NTSTATUS add_aliases(const DOM_SID *domain_sid,
|
||||
struct nt_user_token *token)
|
||||
{
|
||||
uint32 *aliases;
|
||||
size_t i, num_aliases;
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
|
||||
if (!(tmp_ctx = talloc_init("add_aliases"))) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
aliases = NULL;
|
||||
num_aliases = 0;
|
||||
|
||||
status = pdb_enum_alias_memberships(tmp_ctx, domain_sid,
|
||||
token->user_sids,
|
||||
token->num_sids,
|
||||
&aliases, &num_aliases);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
DEBUG(10, ("pdb_enum_alias_memberships failed: %s\n",
|
||||
nt_errstr(status)));
|
||||
TALLOC_FREE(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i=0; i<num_aliases; i++) {
|
||||
DOM_SID alias_sid;
|
||||
sid_compose(&alias_sid, domain_sid, aliases[i]);
|
||||
if (!add_sid_to_array_unique(token, &alias_sid,
|
||||
&token->user_sids,
|
||||
&token->num_sids)) {
|
||||
DEBUG(0, ("add_sid_to_array failed\n"));
|
||||
TALLOC_FREE(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
TALLOC_FREE(tmp_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
*******************************************************************/
|
||||
|
||||
static NTSTATUS add_builtin_administrators( struct nt_user_token *token )
|
||||
{
|
||||
DOM_SID domadm;
|
||||
|
||||
/* nothing to do if we aren't in a domain */
|
||||
|
||||
if ( !(IS_DC || lp_server_role()==ROLE_DOMAIN_MEMBER) ) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* Find the Domain Admins SID */
|
||||
|
||||
if ( IS_DC ) {
|
||||
sid_copy( &domadm, get_global_sam_sid() );
|
||||
} else {
|
||||
if ( !secrets_fetch_domain_sid( lp_workgroup(), &domadm ) )
|
||||
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
|
||||
}
|
||||
sid_append_rid( &domadm, DOMAIN_GROUP_RID_ADMINS );
|
||||
|
||||
/* Add Administrators if the user beloongs to Domain Admins */
|
||||
|
||||
if ( nt_token_check_sid( &domadm, token ) ) {
|
||||
if (!add_sid_to_array(token, &global_sid_Builtin_Administrators,
|
||||
&token->user_sids, &token->num_sids)) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
*******************************************************************/
|
||||
|
||||
static NTSTATUS create_builtin_users( void )
|
||||
{
|
||||
NTSTATUS status;
|
||||
DOM_SID dom_users;
|
||||
|
||||
status = pdb_create_builtin_alias( BUILTIN_ALIAS_RID_USERS );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_users: Failed to create Users\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* add domain users */
|
||||
if ((IS_DC || (lp_server_role() == ROLE_DOMAIN_MEMBER))
|
||||
&& secrets_fetch_domain_sid(lp_workgroup(), &dom_users))
|
||||
{
|
||||
sid_append_rid(&dom_users, DOMAIN_GROUP_RID_USERS );
|
||||
status = pdb_add_aliasmem( &global_sid_Builtin_Users, &dom_users);
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_administrators: Failed to add Domain Users to"
|
||||
" Users\n"));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************
|
||||
*******************************************************************/
|
||||
|
||||
static NTSTATUS create_builtin_administrators( void )
|
||||
{
|
||||
NTSTATUS status;
|
||||
DOM_SID dom_admins, root_sid;
|
||||
fstring root_name;
|
||||
enum lsa_SidType type;
|
||||
TALLOC_CTX *ctx;
|
||||
BOOL ret;
|
||||
|
||||
status = pdb_create_builtin_alias( BUILTIN_ALIAS_RID_ADMINS );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_administrators: Failed to create Administrators\n"));
|
||||
return status;
|
||||
}
|
||||
|
||||
/* add domain admins */
|
||||
if ((IS_DC || (lp_server_role() == ROLE_DOMAIN_MEMBER))
|
||||
&& secrets_fetch_domain_sid(lp_workgroup(), &dom_admins))
|
||||
{
|
||||
sid_append_rid(&dom_admins, DOMAIN_GROUP_RID_ADMINS);
|
||||
status = pdb_add_aliasmem( &global_sid_Builtin_Administrators, &dom_admins );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_administrators: Failed to add Domain Admins"
|
||||
" Administrators\n"));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* add root */
|
||||
if ( (ctx = talloc_init("create_builtin_administrators")) == NULL ) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
fstr_sprintf( root_name, "%s\\root", get_global_sam_name() );
|
||||
ret = lookup_name( ctx, root_name, 0, NULL, NULL, &root_sid, &type );
|
||||
TALLOC_FREE( ctx );
|
||||
|
||||
if ( ret ) {
|
||||
status = pdb_add_aliasmem( &global_sid_Builtin_Administrators, &root_sid );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(0,("create_builtin_administrators: Failed to add root"
|
||||
" Administrators\n"));
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
Create a NT token for the user, expanding local aliases
|
||||
*******************************************************************/
|
||||
|
||||
struct nt_user_token *create_local_nt_token(TALLOC_CTX *mem_ctx,
|
||||
const DOM_SID *user_sid,
|
||||
BOOL is_guest,
|
||||
int num_groupsids,
|
||||
const DOM_SID *groupsids)
|
||||
{
|
||||
struct nt_user_token *result = NULL;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
gid_t gid;
|
||||
|
||||
DEBUG(10, ("Create local NT token for %s\n", sid_string_static(user_sid)));
|
||||
|
||||
if (!(result = TALLOC_ZERO_P(mem_ctx, NT_USER_TOKEN))) {
|
||||
DEBUG(0, ("talloc failed\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add the user and primary group sid */
|
||||
|
||||
if (!add_sid_to_array(result, user_sid,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* For guest, num_groupsids may be zero. */
|
||||
if (num_groupsids) {
|
||||
if (!add_sid_to_array(result, &groupsids[0],
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add in BUILTIN sids */
|
||||
|
||||
if (!add_sid_to_array(result, &global_sid_World,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
if (!add_sid_to_array(result, &global_sid_Network,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (is_guest) {
|
||||
if (!add_sid_to_array(result, &global_sid_Builtin_Guests,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
if (!add_sid_to_array(result, &global_sid_Authenticated_Users,
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now the SIDs we got from authentication. These are the ones from
|
||||
* the info3 struct or from the pdb_enum_group_memberships, depending
|
||||
* on who authenticated the user.
|
||||
* Note that we start the for loop at "1" here, we already added the
|
||||
* first group sid as primary above. */
|
||||
|
||||
for (i=1; i<num_groupsids; i++) {
|
||||
if (!add_sid_to_array_unique(result, &groupsids[i],
|
||||
&result->user_sids, &result->num_sids)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deal with the BUILTIN\Administrators group. If the SID can
|
||||
be resolved then assume that the add_aliasmem( S-1-5-32 )
|
||||
handled it. */
|
||||
|
||||
if ( !sid_to_gid( &global_sid_Builtin_Administrators, &gid ) ) {
|
||||
/* We can only create a mapping if winbind is running
|
||||
and the nested group functionality has been enabled */
|
||||
|
||||
if ( lp_winbind_nested_groups() && winbind_ping() ) {
|
||||
become_root();
|
||||
status = create_builtin_administrators( );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(2,("create_local_nt_token: Failed to create BUILTIN\\Administrators group!\n"));
|
||||
/* don't fail, just log the message */
|
||||
}
|
||||
unbecome_root();
|
||||
}
|
||||
else {
|
||||
status = add_builtin_administrators( result );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
/* just log a complaint but do not fail */
|
||||
DEBUG(3,("create_local_nt_token: failed to check for local Administrators"
|
||||
" membership (%s)\n", nt_errstr(status)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Deal with the BUILTIN\Users group. If the SID can
|
||||
be resolved then assume that the add_aliasmem( S-1-5-32 )
|
||||
handled it. */
|
||||
|
||||
if ( !sid_to_gid( &global_sid_Builtin_Users, &gid ) ) {
|
||||
/* We can only create a mapping if winbind is running
|
||||
and the nested group functionality has been enabled */
|
||||
|
||||
if ( lp_winbind_nested_groups() && winbind_ping() ) {
|
||||
become_root();
|
||||
status = create_builtin_users( );
|
||||
if ( !NT_STATUS_IS_OK(status) ) {
|
||||
DEBUG(2,("create_local_nt_token: Failed to create BUILTIN\\Users group!\n"));
|
||||
/* don't fail, just log the message */
|
||||
}
|
||||
unbecome_root();
|
||||
}
|
||||
}
|
||||
|
||||
/* Deal with local groups */
|
||||
|
||||
if (lp_winbind_nested_groups()) {
|
||||
|
||||
/* Now add the aliases. First the one from our local SAM */
|
||||
|
||||
status = add_aliases(get_global_sam_sid(), result);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
TALLOC_FREE(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Finally the builtin ones */
|
||||
|
||||
status = add_aliases(&global_sid_Builtin, result);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
TALLOC_FREE(result);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_privileges_for_sids(&result->privileges, result->user_sids,
|
||||
result->num_sids);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* END */
|
@ -496,3 +496,63 @@ WERROR reg_deletevalue(struct registry_key *key, const char *name)
|
||||
return WERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Utility function to open a complete registry path including the hive
|
||||
* prefix. This should become the replacement function for
|
||||
* regkey_open_internal.
|
||||
*/
|
||||
|
||||
WERROR reg_open_path(TALLOC_CTX *mem_ctx, const char *orig_path,
|
||||
uint32 desired_access, const struct nt_user_token *token,
|
||||
struct registry_key **pkey)
|
||||
{
|
||||
struct registry_key *hive, *key;
|
||||
char *path, *p;
|
||||
WERROR err;
|
||||
|
||||
if (!(path = SMB_STRDUP(orig_path))) {
|
||||
return WERR_NOMEM;
|
||||
}
|
||||
|
||||
p = strchr(path, '\\');
|
||||
|
||||
if ((p == NULL) || (p[1] == '\0')) {
|
||||
/*
|
||||
* No key behind the hive, just return the hive
|
||||
*/
|
||||
|
||||
err = reg_openhive(mem_ctx, path, desired_access, token,
|
||||
&hive);
|
||||
if (!W_ERROR_IS_OK(err)) {
|
||||
SAFE_FREE(path);
|
||||
return err;
|
||||
}
|
||||
SAFE_FREE(path);
|
||||
*pkey = hive;
|
||||
return WERR_OK;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
|
||||
err = reg_openhive(mem_ctx, path, SEC_RIGHTS_ENUM_SUBKEYS, token,
|
||||
&hive);
|
||||
if (!W_ERROR_IS_OK(err)) {
|
||||
SAFE_FREE(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = reg_openkey(mem_ctx, hive, p+1, desired_access, &key);
|
||||
|
||||
TALLOC_FREE(hive);
|
||||
SAFE_FREE(path);
|
||||
|
||||
if (!W_ERROR_IS_OK(err)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*pkey = key;
|
||||
return WERR_OK;
|
||||
}
|
||||
|
||||
/* END */
|
||||
|
@ -45,45 +45,6 @@ REGISTRY_HOOK reg_hooks[] = {
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
static struct generic_mapping reg_generic_map =
|
||||
{ REG_KEY_READ, REG_KEY_WRITE, REG_KEY_EXECUTE, REG_KEY_ALL };
|
||||
|
||||
/********************************************************************
|
||||
********************************************************************/
|
||||
|
||||
static SEC_DESC* construct_registry_sd( TALLOC_CTX *ctx )
|
||||
{
|
||||
SEC_ACE ace[2];
|
||||
SEC_ACCESS mask;
|
||||
size_t i = 0;
|
||||
SEC_DESC *sd;
|
||||
SEC_ACL *acl;
|
||||
size_t sd_size;
|
||||
|
||||
/* basic access for Everyone */
|
||||
|
||||
init_sec_access(&mask, REG_KEY_READ );
|
||||
init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
|
||||
|
||||
/* Full Access 'BUILTIN\Administrators' */
|
||||
|
||||
init_sec_access(&mask, REG_KEY_ALL );
|
||||
init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
|
||||
|
||||
|
||||
/* create the security descriptor */
|
||||
|
||||
if ( !(acl = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace)) )
|
||||
return NULL;
|
||||
|
||||
if ( !(sd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, acl, &sd_size)) )
|
||||
return NULL;
|
||||
|
||||
return sd;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
Open the registry database and initialize the REGISTRY_HOOK cache
|
||||
***********************************************************************/
|
||||
@ -123,231 +84,6 @@ BOOL init_registry( void )
|
||||
return True;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for storing registry subkeys
|
||||
***********************************************************************/
|
||||
|
||||
BOOL store_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys )
|
||||
{
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->store_subkeys )
|
||||
return key->hook->ops->store_subkeys( key->name, subkeys );
|
||||
|
||||
return False;
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for storing registry values
|
||||
***********************************************************************/
|
||||
|
||||
BOOL store_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
|
||||
{
|
||||
if ( check_dynamic_reg_values( key ) )
|
||||
return False;
|
||||
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->store_values )
|
||||
return key->hook->ops->store_values( key->name, val );
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for enumerating registry subkeys
|
||||
Initialize the TALLOC_CTX if necessary
|
||||
***********************************************************************/
|
||||
|
||||
int fetch_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkey_ctr )
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->fetch_subkeys )
|
||||
result = key->hook->ops->fetch_subkeys( key->name, subkey_ctr );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for enumerating registry values
|
||||
***********************************************************************/
|
||||
|
||||
int fetch_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->fetch_values )
|
||||
result = key->hook->ops->fetch_values( key->name, val );
|
||||
|
||||
/* if the backend lookup returned no data, try the dynamic overlay */
|
||||
|
||||
if ( result == 0 ) {
|
||||
result = fetch_dynamic_reg_values( key, val );
|
||||
|
||||
return ( result != -1 ) ? result : 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level access check for passing the required access mask to the
|
||||
underlying registry backend
|
||||
***********************************************************************/
|
||||
|
||||
BOOL regkey_access_check( REGISTRY_KEY *key, uint32 requested, uint32 *granted,
|
||||
const struct nt_user_token *token )
|
||||
{
|
||||
SEC_DESC *sec_desc;
|
||||
NTSTATUS status;
|
||||
WERROR err;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
/* use the default security check if the backend has not defined its
|
||||
* own */
|
||||
|
||||
if (key->hook && key->hook->ops && key->hook->ops->reg_access_check) {
|
||||
return key->hook->ops->reg_access_check( key->name, requested,
|
||||
granted, token );
|
||||
}
|
||||
|
||||
/*
|
||||
* The secdesc routines can't yet cope with a NULL talloc ctx sanely.
|
||||
*/
|
||||
|
||||
if (!(mem_ctx = talloc_init("regkey_access_check"))) {
|
||||
return False;
|
||||
}
|
||||
|
||||
err = regkey_get_secdesc(mem_ctx, key, &sec_desc);
|
||||
|
||||
if (!W_ERROR_IS_OK(err)) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return False;
|
||||
}
|
||||
|
||||
se_map_generic( &requested, ®_generic_map );
|
||||
|
||||
if (!se_access_check(sec_desc, token, requested, granted, &status)) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return False;
|
||||
}
|
||||
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_IS_OK(status);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
***********************************************************************/
|
||||
|
||||
static int regkey_destructor(REGISTRY_KEY *key)
|
||||
{
|
||||
return regdb_close();
|
||||
}
|
||||
|
||||
WERROR regkey_open_onelevel( TALLOC_CTX *mem_ctx, struct registry_key *parent,
|
||||
const char *name,
|
||||
const struct nt_user_token *token,
|
||||
uint32 access_desired,
|
||||
struct registry_key **pregkey)
|
||||
{
|
||||
WERROR result = WERR_OK;
|
||||
struct registry_key *regkey;
|
||||
REGISTRY_KEY *key;
|
||||
REGSUBKEY_CTR *subkeys = NULL;
|
||||
|
||||
DEBUG(7,("regkey_open_onelevel: name = [%s]\n", name));
|
||||
|
||||
SMB_ASSERT(strchr(name, '\\') == NULL);
|
||||
|
||||
if (!(regkey = TALLOC_ZERO_P(mem_ctx, struct registry_key)) ||
|
||||
!(regkey->token = dup_nt_token(regkey, token)) ||
|
||||
!(regkey->key = TALLOC_ZERO_P(regkey, REGISTRY_KEY))) {
|
||||
result = WERR_NOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ( !(W_ERROR_IS_OK(result = regdb_open())) ) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
key = regkey->key;
|
||||
talloc_set_destructor(key, regkey_destructor);
|
||||
|
||||
/* initialization */
|
||||
|
||||
key->type = REG_KEY_GENERIC;
|
||||
|
||||
if (name[0] == '\0') {
|
||||
/*
|
||||
* Open a copy of the parent key
|
||||
*/
|
||||
if (!parent) {
|
||||
result = WERR_BADFILE;
|
||||
goto done;
|
||||
}
|
||||
key->name = talloc_strdup(key, parent->key->name);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Normal subkey open
|
||||
*/
|
||||
key->name = talloc_asprintf(key, "%s%s%s",
|
||||
parent ? parent->key->name : "",
|
||||
parent ? "\\": "",
|
||||
name);
|
||||
}
|
||||
|
||||
if (key->name == NULL) {
|
||||
result = WERR_NOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Tag this as a Performance Counter Key */
|
||||
|
||||
if( StrnCaseCmp(key->name, KEY_HKPD, strlen(KEY_HKPD)) == 0 )
|
||||
key->type = REG_KEY_HKPD;
|
||||
|
||||
/* Look up the table of registry I/O operations */
|
||||
|
||||
if ( !(key->hook = reghook_cache_find( key->name )) ) {
|
||||
DEBUG(0,("reg_open_onelevel: Failed to assigned a "
|
||||
"REGISTRY_HOOK to [%s]\n", key->name ));
|
||||
result = WERR_BADFILE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* check if the path really exists; failed is indicated by -1 */
|
||||
/* if the subkey count failed, bail out */
|
||||
|
||||
if ( !(subkeys = TALLOC_ZERO_P( key, REGSUBKEY_CTR )) ) {
|
||||
result = WERR_NOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ( fetch_reg_keys( key, subkeys ) == -1 ) {
|
||||
result = WERR_BADFILE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
TALLOC_FREE( subkeys );
|
||||
|
||||
if ( !regkey_access_check( key, access_desired, &key->access_granted,
|
||||
token ) ) {
|
||||
result = WERR_ACCESS_DENIED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
*pregkey = regkey;
|
||||
result = WERR_OK;
|
||||
|
||||
done:
|
||||
if ( !W_ERROR_IS_OK(result) ) {
|
||||
TALLOC_FREE(regkey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WERROR regkey_open_internal( TALLOC_CTX *ctx, REGISTRY_KEY **regkey,
|
||||
const char *path,
|
||||
const struct nt_user_token *token,
|
||||
@ -366,29 +102,6 @@ WERROR regkey_open_internal( TALLOC_CTX *ctx, REGISTRY_KEY **regkey,
|
||||
return WERR_OK;
|
||||
}
|
||||
|
||||
WERROR regkey_get_secdesc(TALLOC_CTX *mem_ctx, REGISTRY_KEY *key,
|
||||
struct security_descriptor **psecdesc)
|
||||
{
|
||||
struct security_descriptor *secdesc;
|
||||
|
||||
if (key->hook && key->hook->ops && key->hook->ops->get_secdesc) {
|
||||
WERROR err;
|
||||
|
||||
err = key->hook->ops->get_secdesc(mem_ctx, key->name,
|
||||
psecdesc);
|
||||
if (W_ERROR_IS_OK(err)) {
|
||||
return WERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(secdesc = construct_registry_sd(mem_ctx))) {
|
||||
return WERR_NOMEM;
|
||||
}
|
||||
|
||||
*psecdesc = secdesc;
|
||||
return WERR_OK;
|
||||
}
|
||||
|
||||
WERROR regkey_set_secdesc(REGISTRY_KEY *key,
|
||||
struct security_descriptor *psecdesc)
|
||||
{
|
||||
@ -399,65 +112,6 @@ WERROR regkey_set_secdesc(REGISTRY_KEY *key,
|
||||
return WERR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Utility function to open a complete registry path including the hive
|
||||
* prefix. This should become the replacement function for
|
||||
* regkey_open_internal.
|
||||
*/
|
||||
|
||||
WERROR reg_open_path(TALLOC_CTX *mem_ctx, const char *orig_path,
|
||||
uint32 desired_access, const struct nt_user_token *token,
|
||||
struct registry_key **pkey)
|
||||
{
|
||||
struct registry_key *hive, *key;
|
||||
char *path, *p;
|
||||
WERROR err;
|
||||
|
||||
if (!(path = SMB_STRDUP(orig_path))) {
|
||||
return WERR_NOMEM;
|
||||
}
|
||||
|
||||
p = strchr(path, '\\');
|
||||
|
||||
if ((p == NULL) || (p[1] == '\0')) {
|
||||
/*
|
||||
* No key behind the hive, just return the hive
|
||||
*/
|
||||
|
||||
err = reg_openhive(mem_ctx, path, desired_access, token,
|
||||
&hive);
|
||||
if (!W_ERROR_IS_OK(err)) {
|
||||
SAFE_FREE(path);
|
||||
return err;
|
||||
}
|
||||
SAFE_FREE(path);
|
||||
*pkey = hive;
|
||||
return WERR_OK;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
|
||||
err = reg_openhive(mem_ctx, path, SEC_RIGHTS_ENUM_SUBKEYS, token,
|
||||
&hive);
|
||||
if (!W_ERROR_IS_OK(err)) {
|
||||
SAFE_FREE(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = reg_openkey(mem_ctx, hive, p+1, desired_access, &key);
|
||||
|
||||
TALLOC_FREE(hive);
|
||||
SAFE_FREE(path);
|
||||
|
||||
if (!W_ERROR_IS_OK(err)) {
|
||||
return err;
|
||||
}
|
||||
|
||||
*pkey = key;
|
||||
return WERR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to create a registry key without opening the hive
|
||||
* before. Assumes the hive already exists.
|
||||
|
315
source/registry/reg_frontend_hilvl.c
Normal file
315
source/registry/reg_frontend_hilvl.c
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Unix SMB/CIFS implementation.
|
||||
* Virtual Windows Registry Layer
|
||||
* Copyright (C) Gerald Carter 2002-2005
|
||||
* Copyright (C) Michael Adam 2006
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Implementation of registry frontend view functions.
|
||||
* Functions moved from reg_frontend.c to minimize linker deps.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
|
||||
static struct generic_mapping reg_generic_map =
|
||||
{ REG_KEY_READ, REG_KEY_WRITE, REG_KEY_EXECUTE, REG_KEY_ALL };
|
||||
|
||||
/********************************************************************
|
||||
********************************************************************/
|
||||
|
||||
static SEC_DESC* construct_registry_sd( TALLOC_CTX *ctx )
|
||||
{
|
||||
SEC_ACE ace[2];
|
||||
SEC_ACCESS mask;
|
||||
size_t i = 0;
|
||||
SEC_DESC *sd;
|
||||
SEC_ACL *acl;
|
||||
size_t sd_size;
|
||||
|
||||
/* basic access for Everyone */
|
||||
|
||||
init_sec_access(&mask, REG_KEY_READ );
|
||||
init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
|
||||
|
||||
/* Full Access 'BUILTIN\Administrators' */
|
||||
|
||||
init_sec_access(&mask, REG_KEY_ALL );
|
||||
init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
|
||||
|
||||
|
||||
/* create the security descriptor */
|
||||
|
||||
if ( !(acl = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace)) )
|
||||
return NULL;
|
||||
|
||||
if ( !(sd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, acl, &sd_size)) )
|
||||
return NULL;
|
||||
|
||||
return sd;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for storing registry subkeys
|
||||
***********************************************************************/
|
||||
|
||||
BOOL store_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys )
|
||||
{
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->store_subkeys )
|
||||
return key->hook->ops->store_subkeys( key->name, subkeys );
|
||||
|
||||
return False;
|
||||
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for storing registry values
|
||||
***********************************************************************/
|
||||
|
||||
BOOL store_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
|
||||
{
|
||||
if ( check_dynamic_reg_values( key ) )
|
||||
return False;
|
||||
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->store_values )
|
||||
return key->hook->ops->store_values( key->name, val );
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for enumerating registry subkeys
|
||||
Initialize the TALLOC_CTX if necessary
|
||||
***********************************************************************/
|
||||
|
||||
int fetch_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkey_ctr )
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->fetch_subkeys )
|
||||
result = key->hook->ops->fetch_subkeys( key->name, subkey_ctr );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level wrapper function for enumerating registry values
|
||||
***********************************************************************/
|
||||
|
||||
int fetch_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
|
||||
{
|
||||
int result = -1;
|
||||
|
||||
if ( key->hook && key->hook->ops && key->hook->ops->fetch_values )
|
||||
result = key->hook->ops->fetch_values( key->name, val );
|
||||
|
||||
/* if the backend lookup returned no data, try the dynamic overlay */
|
||||
|
||||
if ( result == 0 ) {
|
||||
result = fetch_dynamic_reg_values( key, val );
|
||||
|
||||
return ( result != -1 ) ? result : 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
High level access check for passing the required access mask to the
|
||||
underlying registry backend
|
||||
***********************************************************************/
|
||||
|
||||
BOOL regkey_access_check( REGISTRY_KEY *key, uint32 requested, uint32 *granted,
|
||||
const struct nt_user_token *token )
|
||||
{
|
||||
SEC_DESC *sec_desc;
|
||||
NTSTATUS status;
|
||||
WERROR err;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
/* use the default security check if the backend has not defined its
|
||||
* own */
|
||||
|
||||
if (key->hook && key->hook->ops && key->hook->ops->reg_access_check) {
|
||||
return key->hook->ops->reg_access_check( key->name, requested,
|
||||
granted, token );
|
||||
}
|
||||
|
||||
/*
|
||||
* The secdesc routines can't yet cope with a NULL talloc ctx sanely.
|
||||
*/
|
||||
|
||||
if (!(mem_ctx = talloc_init("regkey_access_check"))) {
|
||||
return False;
|
||||
}
|
||||
|
||||
err = regkey_get_secdesc(mem_ctx, key, &sec_desc);
|
||||
|
||||
if (!W_ERROR_IS_OK(err)) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return False;
|
||||
}
|
||||
|
||||
se_map_generic( &requested, ®_generic_map );
|
||||
|
||||
if (!se_access_check(sec_desc, token, requested, granted, &status)) {
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return False;
|
||||
}
|
||||
|
||||
TALLOC_FREE(mem_ctx);
|
||||
return NT_STATUS_IS_OK(status);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
***********************************************************************/
|
||||
|
||||
static int regkey_destructor(REGISTRY_KEY *key)
|
||||
{
|
||||
return regdb_close();
|
||||
}
|
||||
|
||||
WERROR regkey_open_onelevel( TALLOC_CTX *mem_ctx, struct registry_key *parent,
|
||||
const char *name,
|
||||
const struct nt_user_token *token,
|
||||
uint32 access_desired,
|
||||
struct registry_key **pregkey)
|
||||
{
|
||||
WERROR result = WERR_OK;
|
||||
struct registry_key *regkey;
|
||||
REGISTRY_KEY *key;
|
||||
REGSUBKEY_CTR *subkeys = NULL;
|
||||
|
||||
DEBUG(7,("regkey_open_onelevel: name = [%s]\n", name));
|
||||
|
||||
SMB_ASSERT(strchr(name, '\\') == NULL);
|
||||
|
||||
if (!(regkey = TALLOC_ZERO_P(mem_ctx, struct registry_key)) ||
|
||||
!(regkey->token = dup_nt_token(regkey, token)) ||
|
||||
!(regkey->key = TALLOC_ZERO_P(regkey, REGISTRY_KEY))) {
|
||||
result = WERR_NOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ( !(W_ERROR_IS_OK(result = regdb_open())) ) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
key = regkey->key;
|
||||
talloc_set_destructor(key, regkey_destructor);
|
||||
|
||||
/* initialization */
|
||||
|
||||
key->type = REG_KEY_GENERIC;
|
||||
|
||||
if (name[0] == '\0') {
|
||||
/*
|
||||
* Open a copy of the parent key
|
||||
*/
|
||||
if (!parent) {
|
||||
result = WERR_BADFILE;
|
||||
goto done;
|
||||
}
|
||||
key->name = talloc_strdup(key, parent->key->name);
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Normal subkey open
|
||||
*/
|
||||
key->name = talloc_asprintf(key, "%s%s%s",
|
||||
parent ? parent->key->name : "",
|
||||
parent ? "\\": "",
|
||||
name);
|
||||
}
|
||||
|
||||
if (key->name == NULL) {
|
||||
result = WERR_NOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Tag this as a Performance Counter Key */
|
||||
|
||||
if( StrnCaseCmp(key->name, KEY_HKPD, strlen(KEY_HKPD)) == 0 )
|
||||
key->type = REG_KEY_HKPD;
|
||||
|
||||
/* Look up the table of registry I/O operations */
|
||||
|
||||
if ( !(key->hook = reghook_cache_find( key->name )) ) {
|
||||
DEBUG(0,("reg_open_onelevel: Failed to assigned a "
|
||||
"REGISTRY_HOOK to [%s]\n", key->name ));
|
||||
result = WERR_BADFILE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* check if the path really exists; failed is indicated by -1 */
|
||||
/* if the subkey count failed, bail out */
|
||||
|
||||
if ( !(subkeys = TALLOC_ZERO_P( key, REGSUBKEY_CTR )) ) {
|
||||
result = WERR_NOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ( fetch_reg_keys( key, subkeys ) == -1 ) {
|
||||
result = WERR_BADFILE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
TALLOC_FREE( subkeys );
|
||||
|
||||
if ( !regkey_access_check( key, access_desired, &key->access_granted,
|
||||
token ) ) {
|
||||
result = WERR_ACCESS_DENIED;
|
||||
goto done;
|
||||
}
|
||||
|
||||
*pregkey = regkey;
|
||||
result = WERR_OK;
|
||||
|
||||
done:
|
||||
if ( !W_ERROR_IS_OK(result) ) {
|
||||
TALLOC_FREE(regkey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WERROR regkey_get_secdesc(TALLOC_CTX *mem_ctx, REGISTRY_KEY *key,
|
||||
struct security_descriptor **psecdesc)
|
||||
{
|
||||
struct security_descriptor *secdesc;
|
||||
|
||||
if (key->hook && key->hook->ops && key->hook->ops->get_secdesc) {
|
||||
WERROR err;
|
||||
|
||||
err = key->hook->ops->get_secdesc(mem_ctx, key->name,
|
||||
psecdesc);
|
||||
if (W_ERROR_IS_OK(err)) {
|
||||
return WERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(secdesc = construct_registry_sd(mem_ctx))) {
|
||||
return WERR_NOMEM;
|
||||
}
|
||||
|
||||
*psecdesc = secdesc;
|
||||
return WERR_OK;
|
||||
}
|
||||
|
||||
|
||||
/* END */
|
@ -85,6 +85,7 @@ int opt_attrs = 0;
|
||||
int opt_timestamps = 0;
|
||||
const char *opt_exclude = NULL;
|
||||
const char *opt_destination = NULL;
|
||||
BOOL opt_testmode = False;
|
||||
|
||||
BOOL opt_have_ip = False;
|
||||
struct in_addr opt_dest_ip;
|
||||
@ -871,6 +872,7 @@ static struct functable net_func[] = {
|
||||
{"STATUS", net_status},
|
||||
{"USERSHARE", net_usershare},
|
||||
{"USERSIDLIST", net_usersidlist},
|
||||
{"CONF", net_conf},
|
||||
#ifdef WITH_FAKE_KASERVER
|
||||
{"AFS", net_afs},
|
||||
#endif
|
||||
@ -912,6 +914,7 @@ static struct functable net_func[] = {
|
||||
{"machine-pass",'P', POPT_ARG_NONE, &opt_machine_pass},
|
||||
{"myworkgroup", 'W', POPT_ARG_STRING, &opt_workgroup},
|
||||
{"verbose", 'v', POPT_ARG_NONE, &opt_verbose},
|
||||
{"test", 'T', POPT_ARG_NONE, &opt_testmode},
|
||||
/* Options for 'net groupmap set' */
|
||||
{"local", 'L', POPT_ARG_NONE, &opt_localgroup},
|
||||
{"domain", 'D', POPT_ARG_NONE, &opt_domaingroup},
|
||||
|
@ -114,6 +114,7 @@ extern int opt_attrs;
|
||||
extern int opt_timestamps;
|
||||
extern const char *opt_exclude;
|
||||
extern const char *opt_destination;
|
||||
extern BOOL opt_testmode;
|
||||
|
||||
extern BOOL opt_have_ip;
|
||||
extern struct in_addr opt_dest_ip;
|
||||
|
1118
source/utils/net_conf.c
Normal file
1118
source/utils/net_conf.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user