ad_gpo.c revision 818c55be478ca2539a86567280114e823d79a51f
7968d3a131e5a684ec1ff0c6d88aae638549153dChristian Maeder/*
306763c67bb99228487345b32ab8c5c6cd41f23cChristian Maeder SSSD
e6d40133bc9f858308654afb1262b8b483ec5922Till Mossakowski
7968d3a131e5a684ec1ff0c6d88aae638549153dChristian Maeder Authors:
97018cf5fa25b494adffd7e9b4e87320dae6bf47Christian Maeder Yassir Elley <yelley@redhat.com>
2eeec5240b424984e3ee26296da1eeab6c6d739eChristian Maeder
306763c67bb99228487345b32ab8c5c6cd41f23cChristian Maeder Copyright (C) 2013 Red Hat
306763c67bb99228487345b32ab8c5c6cd41f23cChristian Maeder
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maeder This program is free software; you can redistribute it and/or modify
e6d40133bc9f858308654afb1262b8b483ec5922Till Mossakowski it under the terms of the GNU General Public License as published by
679d3f541f7a9ede4079e045f7758873bb901872Till Mossakowski the Free Software Foundation; either version 3 of the License, or
679d3f541f7a9ede4079e045f7758873bb901872Till Mossakowski (at your option) any later version.
306763c67bb99228487345b32ab8c5c6cd41f23cChristian Maeder
306763c67bb99228487345b32ab8c5c6cd41f23cChristian Maeder This program is distributed in the hope that it will be useful,
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder but WITHOUT ANY WARRANTY; without even the implied warranty of
4c8d3c5a9e938633f6147b5a595b9b93bfca99e6Christian Maeder MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder GNU General Public License for more details.
0fe1b901cec27c06b8aad7548f56a7cab4dee6a4Till Mossakowski
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder You should have received a copy of the GNU General Public License
f69658e57cba7ecb37c0d84181f4c563215c2534Till Mossakowski along with this program. If not, see <http://www.gnu.org/licenses/>.
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder*/
ba904a15082557e939db689fcfba0c68c9a4f740Christian Maeder
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder/*
d420c2a27c4dfa0a9c8031449db2e1a89ad2cc3aChristian Maeder * This file implements the following pair of *public* functions (see header):
ba904a15082557e939db689fcfba0c68c9a4f740Christian Maeder * ad_gpo_access_send/recv: provides client-side GPO processing
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder *
1842453990fed8a1bd7a5ac792d7982c1d2bfcd5Christian Maeder * This file also implements the following pairs of *private* functions (which
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder * are used by the public functions):
ba904a15082557e939db689fcfba0c68c9a4f740Christian Maeder * ad_gpo_process_som_send/recv: populate list of gp_som objects
e182d0ec56025d97d74829cac75ee31eec12b093Maciek Makowski * ad_gpo_process_gpo_send/recv: populate list of gp_gpo objects
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder * ad_gpo_process_cse_send/recv: retrieve policy file data
9603ad7198b72e812688ad7970e4eac4b553837aKlaus Luettich */
ba904a15082557e939db689fcfba0c68c9a4f740Christian Maeder
58b671de3fe578346fef9642ffa3c5a0a0edb3cbTill Mossakowski#include <security/pam_modules.h>
56cd0da55d058b262b1626ddcd78db6bd9a90551Christian Maeder#include <syslog.h>
ba904a15082557e939db689fcfba0c68c9a4f740Christian Maeder#include <fcntl.h>
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder#include <ini_configobj.h>
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder#include "util/util.h"
ff21411cdf7c6d8d3acebbdebeade1d5ac502b83Christian Maeder#include "util/strtonum.h"
ba904a15082557e939db689fcfba0c68c9a4f740Christian Maeder#include "util/child_common.h"
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder#include "providers/data_provider.h"
43b4c41fbb07705c9df321221ab9cb9832460407Christian Maeder#include "providers/dp_backend.h"
43b4c41fbb07705c9df321221ab9cb9832460407Christian Maeder#include "providers/ad/ad_access.h"
4c8d3c5a9e938633f6147b5a595b9b93bfca99e6Christian Maeder#include "providers/ad/ad_common.h"
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder#include "providers/ad/ad_domain_info.h"
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder#include "providers/ad/ad_gpo.h"
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder#include "providers/ldap/sdap_access.h"
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#include "providers/ldap/sdap_async.h"
b9804822fb178b0fc27ce967a6a8cedc42c5bf90Christian Maeder#include "providers/ldap/sdap.h"
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder#include "providers/ldap/sdap_idmap.h"
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder#include "util/util_sss_idmap.h"
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder#include <ndr.h>
697e63e30aa3c309a1ef1f9357745111f8dfc5a9Christian Maeder#include <gen_ndr/security.h>
c4040537e6f2153af475dd8b07260a1ee9a56ac0Christian Maeder
c4040537e6f2153af475dd8b07260a1ee9a56ac0Christian Maeder/* == gpo-ldap constants =================================================== */
b0294d73dcefc502ddaa13e18b46103a5916971fTill Mossakowski
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#define AD_AT_DN "distinguishedName"
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#define AD_AT_UAC "userAccountControl"
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#define AD_AT_CONFIG_NC "configurationNamingContext"
1b05bdb88b90d3c947351f262d7ae7d68f0a4a6fTill Mossakowski#define AD_AT_GPLINK "gPLink"
1b05bdb88b90d3c947351f262d7ae7d68f0a4a6fTill Mossakowski#define AD_AT_GPOPTIONS "gpOptions"
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#define AD_AT_NT_SEC_DESC "nTSecurityDescriptor"
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder#define AD_AT_CN "cn"
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#define AD_AT_FILE_SYS_PATH "gPCFileSysPath"
1e3950d5c1f0e041dd7677856e43f07796567d5bChristian Maeder#define AD_AT_MACHINE_EXT_NAMES "gPCMachineExtensionNames"
21dae7237ac384abdb94a81e00b3f099873ec623Till Mossakowski#define AD_AT_FUNC_VERSION "gPCFunctionalityVersion"
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#define AD_AT_FLAGS "flags"
3bcd9d942601d59dd55a6069d8b2d1c33d7ced0eChristian Maeder
b03274844ecd270f9e9331f51cc4236a33e2e671Christian Maeder#define UAC_WORKSTATION_TRUST_ACCOUNT 0x00001000
a1c6679d00e15a949730ab640159e0adc5b0e3e7Christian Maeder#define AD_AGP_GUID "edacfd8f-ffb3-11d1-b41d-00a0c968f939"
2604ffeb18ba727a1c9a683f86629c4ce01b06acChristian Maeder#define AD_AUTHENTICATED_USERS_SID "S-1-5-11"
dc8100ead1e97ea34c9ff3fe4af14d37510bf8aeChristian Maeder
4fc3dccf009f8ad9522960e54ab7a5c3d601e342Klaus Luettich/* == gpo-smb constants ==================================================== */
dc8100ead1e97ea34c9ff3fe4af14d37510bf8aeChristian Maeder
1e3950d5c1f0e041dd7677856e43f07796567d5bChristian Maeder#define SMB_STANDARD_URI "smb://"
4fc3dccf009f8ad9522960e54ab7a5c3d601e342Klaus Luettich#define BUFSIZE 65536
4fc3dccf009f8ad9522960e54ab7a5c3d601e342Klaus Luettich
ff21411cdf7c6d8d3acebbdebeade1d5ac502b83Christian Maeder#define RIGHTS_SECTION "Privilege Rights"
4fc3dccf009f8ad9522960e54ab7a5c3d601e342Klaus Luettich#define ALLOW_LOGON_INTERACTIVE "SeInteractiveLogonRight"
4fc3dccf009f8ad9522960e54ab7a5c3d601e342Klaus Luettich#define DENY_LOGON_INTERACTIVE "SeDenyInteractiveLogonRight"
4fc3dccf009f8ad9522960e54ab7a5c3d601e342Klaus Luettich#define ALLOW_LOGON_REMOTE_INTERACTIVE "SeRemoteInteractiveLogonRight"
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#define DENY_LOGON_REMOTE_INTERACTIVE "SeDenyRemoteInteractiveLogonRight"
f0e85ee7e4accfc01f46aa0363acc59fcd248e8aTill Mossakowski#define ALLOW_LOGON_NETWORK "SeNetworkLogonRight"
f0e85ee7e4accfc01f46aa0363acc59fcd248e8aTill Mossakowski#define DENY_LOGON_NETWORK "SeDenyNetworkLogonRight"
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder#define ALLOW_LOGON_BATCH "SeBatchLogonRight"
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#define DENY_LOGON_BATCH "SeDenyBatchLogonRight"
c4040537e6f2153af475dd8b07260a1ee9a56ac0Christian Maeder#define ALLOW_LOGON_SERVICE "SeServiceLogonRight"
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder#define DENY_LOGON_SERVICE "SeDenyServiceLogonRight"
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder#define GP_EXT_GUID_SECURITY "{827D319E-6EAC-11D2-A4EA-00C04F79F83A}"
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder#define GP_EXT_GUID_SECURITY_SUFFIX "/Machine/Microsoft/Windows NT/SecEdit/GptTmpl.inf"
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder#ifndef SSSD_LIBEXEC_PATH
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder#error "SSSD_LIBEXEC_PATH not defined"
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder#else
a3a7d8b3cdf05c8040c62dbcf9a15dc5042cd721Christian Maeder#define GPO_CHILD SSSD_LIBEXEC_PATH"/gpo_child"
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder#endif
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder
e97bcfa4f74907e1a5ccfc3bc1509d1849cda603Christian Maeder/* fd used by the gpo_child process for logging */
ff21411cdf7c6d8d3acebbdebeade1d5ac502b83Christian Maederint gpo_child_debug_fd = -1;
ff21411cdf7c6d8d3acebbdebeade1d5ac502b83Christian Maeder
ff21411cdf7c6d8d3acebbdebeade1d5ac502b83Christian Maeder/* == common data structures and declarations ============================= */
ff21411cdf7c6d8d3acebbdebeade1d5ac502b83Christian Maeder
ade09f8be34b77402276c524bf29f2e5df666a94Christian Maederstruct gp_som {
ade09f8be34b77402276c524bf29f2e5df666a94Christian Maeder const char *som_dn;
ff21411cdf7c6d8d3acebbdebeade1d5ac502b83Christian Maeder struct gp_gplink **gplink_list;
e97bcfa4f74907e1a5ccfc3bc1509d1849cda603Christian Maeder int num_gplinks;
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder};
f3e815e0f960f2c002904b18ad75c149a3827d9fChristian Maeder
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maederstruct gp_gplink {
e97bcfa4f74907e1a5ccfc3bc1509d1849cda603Christian Maeder const char *gpo_dn;
2afae0880da7ca73c9376fd4d653ab19833fe858Christian Maeder bool enforced;
a3a7d8b3cdf05c8040c62dbcf9a15dc5042cd721Christian Maeder};
0ea2cddb8715a770e646895e16b7b8085f49167cChristian Maeder
2afae0880da7ca73c9376fd4d653ab19833fe858Christian Maederstruct gp_gpo {
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder struct security_descriptor *gpo_sd;
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder const char *gpo_dn;
21dae7237ac384abdb94a81e00b3f099873ec623Till Mossakowski const char *gpo_guid;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder const char *smb_server;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder const char *smb_share;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder const char *smb_path;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder const char **gpo_cse_guids;
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder int num_gpo_cse_guids;
b03274844ecd270f9e9331f51cc4236a33e2e671Christian Maeder int gpo_func_version;
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder int gpo_flags;
42972ddff400840d46eb54422b60083228b2996cChristian Maeder bool send_to_child;
b03274844ecd270f9e9331f51cc4236a33e2e671Christian Maeder const char *policy_filename;
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder};
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maederenum ace_eval_status {
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder AD_GPO_ACE_DENIED,
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder AD_GPO_ACE_ALLOWED,
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder AD_GPO_ACE_NEUTRAL
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder};
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maederstruct tevent_req *ad_gpo_process_som_send(TALLOC_CTX *mem_ctx,
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder struct tevent_context *ev,
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder struct sdap_id_conn_ctx *conn,
bcd35fcdda4233c42766519772b2e9fbab57f975Christian Maeder struct ldb_context *ldb_ctx,
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder struct sdap_id_op *sdap_op,
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder struct sdap_options *opts,
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder int timeout,
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder const char *target_dn,
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder const char *domain_name);
be8b8876edf3d7138ddd39a4ec07d857dde5bbb5Christian Maederint ad_gpo_process_som_recv(struct tevent_req *req,
be8b8876edf3d7138ddd39a4ec07d857dde5bbb5Christian Maeder TALLOC_CTX *mem_ctx,
be8b8876edf3d7138ddd39a4ec07d857dde5bbb5Christian Maeder struct gp_som ***som_list);
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder
254df6f22d01eacf7c57b85729e0445747b630d9Christian Maederstruct tevent_req *ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder struct tevent_context *ev,
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder struct sdap_id_op *sdap_op,
be8b8876edf3d7138ddd39a4ec07d857dde5bbb5Christian Maeder struct sdap_options *opts,
a55b09f4ef6e58ad617b59899d93c63bb4d6f287Christian Maeder char *server_hostname,
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder int timeout,
b83ff3749d99d03b641adee264b781039a551addChristian Maeder struct gp_som **som_list);
b83ff3749d99d03b641adee264b781039a551addChristian Maederint ad_gpo_process_gpo_recv(struct tevent_req *req,
7297175957c5ad3c0498032190b1dee9ec5fb873Christian Maeder TALLOC_CTX *mem_ctx,
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder struct gp_gpo ***candidate_gpos,
17d4f8c5576d93f36cafe68161cdb960ec49ce7cChristian Maeder int *num_candidate_gpos);
17d4f8c5576d93f36cafe68161cdb960ec49ce7cChristian Maeder
17d4f8c5576d93f36cafe68161cdb960ec49ce7cChristian Maederstruct tevent_req *ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
f1d04fe5072b827d9cc490ebdbca78108241a392Christian Maeder struct tevent_context *ev,
b03274844ecd270f9e9331f51cc4236a33e2e671Christian Maeder bool send_to_child,
f1d04fe5072b827d9cc490ebdbca78108241a392Christian Maeder struct sss_domain_info *domain,
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder const char *gpo_guid,
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder const char *smb_server,
17d4f8c5576d93f36cafe68161cdb960ec49ce7cChristian Maeder const char *smb_share,
b03274844ecd270f9e9331f51cc4236a33e2e671Christian Maeder const char *smb_path,
2afae0880da7ca73c9376fd4d653ab19833fe858Christian Maeder const char *smb_cse_suffix,
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder int cached_gpt_version,
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder int gpo_timeout_option);
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder
26ed2a19326560786ff94dfc462309d6d5d862a8Heng Jiangint ad_gpo_process_cse_recv(struct tevent_req *req);
1b05bdb88b90d3c947351f262d7ae7d68f0a4a6fTill Mossakowski
21dae7237ac384abdb94a81e00b3f099873ec623Till Mossakowski/* == ad_gpo_parse_map_options and helpers ==================================*/
1b05bdb88b90d3c947351f262d7ae7d68f0a4a6fTill Mossakowski
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder#define GPO_LOGIN "login"
15bceb77af626f79747d46d35979640f229a4c71Christian Maeder#define GPO_SU "su"
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder#define GPO_SU_L "su-l"
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder#define GPO_GDM_FINGERPRINT "gdm-fingerprint"
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder#define GPO_GDM_PASSWORD "gdm-password"
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder#define GPO_GDM_SMARTCARD "gdm-smartcard"
758cf077c1349a62f0541808d8fa9582bc263704Christian Maeder#define GPO_KDM "kdm"
74b841a4b332085d5fd79975a13313c2681ae595Christian Maeder#define GPO_SSHD "sshd"
74b841a4b332085d5fd79975a13313c2681ae595Christian Maeder#define GPO_FTP "ftp"
74b841a4b332085d5fd79975a13313c2681ae595Christian Maeder#define GPO_SAMBA "samba"
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder#define GPO_CROND "crond"
74b841a4b332085d5fd79975a13313c2681ae595Christian Maeder#define GPO_SUDO "sudo"
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maeder#define GPO_SUDO_I "sudo-i"
74b841a4b332085d5fd79975a13313c2681ae595Christian Maeder#define GPO_SYSTEMD_USER "systemd-user"
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maederstruct gpo_map_option_entry {
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder enum gpo_map_type gpo_map_type;
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder enum ad_basic_opt ad_basic_opt;
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder const char **gpo_map_defaults;
74b841a4b332085d5fd79975a13313c2681ae595Christian Maeder const char *allow_key;
26ed2a19326560786ff94dfc462309d6d5d862a8Heng Jiang const char *deny_key;
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder};
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maederconst char *gpo_map_interactive_defaults[] =
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder {GPO_LOGIN, GPO_SU, GPO_SU_L,
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder GPO_GDM_FINGERPRINT, GPO_GDM_PASSWORD, GPO_GDM_SMARTCARD, GPO_KDM, NULL};
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maederconst char *gpo_map_remote_interactive_defaults[] = {GPO_SSHD, NULL};
26ed2a19326560786ff94dfc462309d6d5d862a8Heng Jiangconst char *gpo_map_network_defaults[] = {GPO_FTP, GPO_SAMBA, NULL};
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maederconst char *gpo_map_batch_defaults[] = {GPO_CROND, NULL};
7297175957c5ad3c0498032190b1dee9ec5fb873Christian Maederconst char *gpo_map_service_defaults[] = {NULL};
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maederconst char *gpo_map_permit_defaults[] = {GPO_SUDO, GPO_SUDO_I,
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder GPO_SYSTEMD_USER, NULL};
1b05bdb88b90d3c947351f262d7ae7d68f0a4a6fTill Mossakowskiconst char *gpo_map_deny_defaults[] = {NULL};
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder
948f37fdb71c544ff4c907bc5863702648cf36e4Christian Maederstruct gpo_map_option_entry gpo_map_option_entries[] = {
948f37fdb71c544ff4c907bc5863702648cf36e4Christian Maeder {GPO_MAP_INTERACTIVE, AD_GPO_MAP_INTERACTIVE, gpo_map_interactive_defaults,
1e3950d5c1f0e041dd7677856e43f07796567d5bChristian Maeder ALLOW_LOGON_INTERACTIVE, DENY_LOGON_INTERACTIVE},
7f7460e7095628f3437b116ee78d3043d11f8febChristian Maeder {GPO_MAP_REMOTE_INTERACTIVE, AD_GPO_MAP_REMOTE_INTERACTIVE,
948f37fdb71c544ff4c907bc5863702648cf36e4Christian Maeder gpo_map_remote_interactive_defaults,
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder ALLOW_LOGON_REMOTE_INTERACTIVE, DENY_LOGON_REMOTE_INTERACTIVE},
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder {GPO_MAP_NETWORK, AD_GPO_MAP_NETWORK, gpo_map_network_defaults,
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder ALLOW_LOGON_NETWORK, DENY_LOGON_NETWORK},
1e3950d5c1f0e041dd7677856e43f07796567d5bChristian Maeder {GPO_MAP_BATCH, AD_GPO_MAP_BATCH, gpo_map_batch_defaults,
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder ALLOW_LOGON_BATCH, DENY_LOGON_BATCH},
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder {GPO_MAP_SERVICE, AD_GPO_MAP_SERVICE, gpo_map_service_defaults,
f3faf4e4346b6224a3aaeeac11bac8b5c8932a29Christian Maeder ALLOW_LOGON_SERVICE, DENY_LOGON_SERVICE},
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder {GPO_MAP_PERMIT, AD_GPO_MAP_PERMIT, gpo_map_permit_defaults, NULL, NULL},
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder {GPO_MAP_DENY, AD_GPO_MAP_DENY, gpo_map_deny_defaults, NULL, NULL},
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder};
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maederconst char* gpo_map_type_string(int gpo_map_type)
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder{
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder switch(gpo_map_type) {
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder case GPO_MAP_INTERACTIVE: return "Interactive";
1f315f2e146d15c0aec01f7ae076708bbac29796Christian Maeder case GPO_MAP_REMOTE_INTERACTIVE: return "Remote Interactive";
ef2affdc0cdf3acd5c051597c04ab9b08a346a7dChristian Maeder case GPO_MAP_NETWORK: return "Network";
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder case GPO_MAP_BATCH: return "Batch";
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder case GPO_MAP_SERVICE: return "Service";
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder case GPO_MAP_PERMIT: return "Permitted";
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder case GPO_MAP_DENY: return "Denied";
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder }
3bcf8bbafdda23d3c6be2deec9e68f98223b78c1Christian Maeder return NULL;
3bcf8bbafdda23d3c6be2deec9e68f98223b78c1Christian Maeder}
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder
3bcf8bbafdda23d3c6be2deec9e68f98223b78c1Christian Maederstatic inline bool
3bcf8bbafdda23d3c6be2deec9e68f98223b78c1Christian Maederad_gpo_service_in_list(char **list, size_t nlist, const char *str)
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder{
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder size_t i;
f3faf4e4346b6224a3aaeeac11bac8b5c8932a29Christian Maeder
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder for (i = 0; i < nlist; i++) {
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder if (strcasecmp(list[i], str) == 0) {
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder break;
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder }
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder }
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder return (i < nlist) ? true : false;
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder}
ef2affdc0cdf3acd5c051597c04ab9b08a346a7dChristian Maeder
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maedererrno_t
4a8f990902448d0562fbe1a98ce685ddbd531d38Christian Maederad_gpo_parse_map_option_helper(enum gpo_map_type gpo_map_type,
4a8f990902448d0562fbe1a98ce685ddbd531d38Christian Maeder hash_key_t key,
4a8f990902448d0562fbe1a98ce685ddbd531d38Christian Maeder hash_table_t *options_table)
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder{
46947810076241f06f3e2919edb2289ed84d6c15Christian Maeder hash_value_t val;
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder int hret;
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder int ret;
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder hret = hash_lookup(options_table, &key, &val);
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder if (hret != HASH_SUCCESS && hret != HASH_ERROR_KEY_NOT_FOUND) {
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder DEBUG(SSSDBG_OP_FAILURE, "Error checking hash table: [%s]\n",
7688e20f844fe88f75c04016841ebb5e5e3d927fChristian Maeder hash_error_string(hret));
7688e20f844fe88f75c04016841ebb5e5e3d927fChristian Maeder ret = EINVAL;
7688e20f844fe88f75c04016841ebb5e5e3d927fChristian Maeder goto done;
ef2affdc0cdf3acd5c051597c04ab9b08a346a7dChristian Maeder } else if (hret == HASH_SUCCESS) {
7688e20f844fe88f75c04016841ebb5e5e3d927fChristian Maeder /* handle unexpected case where mapping for key already exists */
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder if (val.i == gpo_map_type) {
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder /* mapping for key exists for same map type; no error */
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder DEBUG(SSSDBG_TRACE_FUNC,
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder "PAM service %s maps to %s multiple times\n", key.str,
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder gpo_map_type_string(gpo_map_type));
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder ret = EOK;
996a56a455d65cfac4ddedd44fd90cfc1ea849aeChristian Maeder } else {
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder /* mapping for key exists for different map type; error! */
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder DEBUG(SSSDBG_CRIT_FAILURE,
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder "PAM service %s maps to both %s and %s\n", key.str,
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder gpo_map_type_string(val.i), gpo_map_type_string(gpo_map_type));
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder ret = EINVAL;
b03274844ecd270f9e9331f51cc4236a33e2e671Christian Maeder }
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder goto done;
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder } else {
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder /* handle expected case where mapping for key doesn't already exist */
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder val.type = HASH_VALUE_INT;
1f315f2e146d15c0aec01f7ae076708bbac29796Christian Maeder val.i = gpo_map_type;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder
996a56a455d65cfac4ddedd44fd90cfc1ea849aeChristian Maeder hret = hash_enter(options_table, &key, &val);
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder if (hret != HASH_SUCCESS) {
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder DEBUG(SSSDBG_OP_FAILURE, "Error checking hash table: [%s]\n",
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder hash_error_string(hret));
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder ret = EIO;
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder goto done;
b03274844ecd270f9e9331f51cc4236a33e2e671Christian Maeder }
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder ret = EOK;
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder }
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maederdone:
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder return ret;
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder}
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maedererrno_t
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maederad_gpo_parse_map_option(TALLOC_CTX *mem_ctx,
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder enum gpo_map_type gpo_map_type,
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder hash_table_t *options_table,
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder char *conf_str,
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder const char **defaults)
4b0e0613129ebfc53e3e87985c20a537da91d18dChristian Maeder{
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder TALLOC_CTX *tmp_ctx;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder errno_t ret;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder char **conf_list = NULL;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder int conf_list_size = 0;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder char **add_list = NULL;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder char **remove_list = NULL;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder int ai = 0, ri = 0;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder int i;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder hash_key_t key;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder tmp_ctx = talloc_new(NULL);
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder if (tmp_ctx == NULL) {
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder ret = ENOMEM;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder goto done;
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder }
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder DEBUG(SSSDBG_TRACE_ALL, "gpo_map_type: %s\n",
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder gpo_map_type_string(gpo_map_type));
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder if (conf_str) {
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder ret = split_on_separator(tmp_ctx, conf_str, ',', true, true,
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder &conf_list, &conf_list_size);
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder if (ret != EOK) {
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder DEBUG(SSSDBG_OP_FAILURE,
1b05bdb88b90d3c947351f262d7ae7d68f0a4a6fTill Mossakowski "Cannot parse list of service names %s: %d\n", conf_str, ret);
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder ret = EINVAL;
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder goto done;
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder }
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder add_list = talloc_zero_array(tmp_ctx, char *, conf_list_size);
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder remove_list = talloc_zero_array(tmp_ctx, char *, conf_list_size);
ef2affdc0cdf3acd5c051597c04ab9b08a346a7dChristian Maeder if (add_list == NULL || remove_list == NULL) {
5a87ed846cc38cb0e3adf8f736d95614d3e724a3Christian Maeder ret = ENOMEM;
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder goto done;
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder }
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder }
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder for (i = 0; i < conf_list_size; i++) {
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder switch (conf_list[i][0]) {
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder case '+':
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder add_list[ai] = conf_list[i] + 1;
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder ai++;
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder continue;
996a56a455d65cfac4ddedd44fd90cfc1ea849aeChristian Maeder case '-':
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder remove_list[ri] = conf_list[i] + 1;
ef2affdc0cdf3acd5c051597c04ab9b08a346a7dChristian Maeder ri++;
fb7c3ffc41767729db506ad0ac1262ecfb5febf8Christian Maeder continue;
9192fdd8f0e682ac0f0183dd854d5210fbfa4ec5Christian Maeder default:
8528053a6a766c3614276df0f59fb2a2e8ab6d18Christian Maeder DEBUG(SSSDBG_CRIT_FAILURE, "ad_gpo_map values must start with"
8528053a6a766c3614276df0f59fb2a2e8ab6d18Christian Maeder "either '+' (for adding service) or '-' (for removing service), "
2afae0880da7ca73c9376fd4d653ab19833fe858Christian Maeder "got '%s'\n",
ff21411cdf7c6d8d3acebbdebeade1d5ac502b83Christian Maeder conf_list[i]);
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maeder ret = EINVAL;
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder goto done;
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder }
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder }
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder /* Start by adding explicitly added services ('+') to hashtable */
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder for (i = 0; i < ai; i++) {
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder /* if the service is explicitly configured to be removed, skip it */
3bcf8bbafdda23d3c6be2deec9e68f98223b78c1Christian Maeder if (ad_gpo_service_in_list(remove_list, ri, add_list[i])) {
1f315f2e146d15c0aec01f7ae076708bbac29796Christian Maeder continue;
3bcf8bbafdda23d3c6be2deec9e68f98223b78c1Christian Maeder }
3bcf8bbafdda23d3c6be2deec9e68f98223b78c1Christian Maeder
3bcf8bbafdda23d3c6be2deec9e68f98223b78c1Christian Maeder key.type = HASH_KEY_STRING;
3bcf8bbafdda23d3c6be2deec9e68f98223b78c1Christian Maeder key.str = (char *)add_list[i];
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder ret = ad_gpo_parse_map_option_helper(gpo_map_type, key, options_table);
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder if (ret != EOK) {
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder DEBUG(SSSDBG_OP_FAILURE, "Invalid configuration: %d\n", ret);
3ea571db6dd6e0c42d02de4e56648c7cd86a3734Christian Maeder goto done;
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder }
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder DEBUG(SSSDBG_TRACE_ALL, "Explicitly added service: %s\n", key.str);
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder }
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder
ddd8734e5b3802a1a6c908af6b1e870af76c932dChristian Maeder /* Add defaults to hashtable */
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maeder for (i = 0; defaults[i]; i++) {
e5fae0f42a23c12fce5389f405659d4e5dce73a4Christian Maeder /* if the service is explicitly configured to be removed, skip it */
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder if (ad_gpo_service_in_list(remove_list, ri, defaults[i])) {
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder continue;
e5fae0f42a23c12fce5389f405659d4e5dce73a4Christian Maeder }
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder key.type = HASH_KEY_STRING;
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder key.str = talloc_strdup(mem_ctx, defaults[i]);
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder
5c13d1acd6298569a5574c07c833cd3fdac4ae4cChristian Maeder ret = ad_gpo_parse_map_option_helper(gpo_map_type, key, options_table);
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder if (ret != EOK) {
c616e681da8c052b62e14247fea522da099ac0e4Christian Maeder DEBUG(SSSDBG_OP_FAILURE, "Invalid configuration: %d\n", ret);
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maeder goto done;
89ab08979dc23d72e9e09c8990a8c44847041d6fChristian Maeder }
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maeder
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maeder DEBUG(SSSDBG_TRACE_ALL, "Default service (not explicitly removed): %s\n",
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder key.str);
c616e681da8c052b62e14247fea522da099ac0e4Christian Maeder }
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maeder
c092fcac4b8f5c524c22ca579189c4487c13edf7Christian Maeder ret = EOK;
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maederdone:
d21dd452cd68abade683103a5c0cfe6d02c5f17bTill Mossakowski talloc_free(tmp_ctx);
d21dd452cd68abade683103a5c0cfe6d02c5f17bTill Mossakowski return ret;
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder}
16023c23c9d17743033afd994ad11c386d17b376Christian Maeder
16023c23c9d17743033afd994ad11c386d17b376Christian Maedererrno_t
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maederad_gpo_parse_map_options(struct ad_access_ctx *access_ctx)
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder{
b9804822fb178b0fc27ce967a6a8cedc42c5bf90Christian Maeder char *gpo_default_right_config;
cd34f330714d6b3ef21a314ea98842c4b8c2fcedCui Jian enum gpo_map_type gpo_default_right;
cd34f330714d6b3ef21a314ea98842c4b8c2fcedCui Jian errno_t ret;
84a30e66aef85cc54d3dd6f8e408729007fe8809Christian Maeder int i;
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder
84a30e66aef85cc54d3dd6f8e408729007fe8809Christian Maeder for (i = 0; i < GPO_MAP_NUM_OPTS; i++) {
edd35c6c970fa1707dc6ad7a3ba26119e0046223Cui Jian
84a30e66aef85cc54d3dd6f8e408729007fe8809Christian Maeder struct gpo_map_option_entry entry = gpo_map_option_entries[i];
2c08058468dab64c89a8eae51b56f9afb8b6cb71Cui Jian
84a30e66aef85cc54d3dd6f8e408729007fe8809Christian Maeder char *entry_config = dp_opt_get_string(access_ctx->ad_options,
84a30e66aef85cc54d3dd6f8e408729007fe8809Christian Maeder entry.ad_basic_opt);
84a30e66aef85cc54d3dd6f8e408729007fe8809Christian Maeder
cd34f330714d6b3ef21a314ea98842c4b8c2fcedCui Jian ret = ad_gpo_parse_map_option(access_ctx, entry.gpo_map_type,
37e30366abd83c00a5d5447b45694627fd783de8Cui Jian access_ctx->gpo_map_options_table,
37e30366abd83c00a5d5447b45694627fd783de8Cui Jian entry_config, entry.gpo_map_defaults);
37e30366abd83c00a5d5447b45694627fd783de8Cui Jian
37e30366abd83c00a5d5447b45694627fd783de8Cui Jian if (ret != EOK) {
cd34f330714d6b3ef21a314ea98842c4b8c2fcedCui Jian DEBUG(SSSDBG_OP_FAILURE, "Invalid configuration: %d\n", ret);
cd34f330714d6b3ef21a314ea98842c4b8c2fcedCui Jian ret = EINVAL;
5efed683fd173e9d53bd5f1929ba5b0c8a228710Christian Maeder goto fail;
876bd2c70a93981cc80f8376284616bce4a0fefcChristian Maeder }
da955132262baab309a50fdffe228c9efe68251dCui Jian }
da955132262baab309a50fdffe228c9efe68251dCui Jian
5efed683fd173e9d53bd5f1929ba5b0c8a228710Christian Maeder /* default right (applicable for services without any mapping) */
5efed683fd173e9d53bd5f1929ba5b0c8a228710Christian Maeder gpo_default_right_config =
2c08058468dab64c89a8eae51b56f9afb8b6cb71Cui Jian dp_opt_get_string(access_ctx->ad_options, AD_GPO_DEFAULT_RIGHT);
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder
2d2826f9db2c17275f91b0104940a60a2f9fd44dChristian Maeder DEBUG(SSSDBG_TRACE_ALL, "gpo_default_right_config: %s\n",
54fb645be0a806e7fd3c0eed5691c2153eb8d518Christian Maeder gpo_default_right_config);
95c3e5d11dcee331dc3876a9bf0c1d6daa38e2caChristian Maeder
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder /* if default right not set in config, set them to DENY */
2d2826f9db2c17275f91b0104940a60a2f9fd44dChristian Maeder if (gpo_default_right_config == NULL) {
ef2affdc0cdf3acd5c051597c04ab9b08a346a7dChristian Maeder gpo_default_right = GPO_MAP_DENY;
22250d2b3c9f86fe19cba665d71c301de03db142Christian Maeder } else if (strncasecmp(gpo_default_right_config, "interactive",
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder strlen("interactive")) == 0) {
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder gpo_default_right = GPO_MAP_INTERACTIVE;
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder } else if (strncasecmp(gpo_default_right_config, "remote_interactive",
22250d2b3c9f86fe19cba665d71c301de03db142Christian Maeder strlen("remote_interactive")) == 0) {
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder gpo_default_right = GPO_MAP_REMOTE_INTERACTIVE;
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder } else if (strncasecmp(gpo_default_right_config, "network",
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder strlen("network")) == 0) {
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder gpo_default_right = GPO_MAP_NETWORK;
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder } else if (strncasecmp(gpo_default_right_config, "batch",
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder strlen("batch")) == 0) {
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder gpo_default_right = GPO_MAP_BATCH;
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder } else if (strncasecmp(gpo_default_right_config, "service",
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder strlen("service")) == 0) {
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder gpo_default_right = GPO_MAP_SERVICE;
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder } else if (strncasecmp(gpo_default_right_config, "permit",
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder strlen("permit")) == 0) {
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder gpo_default_right = GPO_MAP_PERMIT;
7688e20f844fe88f75c04016841ebb5e5e3d927fChristian Maeder } else if (strncasecmp(gpo_default_right_config, "deny",
3d4e57e86d8aee818b589cd1029838e0accade55Christian Maeder strlen("deny")) == 0) {
22250d2b3c9f86fe19cba665d71c301de03db142Christian Maeder gpo_default_right = GPO_MAP_DENY;
22250d2b3c9f86fe19cba665d71c301de03db142Christian Maeder } else {
7688e20f844fe88f75c04016841ebb5e5e3d927fChristian Maeder ret = EINVAL;
goto fail;
}
DEBUG(SSSDBG_TRACE_ALL, "gpo_default_right: %d\n", gpo_default_right);
access_ctx->gpo_default_right = gpo_default_right;
fail:
return ret;
}
/* == ad_gpo_access_send/recv helpers =======================================*/
static bool
ad_gpo_dom_sid_equal(const struct dom_sid *sid1, const struct dom_sid *sid2)
{
int i;
if (sid1 == sid2) {
return true;
}
if (!sid1 || !sid2) {
return false;
}
if (sid1->sid_rev_num != sid2->sid_rev_num) {
return false;
}
for (i = 0; i < 6; i++) {
if (sid1->id_auth[i] != sid2->id_auth[i]) {
return false;
}
}
if (sid1->num_auths != sid2->num_auths) {
return false;
}
for (i = 0; i < sid1->num_auths; i++) {
if (sid1->sub_auths[i] != sid2->sub_auths[i]) {
return false;
}
}
return true;
}
/*
* This function retrieves the SIDs corresponding to the input user and returns
* the user_sid, group_sids, and group_size in their respective output params.
*
* Note: since authentication must complete successfully before the
* gpo access checks are called, we can safely assume that the user/computer
* has been authenticated. As such, this function always adds the
* AD_AUTHENTICATED_USERS_SID to the group_sids.
*/
static errno_t
ad_gpo_get_sids(TALLOC_CTX *mem_ctx,
const char *user,
struct sss_domain_info *domain,
const char **_user_sid,
const char ***_group_sids,
int *_group_size)
{
TALLOC_CTX *tmp_ctx = NULL;
struct ldb_result *res;
int ret = 0;
int i = 0;
int num_group_sids = 0;
const char *user_sid = NULL;
const char *group_sid = NULL;
const char **group_sids = NULL;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
}
/* first result from sysdb_initgroups is user_sid; rest are group_sids */
ret = sysdb_initgroups(tmp_ctx, domain, user, &res);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_initgroups failed: [%d](%s)\n",
ret, sss_strerror(ret));
return ret;
}
if (res->count == 0) {
ret = ENOENT;
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_initgroups returned empty result\n");
return ret;
}
user_sid = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_SID_STR, NULL);
num_group_sids = (res->count) - 1;
/* include space for AD_AUTHENTICATED_USERS_SID and NULL */
group_sids = talloc_array(tmp_ctx, const char *, num_group_sids + 1 + 1);
if (group_sids == NULL) {
ret = ENOMEM;
goto done;
}
for (i = 0; i < num_group_sids; i++) {
group_sid = ldb_msg_find_attr_as_string(res->msgs[i+1],
SYSDB_SID_STR, NULL);
if (group_sid == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "Missing SID for cache entry [%s].\n",
ldb_dn_get_linearized(res->msgs[i+1]->dn));
ret = EINVAL;
goto done;
}
group_sids[i] = talloc_steal(group_sids, group_sid);
if (group_sids[i] == NULL) {
ret = ENOMEM;
goto done;
}
}
group_sids[i++] = talloc_strdup(group_sids, AD_AUTHENTICATED_USERS_SID);
group_sids[i] = NULL;
*_group_size = num_group_sids + 1;
*_group_sids = talloc_steal(mem_ctx, group_sids);
*_user_sid = talloc_steal(mem_ctx, user_sid);
return EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
/*
* This function determines whether the input ace_dom_sid matches any of the
* client's SIDs. The boolean result is assigned to the _included output param.
*/
static errno_t
ad_gpo_ace_includes_client_sid(const char *user_sid,
const char **group_sids,
int group_size,
struct dom_sid ace_dom_sid,
struct sss_idmap_ctx *idmap_ctx,
bool *_included)
{
int i = 0;
struct dom_sid *user_dom_sid;
struct dom_sid *group_dom_sid;
enum idmap_error_code err;
bool included = false;
err = sss_idmap_sid_to_smb_sid(idmap_ctx, user_sid, &user_dom_sid);
if (err != IDMAP_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize idmap context.\n");
return EFAULT;
}
included = ad_gpo_dom_sid_equal(&ace_dom_sid, user_dom_sid);
sss_idmap_free_smb_sid(idmap_ctx, user_dom_sid);
if (included) {
*_included = true;
return EOK;
}
for (i = 0; i < group_size; i++) {
err = sss_idmap_sid_to_smb_sid(idmap_ctx, group_sids[i], &group_dom_sid);
if (err != IDMAP_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize idmap context.\n");
return EFAULT;
}
included = ad_gpo_dom_sid_equal(&ace_dom_sid, group_dom_sid);
sss_idmap_free_smb_sid(idmap_ctx, group_dom_sid);
if (included) {
*_included = true;
return EOK;
}
}
*_included = false;
return EOK;
}
/*
* This function determines whether use of the extended right
* named "ApplyGroupPolicy" (AGP) is allowed, by comparing the specified
* user_sid and group_sids against the specified access control entry (ACE).
* This function returns ALLOWED, DENIED, or NEUTRAL depending on whether
* the ACE explictly allows, explicitly denies, or does neither.
*
* Note that the 'M' abbreviation used in the evaluation algorithm stands for
* "access_mask", which represents the set of access rights associated with an
* individual ACE. The access right of interest to the GPO code is
* RIGHT_DS_CONTROL_ACCESS, which serves as a container for all control access
* rights. The specific control access right is identified by a GUID in the
* ACE's ObjectType. In our case, this is the GUID corresponding to AGP.
*
* The ACE evaluation algorithm is specified in [MS-ADTS] 5.1.3.3.4:
* - Deny access by default
* - If the "Inherit Only" (IO) flag is set in the ACE, skip the ACE.
* - If the SID in the ACE does not match any SID in the requester's
* security context, skip the ACE
* - If the ACE type is "Object Access Allowed", the access right
* RIGHT_DS_CONTROL_ACCESS (CR) is present in M, and the ObjectType
* field in the ACE is either not present OR contains a GUID value equal
* to AGP, then grant requested control access right. Stop access checking.
* - If the ACE type is "Object Access Denied", the access right
* RIGHT_DS_CONTROL_ACCESS (CR) is present in M, and the ObjectType
* field in the ACE is either not present OR contains a GUID value equal to
* AGP, then deny the requested control access right. Stop access checking.
*/
static enum ace_eval_status ad_gpo_evaluate_ace(struct security_ace *ace,
struct sss_idmap_ctx *idmap_ctx,
const char *user_sid,
const char **group_sids,
int group_size)
{
bool agp_included = false;
bool included = false;
int ret = 0;
struct security_ace_object object;
struct GUID ext_right_agp_guid;
if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
return AD_GPO_ACE_NEUTRAL;
}
ret = ad_gpo_ace_includes_client_sid(user_sid, group_sids, group_size,
ace->trustee, idmap_ctx, &included);
if (ret != EOK) {
return AD_GPO_ACE_DENIED;
}
if (!included) {
return AD_GPO_ACE_NEUTRAL;
}
object = ace->object.object;
GUID_from_string(AD_AGP_GUID, &ext_right_agp_guid);
if (object.flags & SEC_ACE_OBJECT_TYPE_PRESENT) {
if (GUID_equal(&object.type.type, &ext_right_agp_guid)) {
agp_included = true;
}
} else {
agp_included = false;
}
if (ace->access_mask & SEC_ADS_CONTROL_ACCESS) {
if (agp_included) {
if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT) {
return AD_GPO_ACE_ALLOWED;
} else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT) {
return AD_GPO_ACE_DENIED;
}
}
}
return AD_GPO_ACE_DENIED;
}
/*
* This function extracts the GPO's DACL (discretionary access control list)
* from the GPO's specified security descriptor, and determines whether
* the GPO is applicable to the policy target, by comparing the specified
* user_sid and group_sids against each access control entry (ACE) in the DACL.
* The boolean result is assigned to the _access_allowed output parameter.
*/
static errno_t ad_gpo_evaluate_dacl(struct security_acl *dacl,
struct sss_idmap_ctx *idmap_ctx,
const char *user_sid,
const char **group_sids,
int group_size,
bool *_dacl_access_allowed)
{
uint32_t num_aces = 0;
enum ace_eval_status ace_status;
int i = 0;
struct security_ace *ace = NULL;
num_aces = dacl->num_aces;
/*
* [MS-ADTS] 5.1.3.3.4:
* If the DACL does not have any ACE, then deny the requester the
* requested control access right.
*/
if (num_aces == 0) {
*_dacl_access_allowed = false;
return EOK;
}
for (i = 0; i < dacl->num_aces; i ++) {
ace = &dacl->aces[i];
ace_status = ad_gpo_evaluate_ace(ace, idmap_ctx, user_sid,
group_sids, group_size);
switch (ace_status) {
case AD_GPO_ACE_NEUTRAL:
continue;
case AD_GPO_ACE_ALLOWED:
*_dacl_access_allowed = true;
return EOK;
case AD_GPO_ACE_DENIED:
*_dacl_access_allowed = false;
return EOK;
}
}
*_dacl_access_allowed = false;
return EOK;
}
/*
* This function takes candidate_gpos as input, filters out any gpo that is
* not applicable to the policy target and assigns the result to the
* _dacl_filtered_gpos output parameter. The filtering algorithm is
* defined in [MS-GPOL] 3.2.5.1.6
*/
static errno_t
ad_gpo_filter_gpos_by_dacl(TALLOC_CTX *mem_ctx,
const char *user,
struct sss_domain_info *domain,
struct sss_idmap_ctx *idmap_ctx,
struct gp_gpo **candidate_gpos,
int num_candidate_gpos,
struct gp_gpo ***_dacl_filtered_gpos,
int *_num_dacl_filtered_gpos)
{
TALLOC_CTX *tmp_ctx = NULL;
int i = 0;
int ret = 0;
struct gp_gpo *candidate_gpo = NULL;
struct security_descriptor *sd = NULL;
struct security_acl *dacl = NULL;
const char *user_sid = NULL;
const char **group_sids = NULL;
int group_size = 0;
int gpo_dn_idx = 0;
bool access_allowed = false;
struct gp_gpo **dacl_filtered_gpos = NULL;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
}
ret = ad_gpo_get_sids(tmp_ctx, user, domain, &user_sid,
&group_sids, &group_size);
if (ret != EOK) {
ret = ERR_NO_SIDS;
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve SIDs: [%d](%s)\n", ret, sss_strerror(ret));
goto done;
}
dacl_filtered_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
if (dacl_filtered_gpos == NULL) {
ret = ENOMEM;
goto done;
}
for (i = 0; i < num_candidate_gpos; i++) {
access_allowed = false;
candidate_gpo = candidate_gpos[i];
sd = candidate_gpo->gpo_sd;
dacl = candidate_gpo->gpo_sd->dacl;
DEBUG(SSSDBG_TRACE_ALL, "examining dacl candidate_gpo_guid:%s\n",
candidate_gpo->gpo_guid);
/* gpo_func_version must be set to version 2 */
if (candidate_gpo->gpo_func_version != 2) {
DEBUG(SSSDBG_TRACE_ALL,
"GPO not applicable to target per security filtering\n");
continue;
}
/* gpo_flags value of 2 means that GPO's computer portion is disabled */
if (candidate_gpo->gpo_flags == 2) {
DEBUG(SSSDBG_TRACE_ALL,
"GPO not applicable to target per security filtering\n");
continue;
}
/*
* [MS-ADTS] 5.1.3.3.4:
* If the security descriptor has no DACL or its "DACL Present" bit
* is not set, then grant requester the requested control access right.
*/
if ((!(sd->type & SEC_DESC_DACL_PRESENT)) || (dacl == NULL)) {
DEBUG(SSSDBG_TRACE_ALL, "DACL is not present\n");
access_allowed = true;
break;
}
ret = ad_gpo_evaluate_dacl(dacl, idmap_ctx, user_sid, group_sids,
group_size, &access_allowed);
if (ret != EOK) {
DEBUG(SSSDBG_MINOR_FAILURE, "Could not determine if GPO is applicable\n");
continue;
}
if (access_allowed) {
DEBUG(SSSDBG_TRACE_ALL,
"GPO applicable to target per security filtering\n");
dacl_filtered_gpos[gpo_dn_idx] = talloc_steal(dacl_filtered_gpos,
candidate_gpo);
gpo_dn_idx++;
} else {
DEBUG(SSSDBG_TRACE_ALL,
"GPO not applicable to target per security filtering\n");
continue;
}
}
dacl_filtered_gpos[gpo_dn_idx] = NULL;
*_dacl_filtered_gpos = talloc_steal(mem_ctx, dacl_filtered_gpos);
*_num_dacl_filtered_gpos = gpo_dn_idx;
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
/*
* This function determines whether the input cse_guid matches any of the input
* gpo_cse_guids. The boolean result is assigned to the _included output param.
*/
static bool
ad_gpo_includes_cse_guid(const char *cse_guid,
const char **gpo_cse_guids,
int num_gpo_cse_guids)
{
int i = 0;
const char *gpo_cse_guid = NULL;
for (i = 0; i < num_gpo_cse_guids; i++) {
gpo_cse_guid = gpo_cse_guids[i];
if (strcmp(gpo_cse_guid, cse_guid) == 0) {
return true;
}
}
return false;
}
/*
* This function takes an input dacl_filtered_gpos list, filters out any gpo
* that does not contain the input cse_guid, and assigns the result to the
* _cse_filtered_gpos output parameter.
*/
static errno_t
ad_gpo_filter_gpos_by_cse_guid(TALLOC_CTX *mem_ctx,
const char *cse_guid,
struct gp_gpo **dacl_filtered_gpos,
int num_dacl_filtered_gpos,
struct gp_gpo ***_cse_filtered_gpos,
int *_num_cse_filtered_gpos)
{
TALLOC_CTX *tmp_ctx = NULL;
int i = 0;
int ret = 0;
struct gp_gpo *dacl_filtered_gpo = NULL;
int gpo_dn_idx = 0;
struct gp_gpo **cse_filtered_gpos = NULL;
bool included;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
}
cse_filtered_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_dacl_filtered_gpos + 1);
if (cse_filtered_gpos == NULL) {
ret = ENOMEM;
goto done;
}
for (i = 0; i < num_dacl_filtered_gpos; i++) {
dacl_filtered_gpo = dacl_filtered_gpos[i];
DEBUG(SSSDBG_TRACE_ALL, "examining cse candidate_gpo_guid: %s\n",
dacl_filtered_gpo->gpo_guid);
included = ad_gpo_includes_cse_guid(cse_guid,
dacl_filtered_gpo->gpo_cse_guids,
dacl_filtered_gpo->num_gpo_cse_guids);
if (included) {
DEBUG(SSSDBG_TRACE_ALL,
"GPO applicable to target per cse_guid filtering\n");
cse_filtered_gpos[gpo_dn_idx] = talloc_steal(cse_filtered_gpos,
dacl_filtered_gpo);
dacl_filtered_gpos[i] = NULL;
gpo_dn_idx++;
} else {
DEBUG(SSSDBG_TRACE_ALL,
"GPO not applicable to target per cse_guid filtering\n");
continue;
}
}
cse_filtered_gpos[gpo_dn_idx] = NULL;
*_cse_filtered_gpos = talloc_steal(mem_ctx, cse_filtered_gpos);
*_num_cse_filtered_gpos = gpo_dn_idx;
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
/*
* This cse-specific function (GP_EXT_GUID_SECURITY) returns a boolean value
* based on whether the input user_sid or any of the input group_sids appear
* in the input list of privilege_sids.
*/
static bool
check_rights(char **privilege_sids,
int privilege_size,
const char *user_sid,
const char **group_sids,
int group_size)
{
int i, j;
for (i = 0; i < privilege_size; i++) {
if (strcmp(user_sid, privilege_sids[i]) == 0) {
return true;
}
for (j = 0; j < group_size; j++) {
if (strcmp(group_sids[j], privilege_sids[i]) == 0) {
return true;
}
}
}
return false;
}
/*
* This function parses the input ini_config object (which represents
* the cse-specific filename), and returns the policy_setting_value
* corresponding to the input policy_setting_key.
*/
static errno_t
ad_gpo_extract_policy_setting(TALLOC_CTX *mem_ctx,
struct ini_cfgobj *ini_config,
const char *policy_setting_key,
char **_policy_setting_value)
{
struct value_obj *vobj = NULL;
int ret;
const char *policy_setting_value;
ret = ini_get_config_valueobj(RIGHTS_SECTION, policy_setting_key, ini_config,
INI_GET_FIRST_VALUE, &vobj);
if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,
"ini_get_config_valueobj failed [%d][%s]\n", ret, strerror(ret));
goto done;
}
if (vobj == NULL) {
DEBUG(SSSDBG_TRACE_ALL, "section/name not found: [%s][%s]\n",
RIGHTS_SECTION, policy_setting_key);
ret = ENOENT;
goto done;
}
policy_setting_value = ini_get_string_config_value(vobj, &ret);
if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,
"ini_get_string_config_value failed [%d][%s]\n",
ret, strerror(ret));
goto done;
}
if (policy_setting_value[0]) {
*_policy_setting_value = talloc_strdup(mem_ctx, policy_setting_value);
if (!*_policy_setting_value) {
ret = ENOMEM;
goto done;
}
} else {
/* This is an explicitly empty policy setting.
* We need to remove this from the LDB.
*/
*_policy_setting_value = NULL;
}
ret = EOK;
done:
return ret;
}
/*
* This function parses the cse-specific (GP_EXT_GUID_SECURITY) filename,
* and stores the allow_key and deny_key of all of the gpo_map_types present
* in the file (as part of the GPO Result object in the sysdb cache).
*/
static errno_t
ad_gpo_store_policy_settings(struct sss_domain_info *domain,
const char *filename)
{
struct ini_cfgfile *file_ctx = NULL;
struct ini_cfgobj *ini_config = NULL;
int ret;
int i;
char *allow_value = NULL;
char *deny_value = NULL;
const char *allow_key = NULL;
const char *deny_key = NULL;
TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
}
ret = ini_config_create(&ini_config);
if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,
"ini_config_create failed [%d][%s]\n", ret, strerror(ret));
goto done;
}
ret = ini_config_file_open(filename, 0, &file_ctx);
if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,
"ini_config_file_open failed [%d][%s]\n", ret, strerror(ret));
goto done;
}
ret = ini_config_parse(file_ctx, INI_STOP_ON_NONE, 0, 0, ini_config);
if (ret != 0) {
DEBUG(SSSDBG_CRIT_FAILURE,
"ini_config_parse failed [%d][%s]\n", ret, strerror(ret));
goto done;
}
for (i = 0; i < GPO_MAP_NUM_OPTS; i++) {
struct gpo_map_option_entry entry = gpo_map_option_entries[i];
allow_key = entry.allow_key;
if (allow_key != NULL) {
DEBUG(SSSDBG_TRACE_ALL, "allow_key = %s\n", allow_key);
ret = ad_gpo_extract_policy_setting(tmp_ctx,
ini_config,
allow_key,
&allow_value);
if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_CRIT_FAILURE,
"ad_gpo_extract_policy_setting failed for %s [%d][%s]\n",
allow_key, ret, strerror(ret));
goto done;
} else if (ret != ENOENT) {
ret = sysdb_gpo_store_gpo_result_setting(domain,
allow_key,
allow_value);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"sysdb_gpo_store_gpo_result_setting failed for key:"
"'%s' value:'%s' [%d][%s]\n", allow_key, allow_value,
ret, sss_strerror(ret));
goto done;
}
}
}
deny_key = entry.deny_key;
if (deny_key != NULL) {
DEBUG(SSSDBG_TRACE_ALL, "deny_key = %s\n", deny_key);
ret = ad_gpo_extract_policy_setting(tmp_ctx,
ini_config,
deny_key,
&deny_value);
if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_CRIT_FAILURE,
"ad_gpo_extract_policy_setting failed for %s [%d][%s]\n",
deny_key, ret, strerror(ret));
goto done;
} else if (ret != ENOENT) {
ret = sysdb_gpo_store_gpo_result_setting(domain,
deny_key,
deny_value);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"sysdb_gpo_store_gpo_result_setting failed for key:"
"'%s' value:'%s' [%d][%s]\n", deny_key, deny_value,
ret, sss_strerror(ret));
goto done;
}
}
}
}
ret = EOK;
done:
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "Error encountered: %d.\n", ret);
}
ini_config_file_destroy(file_ctx);
ini_config_destroy(ini_config);
talloc_free(tmp_ctx);
return ret;
}
/*
* This cse-specific function (GP_EXT_GUID_SECURITY) performs the access
* check for determining whether logon access is granted or denied for
* the {user,domain} tuple specified in the inputs. This function returns EOK
* to indicate that access is granted. Any other return value indicates that
* access is denied.
*
* The access control algorithm first determines whether the "principal_sids"
* (i.e. user_sid or group_sids) appear in allowed_sids and denied_sids.
*
* For access to be granted, both the "allowed_sids_condition" *and* the
* "denied_sids_condition" must be met (in all other cases, access is denied).
* 1) The "allowed_sids_condition" is satisfied if any of the principal_sids
* appears in allowed_sids OR if the allowed_sids list is empty
* 2) The "denied_sids_condition" is satisfied if none of the principal_sids
* appear in denied_sids
*
* Note that a deployment that is unaware of GPO-based access-control policy
* settings is unaffected by them (b/c absence of allowed_sids grants access).
*
* Note that if a principal_sid appears in both allowed_sids and denied_sids,
* the "allowed_sids_condition" is met, but the "denied_sids_condition" is not.
* In other words, Deny takes precedence over Allow.
*/
static errno_t
ad_gpo_access_check(TALLOC_CTX *mem_ctx,
enum gpo_access_control_mode gpo_mode,
enum gpo_map_type gpo_map_type,
const char *user,
struct sss_domain_info *domain,
char **allowed_sids,
int allowed_size,
char **denied_sids,
int denied_size)
{
const char *user_sid;
const char **group_sids;
int group_size = 0;
bool access_granted = false;
bool access_denied = false;
int ret;
int j;
DEBUG(SSSDBG_TRACE_FUNC, "RESULTANT POLICY:\n");
DEBUG(SSSDBG_TRACE_FUNC, "gpo_map_type: %s\n",
gpo_map_type_string(gpo_map_type));
DEBUG(SSSDBG_TRACE_FUNC, "allowed_size = %d\n", allowed_size);
for (j= 0; j < allowed_size; j++) {
DEBUG(SSSDBG_TRACE_FUNC, "allowed_sids[%d] = %s\n", j, allowed_sids[j]);
}
DEBUG(SSSDBG_TRACE_FUNC, "denied_size = %d\n", denied_size);
for (j= 0; j < denied_size; j++) {
DEBUG(SSSDBG_TRACE_FUNC, " denied_sids[%d] = %s\n", j, denied_sids[j]);
}
ret = ad_gpo_get_sids(mem_ctx, user, domain, &user_sid,
&group_sids, &group_size);
if (ret != EOK) {
ret = ERR_NO_SIDS;
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve SIDs: [%d](%s)\n", ret, sss_strerror(ret));
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, "CURRENT USER:\n");
DEBUG(SSSDBG_TRACE_FUNC, " user_sid = %s\n", user_sid);
for (j= 0; j < group_size; j++) {
DEBUG(SSSDBG_TRACE_FUNC, " group_sids[%d] = %s\n", j,
group_sids[j]);
}
if (allowed_size == 0) {
access_granted = true;
} else {
access_granted = check_rights(allowed_sids, allowed_size, user_sid,
group_sids, group_size);
}
DEBUG(SSSDBG_TRACE_FUNC, "POLICY DECISION:\n");
DEBUG(SSSDBG_TRACE_FUNC, " access_granted = %d\n", access_granted);
access_denied = check_rights(denied_sids, denied_size, user_sid,
group_sids, group_size);
DEBUG(SSSDBG_TRACE_FUNC, " access_denied = %d\n", access_denied);
if (access_granted && !access_denied) {
return EOK;
} else {
switch (gpo_mode) {
case GPO_ACCESS_CONTROL_ENFORCING:
return ERR_ACCESS_DENIED;
case GPO_ACCESS_CONTROL_PERMISSIVE:
DEBUG(SSSDBG_TRACE_FUNC, "access denied: permissive mode\n");
sss_log_ext(SSS_LOG_WARNING, LOG_AUTHPRIV, "Warning: user would " \
"have been denied GPO-based logon access if the " \
"ad_gpo_access_control option were set to enforcing " \
"mode.");
return EOK;
default:
return EINVAL;
}
}
done:
if (ret) {
DEBUG(SSSDBG_CRIT_FAILURE, "Error encountered: %d.\n", ret);
}
return ret;
}
#define GPO_CHILD_LOG_FILE "gpo_child"
static errno_t gpo_child_init(void)
{
return child_debug_init(GPO_CHILD_LOG_FILE, &gpo_child_debug_fd);
}
/*
* This function retrieves the raw policy_setting_value for the input key from
* the GPO_Result object in the sysdb cache. It then parses the raw value and
* uses the results to populate the output parameters with the sids_list and
* the size of the sids_list.
*/
errno_t
parse_policy_setting_value(TALLOC_CTX *mem_ctx,
struct sss_domain_info *domain,
const char *key,
char ***_sids_list,
int *_sids_list_size)
{
int ret;
int i;
const char *value;
int sids_list_size;
char **sids_list = NULL;
ret = sysdb_gpo_get_gpo_result_setting(mem_ctx, domain, key, &value);
if (ret == ENOENT) {
DEBUG(SSSDBG_TRACE_FUNC, "No previous GPO result\n");
value = NULL;
} else if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Cannot retrieve settings from sysdb for key: '%s' [%d][%s].\n",
key, ret, sss_strerror(ret));
goto done;
}
if (value == NULL) {
DEBUG(SSSDBG_TRACE_FUNC,
"No value for key [%s] found in gpo result\n", key);
sids_list_size = 0;
} else {
ret = split_on_separator(mem_ctx, value, ',', true, true,
&sids_list, &sids_list_size);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Cannot parse list of sids %s: %d\n", value, ret);
ret = EINVAL;
goto done;
}
for (i = 0; i < sids_list_size; i++) {
/* remove the asterisk prefix found on sids */
sids_list[i]++;
}
}
*_sids_list = talloc_steal(mem_ctx, sids_list);
*_sids_list_size = sids_list_size;
ret = EOK;
done:
return ret;
}
/*
* This cse-specific function (GP_EXT_GUID_SECURITY) performs HBAC policy
* processing and determines whether logon access is granted or denied for
* the {user,domain} tuple specified in the inputs. This function returns EOK
* to indicate that access is granted. Any other return value indicates that
* access is denied.
*
* Internally, this function retrieves the allow_value and deny_value for the
* input gpo_map_type from the GPO Result object in the sysdb cache, parses
* the values into allow_sids and deny_sids, and executes the access control
* algorithm which compares the allow_sids and deny_sids against the user_sid
* and group_sids for the input user.
*/
static errno_t
ad_gpo_perform_hbac_processing(TALLOC_CTX *mem_ctx,
enum gpo_access_control_mode gpo_mode,
enum gpo_map_type gpo_map_type,
const char *user,
struct sss_domain_info *domain)
{
int ret;
const char *allow_key = NULL;
char **allow_sids;
int allow_size ;
const char *deny_key = NULL;
char **deny_sids;
int deny_size;
allow_key = gpo_map_option_entries[gpo_map_type].allow_key;
DEBUG(SSSDBG_TRACE_ALL, "allow_key: %s\n", allow_key);
deny_key = gpo_map_option_entries[gpo_map_type].deny_key;
DEBUG(SSSDBG_TRACE_ALL, "deny_key: %s\n", deny_key);
ret = parse_policy_setting_value(mem_ctx, domain, allow_key,
&allow_sids, &allow_size);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"parse_policy_setting_value failed for key %s: [%d](%s)\n",
allow_key, ret, sss_strerror(ret));
ret = EINVAL;
goto done;
}
ret = parse_policy_setting_value(mem_ctx, domain, deny_key,
&deny_sids, &deny_size);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"parse_policy_setting_value failed for key %s: [%d](%s)\n",
deny_key, ret, sss_strerror(ret));
ret = EINVAL;
goto done;
}
/* perform access check with the final resultant allow_sids and deny_sids */
ret = ad_gpo_access_check(mem_ctx, gpo_mode, gpo_map_type, user, domain,
allow_sids, allow_size, deny_sids, deny_size);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"GPO access check failed: [%d](%s)\n",
ret, strerror(ret));
goto done;
}
done:
return ret;
}
/* == ad_gpo_access_send/recv implementation ================================*/
struct ad_gpo_access_state {
struct tevent_context *ev;
struct ldb_context *ldb_ctx;
enum gpo_access_control_mode gpo_mode;
enum gpo_map_type gpo_map_type;
struct sdap_id_conn_ctx *conn;
struct sdap_id_op *sdap_op;
char *server_hostname;
struct sdap_options *opts;
int timeout;
struct sss_domain_info *domain;
const char *user;
int gpo_timeout_option;
const char *ad_hostname;
const char *target_dn;
struct gp_gpo **dacl_filtered_gpos;
int num_dacl_filtered_gpos;
struct gp_gpo **cse_filtered_gpos;
int num_cse_filtered_gpos;
int cse_gpo_index;
};
static void ad_gpo_connect_done(struct tevent_req *subreq);
static void ad_gpo_target_dn_retrieval_done(struct tevent_req *subreq);
static void ad_gpo_process_som_done(struct tevent_req *subreq);
static void ad_gpo_process_gpo_done(struct tevent_req *subreq);
static errno_t ad_gpo_cse_step(struct tevent_req *req);
static void ad_gpo_cse_done(struct tevent_req *subreq);
struct tevent_req *
ad_gpo_access_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sss_domain_info *domain,
struct ad_access_ctx *ctx,
const char *user,
const char *service)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct ad_gpo_access_state *state;
errno_t ret;
int hret;
hash_key_t key;
hash_value_t val;
enum gpo_map_type gpo_map_type;
/* setup logging for gpo child */
gpo_child_init();
req = tevent_req_create(mem_ctx, &state, struct ad_gpo_access_state);
if (req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
return NULL;
}
/* determine service's option_type (e.g. interactive, network, etc) */
key.type = HASH_KEY_STRING;
key.str = talloc_strdup(state, service);
hret = hash_lookup(ctx->gpo_map_options_table, &key, &val);
if (hret != HASH_SUCCESS && hret != HASH_ERROR_KEY_NOT_FOUND) {
DEBUG(SSSDBG_OP_FAILURE, "Error checking hash table: [%s]\n",
hash_error_string(hret));
ret = EINVAL;
goto immediately;
}
/* if service isn't mapped, map it to value of ad_gpo_default_right option */
if (hret == HASH_ERROR_KEY_NOT_FOUND) {
DEBUG(SSSDBG_TRACE_FUNC, "using default right\n");
gpo_map_type = ctx->gpo_default_right;
} else {
gpo_map_type = (enum gpo_map_type) val.i;
}
DEBUG(SSSDBG_TRACE_FUNC, "service %s maps to %s\n", service,
gpo_map_type_string(gpo_map_type));
if (gpo_map_type == GPO_MAP_PERMIT) {
ret = EOK;
tevent_req_done(req);
tevent_req_post(req, ev);
goto immediately;
}
if (gpo_map_type == GPO_MAP_DENY) {
switch (ctx->gpo_access_control_mode) {
case GPO_ACCESS_CONTROL_ENFORCING:
ret = ERR_ACCESS_DENIED;
goto immediately;
case GPO_ACCESS_CONTROL_PERMISSIVE:
DEBUG(SSSDBG_TRACE_FUNC, "access denied: permissive mode\n");
sss_log_ext(SSS_LOG_WARNING, LOG_AUTHPRIV, "Warning: user would " \
"have been denied GPO-based logon access if the " \
"ad_gpo_access_control option were set to enforcing " \
"mode.");
ret = EOK;
tevent_req_done(req);
tevent_req_post(req, ev);
goto immediately;
default:
ret = EINVAL;
goto immediately;
}
}
state->gpo_map_type = gpo_map_type;
state->domain = domain;
state->dacl_filtered_gpos = NULL;
state->num_dacl_filtered_gpos = 0;
state->cse_filtered_gpos = NULL;
state->num_cse_filtered_gpos = 0;
state->cse_gpo_index = 0;
state->ev = ev;
state->user = user;
state->ldb_ctx = sysdb_ctx_get_ldb(domain->sysdb);
state->gpo_mode = ctx->gpo_access_control_mode;
state->gpo_timeout_option = ctx->gpo_cache_timeout;
state->ad_hostname = dp_opt_get_string(ctx->ad_options, AD_HOSTNAME);
state->opts = ctx->sdap_access_ctx->id_ctx->opts;
state->timeout = dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT);
state->conn = ad_get_dom_ldap_conn(ctx->ad_id_ctx, domain);
state->sdap_op = sdap_id_op_create(state, state->conn->conn_cache);
if (state->sdap_op == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_id_op_create failed.\n");
ret = ENOMEM;
goto immediately;
}
subreq = sdap_id_op_connect_send(state->sdap_op, state, &ret);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE,
"sdap_id_op_connect_send failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto immediately;
}
tevent_req_set_callback(subreq, ad_gpo_connect_done, req);
ret = EOK;
immediately:
if (ret != EOK) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static errno_t
process_offline_gpos(TALLOC_CTX *mem_ctx,
const char *user,
enum gpo_access_control_mode gpo_mode,
struct sss_domain_info *domain,
enum gpo_map_type gpo_map_type)
{
errno_t ret;
ret = ad_gpo_perform_hbac_processing(mem_ctx,
gpo_mode,
gpo_map_type,
user,
domain);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "HBAC processing failed: [%d](%s}\n",
ret, sss_strerror(ret));
goto done;
}
/* we have successfully processed all offline gpos */
ret = EOK;
done:
return ret;
}
static void
ad_gpo_connect_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
char *filter;
char *sam_account_name;
char *domain_dn;
int dp_error;
errno_t ret;
char *server_uri;
LDAPURLDesc *lud;
const char *attrs[] = {AD_AT_DN, AD_AT_UAC, NULL};
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_access_state);
ret = sdap_id_op_connect_recv(subreq, &dp_error);
talloc_zfree(subreq);
if (ret != EOK) {
if (dp_error != DP_ERR_OFFLINE) {
DEBUG(SSSDBG_OP_FAILURE,
"Failed to connect to AD server: [%d](%s)\n",
ret, strerror(ret));
goto done;
} else {
DEBUG(SSSDBG_TRACE_FUNC, "Preparing for offline operation.\n");
ret = process_offline_gpos(state,
state->user,
state->gpo_mode,
state->domain,
state->gpo_map_type);
if (ret == EOK) {
DEBUG(SSSDBG_TRACE_FUNC, "process_offline_gpos succeeded\n");
tevent_req_done(req);
goto done;
} else {
DEBUG(SSSDBG_OP_FAILURE,
"process_offline_gpos failed [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
}
}
/* extract server_hostname from server_uri */
server_uri = state->conn->service->uri;
ret = ldap_url_parse(server_uri, &lud);
if (ret != LDAP_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to parse ldap URI (%s)!\n", server_uri);
ret = EINVAL;
goto done;
}
if (lud->lud_host == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE,
"The LDAP URI (%s) did not contain a host name\n", server_uri);
ldap_free_urldesc(lud);
ret = EINVAL;
goto done;
}
state->server_hostname = talloc_strdup(state, lud->lud_host);
ldap_free_urldesc(lud);
if (!state->server_hostname) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_ALL, "server_hostname from uri: %s\n",
state->server_hostname);
sam_account_name = sss_krb5_get_primary(state, "%S$", state->ad_hostname);
if (sam_account_name == NULL) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC, "sam_account_name is %s\n", sam_account_name);
/* Convert the domain name into domain DN */
ret = domain_to_basedn(state, state->domain->name, &domain_dn);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Cannot convert domain name [%s] to base DN [%d]: %s\n",
state->domain->name, ret, sss_strerror(ret));
goto done;
}
/* SDAP_OC_USER objectclass covers both users and computers */
filter = talloc_asprintf(state,
"(&(objectclass=%s)(%s=%s))",
state->opts->user_map[SDAP_OC_USER].name,
state->opts->user_map[SDAP_AT_USER_NAME].name,
sam_account_name);
talloc_zfree(sam_account_name);
if (filter == NULL) {
ret = ENOMEM;
goto done;
}
subreq = sdap_get_generic_send(state, state->ev, state->opts,
sdap_id_op_handle(state->sdap_op),
domain_dn, LDAP_SCOPE_SUBTREE,
filter, attrs, NULL, 0,
state->timeout,
false);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
ret = EIO;
goto done;
}
tevent_req_set_callback(subreq, ad_gpo_target_dn_retrieval_done, req);
ret = EOK;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
}
}
static void
ad_gpo_target_dn_retrieval_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
int ret;
int dp_error;
size_t reply_count;
struct sysdb_attrs **reply;
const char *target_dn = NULL;
uint32_t uac;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_access_state);
ret = sdap_get_generic_recv(subreq, state,
&reply_count, &reply);
talloc_zfree(subreq);
if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get policy target's DN: [%d](%s)\n",
ret, sss_strerror(ret));
ret = ENOENT;
goto done;
}
/* make sure there is only one non-NULL reply returned */
if (reply_count < 1) {
DEBUG(SSSDBG_OP_FAILURE, "No DN retrieved for policy target.\n");
ret = ENOENT;
goto done;
} else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Multiple replies for policy target\n");
ret = ERR_INTERNAL;
goto done;
} else if (reply == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "reply_count is 1, but reply is NULL\n");
ret = ERR_INTERNAL;
goto done;
}
/* reply[0] holds requested attributes of single reply */
ret = sysdb_attrs_get_string(reply[0], AD_AT_DN, &target_dn);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
state->target_dn = talloc_steal(state, target_dn);
if (state->target_dn == NULL) {
ret = ENOMEM;
goto done;
}
ret = sysdb_attrs_get_uint32_t(reply[0], AD_AT_UAC, &uac);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_uint32_t failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
/* we only support computer policy targets, not users */
if (!(uac & UAC_WORKSTATION_TRUST_ACCOUNT)) {
ret = EINVAL;
goto done;
}
subreq = ad_gpo_process_som_send(state,
state->ev,
state->conn,
state->ldb_ctx,
state->sdap_op,
state->opts,
state->timeout,
state->target_dn,
state->domain->name);
if (subreq == NULL) {
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(subreq, ad_gpo_process_som_done, req);
ret = EOK;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
}
}
static void
ad_gpo_process_som_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
int ret;
struct gp_som **som_list;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_access_state);
ret = ad_gpo_process_som_recv(subreq, state, &som_list);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get som list: [%d](%s)\n",
ret, sss_strerror(ret));
ret = ENOENT;
goto done;
}
subreq = ad_gpo_process_gpo_send(state,
state->ev,
state->sdap_op,
state->opts,
state->server_hostname,
state->timeout,
som_list);
if (subreq == NULL) {
ret = ENOMEM;
goto done;
}
tevent_req_set_callback(subreq, ad_gpo_process_gpo_done, req);
ret = EOK;
done:
if (ret != EOK) {
tevent_req_error(req, ret);
}
}
/*
* This function retrieves a list of candidate_gpos and potentially reduces it
* to a list of dacl_filtered_gpos, based on each GPO's DACL.
*
* This function then takes the list of dacl_filtered_gpos and potentially
* reduces it to a list of cse_filtered_gpos, based on whether each GPO's list
* of cse_guids includes the "SecuritySettings" CSE GUID (used for HBAC).
*
* Ultimately, this function then sends each cse_filtered_gpo to the gpo_child,
* which retrieves the GPT.INI and policy files (as needed). Once all files
* have been downloaded, the ad_gpo_cse_done function performs HBAC processing.
*/
static void
ad_gpo_process_gpo_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
int ret;
int dp_error;
struct gp_gpo **candidate_gpos = NULL;
int num_candidate_gpos = 0;
int i = 0;
const char **cse_filtered_gpo_guids;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_access_state);
ret = ad_gpo_process_gpo_recv(subreq, state, &candidate_gpos,
&num_candidate_gpos);
talloc_zfree(subreq);
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get GPO list: [%d](%s)\n",
ret, sss_strerror(ret));
ret = ENOENT;
goto done;
}
ret = ad_gpo_filter_gpos_by_dacl(state, state->user, state->domain,
state->opts->idmap_ctx->map,
candidate_gpos, num_candidate_gpos,
&state->dacl_filtered_gpos,
&state->num_dacl_filtered_gpos);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to filter GPO list by DACKL: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
if (state->dacl_filtered_gpos[0] == NULL) {
/* since no applicable gpos were found, there is nothing to enforce */
DEBUG(SSSDBG_TRACE_FUNC,
"no applicable gpos found after dacl filtering\n");
ret = EOK;
goto done;
}
for (i = 0; i < state->num_dacl_filtered_gpos; i++) {
DEBUG(SSSDBG_TRACE_FUNC, "dacl_filtered_gpos[%d]->gpo_guid is %s\n", i,
state->dacl_filtered_gpos[i]->gpo_guid);
}
ret = ad_gpo_filter_gpos_by_cse_guid(state,
GP_EXT_GUID_SECURITY,
state->dacl_filtered_gpos,
state->num_dacl_filtered_gpos,
&state->cse_filtered_gpos,
&state->num_cse_filtered_gpos);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to filter GPO list by CSE_GUID: [%d](%s)\n",
ret, strerror(ret));
goto done;
}
if (state->cse_filtered_gpos[0] == NULL) {
/* no gpos contain "SecuritySettings" cse_guid, nothing to enforce */
DEBUG(SSSDBG_TRACE_FUNC,
"no applicable gpos found after cse_guid filtering\n");
ret = EOK;
goto done;
}
/* we create and populate an array of applicable gpo-guids */
cse_filtered_gpo_guids =
talloc_array(state, const char *, state->num_cse_filtered_gpos);
if (cse_filtered_gpo_guids == NULL) {
ret = ENOMEM;
goto done;
}
for (i = 0; i < state->num_cse_filtered_gpos; i++) {
DEBUG(SSSDBG_TRACE_FUNC, "cse_filtered_gpos[%d]->gpo_guid is %s\n", i,
state->cse_filtered_gpos[i]->gpo_guid);
cse_filtered_gpo_guids[i] = talloc_steal(cse_filtered_gpo_guids,
state->cse_filtered_gpos[i]->gpo_guid);
if (cse_filtered_gpo_guids[i] == NULL) {
ret = ENOMEM;
goto done;
}
}
DEBUG(SSSDBG_TRACE_FUNC, "num_cse_filtered_gpos: %d\n",
state->num_cse_filtered_gpos);
/*
* before we start processing each gpo, we delete the GPO Result object
* from the sysdb cache so that any previous policy settings are cleared;
* subsequent functions will add the GPO Result object (and populate it
* with resultant policy settings) for this policy application
*/
ret = sysdb_gpo_delete_gpo_result_object(state, state->domain);
if (ret != EOK) {
switch (ret) {
case ENOENT:
DEBUG(SSSDBG_TRACE_FUNC, "No GPO Result available in cache\n");
break;
default:
DEBUG(SSSDBG_FATAL_FAILURE,
"Could not delete GPO Result from cache: [%s]\n",
strerror(ret));
goto done;
}
}
ret = ad_gpo_cse_step(req);
done:
if (ret == EOK) {
tevent_req_done(req);
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
}
}
static errno_t
ad_gpo_cse_step(struct tevent_req *req)
{
struct tevent_req *subreq;
struct ad_gpo_access_state *state;
int i = 0;
struct ldb_result *res;
errno_t ret;
bool send_to_child = true;
int cached_gpt_version = 0;
time_t policy_file_timeout = 0;
state = tevent_req_data(req, struct ad_gpo_access_state);
struct gp_gpo *cse_filtered_gpo =
state->cse_filtered_gpos[state->cse_gpo_index];
/* cse_filtered_gpo is NULL after all GPO policy files have been downloaded */
if (cse_filtered_gpo == NULL) return EOK;
DEBUG(SSSDBG_TRACE_FUNC, "cse filtered_gpos[%d]->gpo_guid is %s\n",
state->cse_gpo_index, cse_filtered_gpo->gpo_guid);
for (i = 0; i < cse_filtered_gpo->num_gpo_cse_guids; i++) {
DEBUG(SSSDBG_TRACE_ALL,
"cse_filtered_gpos[%d]->gpo_cse_guids[%d]->gpo_guid is %s\n",
state->cse_gpo_index, i, cse_filtered_gpo->gpo_cse_guids[i]);
}
DEBUG(SSSDBG_TRACE_FUNC, "smb_server: %s\n", cse_filtered_gpo->smb_server);
DEBUG(SSSDBG_TRACE_FUNC, "smb_share: %s\n", cse_filtered_gpo->smb_share);
DEBUG(SSSDBG_TRACE_FUNC, "smb_path: %s\n", cse_filtered_gpo->smb_path);
DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", cse_filtered_gpo->gpo_guid);
cse_filtered_gpo->policy_filename =
talloc_asprintf(state,
GPO_CACHE_PATH"%s%s",
cse_filtered_gpo->smb_path,
GP_EXT_GUID_SECURITY_SUFFIX);
if (cse_filtered_gpo->policy_filename == NULL) {
return ENOMEM;
}
/* retrieve gpo cache entry; set cached_gpt_version to -1 if unavailable */
DEBUG(SSSDBG_TRACE_FUNC, "retrieving GPO from cache [%s]\n",
cse_filtered_gpo->gpo_guid);
ret = sysdb_gpo_get_gpo_by_guid(state,
state->domain,
cse_filtered_gpo->gpo_guid,
&res);
if (ret == EOK) {
/*
* Note: if the timeout is valid, then we can later avoid downloading
* the GPT.INI file, as well as any policy files (i.e. we don't need
* to interact with the gpo_child at all). However, even if the timeout
* is not valid, while we will have to interact with the gpo child to
* download the GPT.INI file, we may still be able to avoid downloading
* the policy files (if the cached_gpt_version is the same as the
* GPT.INI version). In other words, the timeout is *not* an expiration
* for the entire cache entry; the cached_gpt_version never expires.
*/
cached_gpt_version = ldb_msg_find_attr_as_int(res->msgs[0],
SYSDB_GPO_VERSION_ATTR,
0);
policy_file_timeout = ldb_msg_find_attr_as_uint64
(res->msgs[0], SYSDB_GPO_TIMEOUT_ATTR, 0);
if (policy_file_timeout >= time(NULL)) {
send_to_child = false;
}
} else if (ret == ENOENT) {
DEBUG(SSSDBG_TRACE_FUNC, "ENOENT\n");
cached_gpt_version = -1;
} else {
DEBUG(SSSDBG_FATAL_FAILURE, "Could not read GPO from cache: [%s]\n",
strerror(ret));
return ret;
}
DEBUG(SSSDBG_TRACE_FUNC, "send_to_child: %d\n", send_to_child);
DEBUG(SSSDBG_TRACE_FUNC, "cached_gpt_version: %d\n", cached_gpt_version);
cse_filtered_gpo->send_to_child = send_to_child;
subreq = ad_gpo_process_cse_send(state,
state->ev,
send_to_child,
state->domain,
cse_filtered_gpo->gpo_guid,
cse_filtered_gpo->smb_server,
cse_filtered_gpo->smb_share,
cse_filtered_gpo->smb_path,
GP_EXT_GUID_SECURITY_SUFFIX,
cached_gpt_version,
state->gpo_timeout_option);
tevent_req_set_callback(subreq, ad_gpo_cse_done, req);
return EAGAIN;
}
/*
* This cse-specific function (GP_EXT_GUID_SECURITY) increments the
* cse_gpo_index until the policy settings for all applicable GPOs have been
* stored as part of the GPO Result object in the sysdb cache. Once all
* GPOs have been processed, this functions performs HBAC processing by
* comparing the resultant policy setting values in the GPO Result object
* with the user_sid/group_sids of interest.
*/
static void
ad_gpo_cse_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_access_state *state;
int ret;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_access_state);
struct gp_gpo *cse_filtered_gpo =
state->cse_filtered_gpos[state->cse_gpo_index];
const char *gpo_guid = cse_filtered_gpo->gpo_guid;
DEBUG(SSSDBG_TRACE_FUNC, "gpo_guid: %s\n", gpo_guid);
ret = ad_gpo_process_cse_recv(subreq);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Unable to retrieve policy data: [%d](%s}\n",
ret, sss_strerror(ret));
goto done;
}
/*
* now that the policy file for this gpo have been downloaded to the
* GPO CACHE, we store all of the supported keys present in the file
* (as part of the GPO Result object in the sysdb cache).
*/
ret = ad_gpo_store_policy_settings(state->domain,
cse_filtered_gpo->policy_filename);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ad_gpo_store_policy_settings failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
state->cse_gpo_index++;
ret = ad_gpo_cse_step(req);
if (ret == EOK) {
/* ret is EOK only after all GPO policy files have been downloaded */
ret = ad_gpo_perform_hbac_processing(state,
state->gpo_mode,
state->gpo_map_type,
state->user,
state->domain);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "HBAC processing failed: [%d](%s}\n",
ret, sss_strerror(ret));
goto done;
}
}
done:
if (ret == EOK) {
tevent_req_done(req);
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
}
}
errno_t
ad_gpo_access_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
/* == ad_gpo_process_som_send/recv helpers ================================= */
/*
* This function returns the parent of an LDAP DN
*/
static errno_t
ad_gpo_parent_dn(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb_ctx,
const char *dn,
const char **_parent_dn)
{
struct ldb_dn *ldb_dn;
struct ldb_dn *parent_ldb_dn;
const char *p;
int ret;
TALLOC_CTX *tmp_ctx = NULL;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
}
ldb_dn = ldb_dn_new(tmp_ctx, ldb_ctx, dn);
parent_ldb_dn = ldb_dn_get_parent(tmp_ctx, ldb_dn);
p = ldb_dn_get_linearized(parent_ldb_dn);
*_parent_dn = talloc_steal(mem_ctx, p);
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
/*
* This function populates the _som_list output parameter by parsing the input
* DN into a list of gp_som objects. This function essentially repeatedly
* appends the input DN's parent to the SOM List (if the parent starts with
* "OU=" or "DC="), until the first "DC=" component is reached.
* Example: if input DN is "CN=MyComputer,CN=Computers,OU=Sales,DC=FOO,DC=COM",
* then SOM List has 2 SOM entries: {[OU=Sales,DC=FOO,DC=COM], [DC=FOO, DC=COM]}
*/
static errno_t
ad_gpo_populate_som_list(TALLOC_CTX *mem_ctx,
struct ldb_context *ldb_ctx,
const char *target_dn,
int *_num_soms,
struct gp_som ***_som_list)
{
TALLOC_CTX *tmp_ctx = NULL;
int ret;
int rdn_count = 0;
int som_idx = 0;
struct gp_som **som_list;
const char *parent_dn = NULL;
const char *tmp_dn = NULL;
struct ldb_dn *ldb_target_dn;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
}
ldb_target_dn = ldb_dn_new(tmp_ctx, ldb_ctx, target_dn);
if (ldb_target_dn == NULL) {
ret = EINVAL;
goto done;
}
rdn_count = ldb_dn_get_comp_num(ldb_target_dn);
if (rdn_count == -1) {
ret = EINVAL;
goto done;
}
if (rdn_count == 0) {
*_som_list = NULL;
ret = EOK;
goto done;
}
/* assume the worst-case, in which every parent is a SOM */
/* include space for Site SOM and NULL: rdn_count + 1 + 1 */
som_list = talloc_array(tmp_ctx, struct gp_som *, rdn_count + 1 + 1);
if (som_list == NULL) {
ret = ENOMEM;
goto done;
}
/* first, populate the OU and Domain SOMs */
tmp_dn = target_dn;;
while ((ad_gpo_parent_dn(tmp_ctx, ldb_ctx, tmp_dn, &parent_dn)) == EOK) {
if ((strncasecmp(parent_dn, "OU=", strlen("OU=")) == 0) ||
(strncasecmp(parent_dn, "DC=", strlen("DC=")) == 0)) {
som_list[som_idx] = talloc_zero(som_list, struct gp_som);
if (som_list[som_idx] == NULL) {
ret = ENOMEM;
goto done;
}
som_list[som_idx]->som_dn = talloc_steal(som_list[som_idx],
parent_dn);
if (som_list[som_idx]->som_dn == NULL) {
ret = ENOMEM;
goto done;
}
som_idx++;
}
if (strncasecmp(parent_dn, "DC=", strlen("DC=")) == 0) {
break;
}
tmp_dn = parent_dn;
}
som_list[som_idx] = NULL;
*_num_soms = som_idx;
*_som_list = talloc_steal(mem_ctx, som_list);
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
/*
* This function populates the _gplink_list output parameter by parsing the
* input raw_gplink_value into an array of gp_gplink objects, each consisting of
* a GPO DN and bool enforced field.
*
* The raw_gplink_value is single string consisting of multiple gplink strings.
* The raw_gplink_value is in the following format:
* "[GPO_DN_1;GPLinkOptions_1]...[GPO_DN_n;GPLinkOptions_n]"
*
* Each gplink string consists of a GPO DN and a GPLinkOptions field (which
* indicates whether its associated GPO DN is ignored, unenforced, or enforced).
* If a GPO DN is flagged as ignored, it is discarded and will not be added to
* the _gplink_list. If the allow_enforced_only input is true, AND a GPO DN is
* flagged as unenforced, it will also be discarded.
*
* Example: if raw_gplink_value="[OU=Sales,DC=FOO,DC=COM;0][DC=FOO,DC=COM;2]"
* and allow_enforced_only=FALSE, then the output would consist of following:
* _gplink_list[0]: {GPO DN: "OU=Sales,DC=FOO,DC=COM", enforced: FALSE}
* _gplink_list[1]: {GPO DN: "DC=FOO,DC=COM", enforced: TRUE}
*/
static errno_t
ad_gpo_populate_gplink_list(TALLOC_CTX *mem_ctx,
const char *som_dn,
char *raw_gplink_value,
struct gp_gplink ***_gplink_list,
bool allow_enforced_only)
{
TALLOC_CTX *tmp_ctx = NULL;
char *ptr;
char *first;
char *last;
char *dn;
char *gplink_options;
const char delim = ']';
struct gp_gplink **gplink_list;
int i;
int ret;
uint32_t gplink_number;
int gplink_count = 0;
int num_enabled = 0;
if (raw_gplink_value == NULL ||
*raw_gplink_value == '\0' ||
_gplink_list == NULL) {
return EINVAL;
}
DEBUG(SSSDBG_TRACE_FUNC, "som_dn: %s\n", som_dn);
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
}
ptr = raw_gplink_value;
while ((ptr = strchr(ptr, delim))) {
ptr++;
gplink_count++;
}
if (gplink_count == 0) {
ret = EOK;
goto done;
}
gplink_list = talloc_array(tmp_ctx, struct gp_gplink *, gplink_count + 1);
if (gplink_list == NULL) {
ret = ENOMEM;
goto done;
}
num_enabled = 0;
ptr = raw_gplink_value;
for (i = 0; i < gplink_count; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
ret = EINVAL;
goto done;
}
*last = '\0';
last++;
dn = first;
if ( strncasecmp(dn, "LDAP://", 7)== 0 ) {
dn = dn + 7;
}
gplink_options = strchr(first, ';');
if (gplink_options == NULL) {
ret = EINVAL;
goto done;
}
*gplink_options = '\0';
gplink_options++;
gplink_number = strtouint32(gplink_options, NULL, 10);
if (errno != 0) {
ret = errno;
DEBUG(SSSDBG_OP_FAILURE,
"strtouint32 failed: [%d](%s)\n", ret, sss_strerror(ret));
goto done;
}
DEBUG(SSSDBG_TRACE_ALL,
"gplink_list[%d]: [%s; %d]\n", num_enabled, dn, gplink_number);
if ((gplink_number == 1) || (gplink_number ==3)) {
/* ignore flag is set */
DEBUG(SSSDBG_TRACE_ALL, "ignored gpo skipped\n");
ptr = last;
continue;
}
if (allow_enforced_only && (gplink_number == 0)) {
/* unenforced flag is set; only enforced gpos allowed */
DEBUG(SSSDBG_TRACE_ALL, "unenforced gpo skipped\n");
ptr = last;
continue;
}
gplink_list[num_enabled] = talloc_zero(gplink_list, struct gp_gplink);
if (gplink_list[num_enabled] == NULL) {
ret = ENOMEM;
goto done;
}
gplink_list[num_enabled]->gpo_dn =
talloc_strdup(gplink_list[num_enabled], dn);
if (gplink_list[num_enabled]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
if (gplink_number == 0) {
gplink_list[num_enabled]->enforced = 0;
num_enabled++;
} else if (gplink_number == 2) {
gplink_list[num_enabled]->enforced = 1;
num_enabled++;
} else {
ret = EINVAL;
goto done;
}
ptr = last;
}
gplink_list[num_enabled] = NULL;
*_gplink_list = talloc_steal(mem_ctx, gplink_list);
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
/* == ad_gpo_process_som_send/recv implementation ========================== */
struct ad_gpo_process_som_state {
struct tevent_context *ev;
struct sdap_id_op *sdap_op;
struct sdap_options *opts;
int timeout;
bool allow_enforced_only;
char *site_name;
char *site_dn;
struct gp_som **som_list;
int som_index;
int num_soms;
};
static void ad_gpo_site_name_retrieval_done(struct tevent_req *subreq);
static void ad_gpo_site_dn_retrieval_done(struct tevent_req *subreq);
static errno_t ad_gpo_get_som_attrs_step(struct tevent_req *req);
static void ad_gpo_get_som_attrs_done(struct tevent_req *subreq);
/*
* This function uses the input target_dn and input domain_name to populate
* a list of gp_som objects. Each object in this list represents a SOM
* associated with the target (such as OU, Domain, and Site).
*
* The inputs are used to determine the DNs of each SOM associated with the
* target. In turn, the SOM object DNs are used to retrieve certain LDAP
* attributes of each SOM object, that are parsed into an array of gp_gplink
* objects, essentially representing the GPOs that have been linked to each
* SOM object. Note that it is perfectly valid for there to be *no* GPOs
* linked to a SOM object.
*/
struct tevent_req *
ad_gpo_process_som_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sdap_id_conn_ctx *conn,
struct ldb_context *ldb_ctx,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
int timeout,
const char *target_dn,
const char *domain_name)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct ad_gpo_process_som_state *state;
errno_t ret;
req = tevent_req_create(mem_ctx, &state, struct ad_gpo_process_som_state);
if (req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
return NULL;
}
state->ev = ev;
state->sdap_op = sdap_op;
state->opts = opts;
state->timeout = timeout;
state->som_index = 0;
state->allow_enforced_only = 0;
ret = ad_gpo_populate_som_list(state, ldb_ctx, target_dn,
&state->num_soms, &state->som_list);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve SOM List : [%d](%s)\n",
ret, sss_strerror(ret));
ret = ENOENT;
goto immediately;
}
if (state->som_list == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "target dn must have at least one parent\n");
ret = EINVAL;
goto immediately;
}
subreq = ad_master_domain_send(state, state->ev, conn,
state->sdap_op, domain_name);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ad_master_domain_send failed.\n");
ret = ENOMEM;
goto immediately;
}
tevent_req_set_callback(subreq, ad_gpo_site_name_retrieval_done, req);
ret = EOK;
immediately:
if (ret != EOK) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void
ad_gpo_site_name_retrieval_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_process_som_state *state;
int ret;
char *site;
const char *attrs[] = {AD_AT_CONFIG_NC, NULL};
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_process_som_state);
/* gpo code only cares about the site name */
ret = ad_master_domain_recv(subreq, state, NULL, NULL, &site, NULL);
talloc_zfree(subreq);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Cannot retrieve master domain info\n");
tevent_req_error(req, ENOENT);
return;
}
state->site_name = talloc_asprintf(state, "cn=%s", site);
if (state->site_name == NULL) {
tevent_req_error(req, ENOMEM);
return;
}
/*
* note: the configNC attribute is being retrieved here from the rootDSE
* entry. In future, since we already make an LDAP query for the rootDSE
* entry when LDAP connection is made, this attribute should really be
* retrieved at that point (see https://fedorahosted.org/sssd/ticket/2276)
*/
subreq = sdap_get_generic_send(state, state->ev, state->opts,
sdap_id_op_handle(state->sdap_op),
"", LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, NULL, 0,
state->timeout,
false);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
tevent_req_error(req, ENOMEM);
return;
}
tevent_req_set_callback(subreq, ad_gpo_site_dn_retrieval_done, req);
}
static void
ad_gpo_site_dn_retrieval_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_process_som_state *state;
int ret;
int dp_error;
int i = 0;
size_t reply_count;
struct sysdb_attrs **reply;
const char *configNC;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_process_som_state);
ret = sdap_get_generic_recv(subreq, state,
&reply_count, &reply);
talloc_zfree(subreq);
if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get configNC: [%d](%s)\n", ret, sss_strerror(ret));
ret = ENOENT;
goto done;
}
/* make sure there is only one non-NULL reply returned */
if (reply_count < 1) {
DEBUG(SSSDBG_OP_FAILURE, "No configNC retrieved\n");
ret = ENOENT;
goto done;
} else if (reply_count > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Multiple replies for configNC\n");
ret = ERR_INTERNAL;
goto done;
} else if (reply == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "reply_count is 1, but reply is NULL\n");
ret = ERR_INTERNAL;
goto done;
}
/* reply[0] holds requested attributes of single reply */
ret = sysdb_attrs_get_string(reply[0], AD_AT_CONFIG_NC, &configNC);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
state->site_dn =
talloc_asprintf(state, "%s,cn=Sites,%s", state->site_name, configNC);
if (state->site_dn == NULL) {
ret = ENOMEM;
goto done;
}
/* note that space was allocated for site_dn when allocating som_list */
state->som_list[state->num_soms] =
talloc_zero(state->som_list, struct gp_som);
if (state->som_list[state->num_soms] == NULL) {
ret = ENOMEM;
goto done;
}
state->som_list[state->num_soms]->som_dn =
talloc_steal(state->som_list[state->num_soms], state->site_dn);
if (state->som_list[state->num_soms]->som_dn == NULL) {
ret = ENOMEM;
goto done;
}
state->num_soms++;
state->som_list[state->num_soms] = NULL;
i = 0;
while (state->som_list[i]) {
DEBUG(SSSDBG_TRACE_FUNC, "som_list[%d]->som_dn is %s\n", i,
state->som_list[i]->som_dn);
i++;
}
ret = ad_gpo_get_som_attrs_step(req);
done:
if (ret == EOK) {
tevent_req_done(req);
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
}
}
static errno_t
ad_gpo_get_som_attrs_step(struct tevent_req *req)
{
const char *attrs[] = {AD_AT_GPLINK, AD_AT_GPOPTIONS, NULL};
struct tevent_req *subreq;
struct ad_gpo_process_som_state *state;
state = tevent_req_data(req, struct ad_gpo_process_som_state);
struct gp_som *gp_som = state->som_list[state->som_index];
/* gp_som is NULL only after all SOMs have been processed */
if (gp_som == NULL) return EOK;
const char *som_dn = gp_som->som_dn;
subreq = sdap_get_generic_send(state, state->ev, state->opts,
sdap_id_op_handle(state->sdap_op),
som_dn, LDAP_SCOPE_BASE,
"(objectclass=*)", attrs, NULL, 0,
state->timeout,
false);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
}
tevent_req_set_callback(subreq, ad_gpo_get_som_attrs_done, req);
return EAGAIN;
}
static void
ad_gpo_get_som_attrs_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_process_som_state *state;
int ret;
int dp_error;
size_t num_results;
struct sysdb_attrs **results;
struct ldb_message_element *el = NULL;
uint8_t *raw_gplink_value;
uint8_t *raw_gpoptions_value;
uint32_t allow_enforced_only = 0;
struct gp_som *gp_som;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_process_som_state);
ret = sdap_get_generic_recv(subreq, state,
&num_results, &results);
talloc_zfree(subreq);
if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get SOM attributes: [%d](%s)\n",
ret, sss_strerror(ret));
ret = ENOENT;
goto done;
}
if ((num_results < 1) || (results == NULL)) {
DEBUG(SSSDBG_OP_FAILURE, "no attrs found for SOM; try next SOM.\n");
state->som_index++;
ret = ad_gpo_get_som_attrs_step(req);
goto done;
} else if (num_results > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
ret = ERR_INTERNAL;
goto done;
}
/* Get the gplink value, if available */
ret = sysdb_attrs_get_el(results[0], AD_AT_GPLINK, &el);
if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_el() failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_OP_FAILURE, "no attrs found for SOM; try next SOM\n");
state->som_index++;
ret = ad_gpo_get_som_attrs_step(req);
goto done;
}
raw_gplink_value = el[0].values[0].data;
ret = sysdb_attrs_get_el(results[0], AD_AT_GPOPTIONS, &el);
if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
goto done;
}
if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_TRACE_ALL,
"gpoptions attr not found or has no value; defaults to 0\n");
allow_enforced_only = 0;
} else {
raw_gpoptions_value = el[0].values[0].data;
allow_enforced_only = strtouint32((char *)raw_gpoptions_value, NULL, 10);
if (errno != 0) {
ret = errno;
DEBUG(SSSDBG_OP_FAILURE,
"strtouint32 failed: [%d](%s)\n", ret, sss_strerror(ret));
goto done;
}
}
gp_som = state->som_list[state->som_index];
ret = ad_gpo_populate_gplink_list(gp_som,
gp_som->som_dn,
(char *)raw_gplink_value,
&gp_som->gplink_list,
state->allow_enforced_only);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ad_gpo_populate_gplink_list() failed\n");
goto done;
}
if (allow_enforced_only) {
state->allow_enforced_only = 1;
}
state->som_index++;
ret = ad_gpo_get_som_attrs_step(req);
done:
if (ret == EOK) {
tevent_req_done(req);
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
}
}
int
ad_gpo_process_som_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_som ***som_list)
{
struct ad_gpo_process_som_state *state =
tevent_req_data(req, struct ad_gpo_process_som_state);
TEVENT_REQ_RETURN_ON_ERROR(req);
*som_list = talloc_steal(mem_ctx, state->som_list);
return EOK;
}
/* == ad_gpo_process_gpo_send/recv helpers ================================= */
/*
* This function examines the gp_gplink objects in each gp_som object specified
* in the input som_list, and populates the _candidate_gpos output parameter's
* gpo_dn fields with prioritized list of GPO DNs. Prioritization ensures that:
* - GPOs linked to an OU will be applied after GPOs linked to a Domain,
* which will be applied after GPOs linked to a Site.
* - multiple GPOs linked to a single SOM are applied in their link order
* (i.e. 1st GPO linked to SOM is applied after 2nd GPO linked to SOM, etc).
* - enforced GPOs are applied after unenforced GPOs.
*
* As such, the _candidate_gpos output's dn fields looks like (in link order):
* [unenforced {Site, Domain, OU}; enforced {Site, Domain, OU}]
*
* Note that in the case of conflicting policy settings, GPOs appearing later
* in the list will trump GPOs appearing earlier in the list.
*/
static errno_t
ad_gpo_populate_candidate_gpos(TALLOC_CTX *mem_ctx,
struct gp_som **som_list,
struct gp_gpo ***_candidate_gpos,
int *_num_candidate_gpos)
{
TALLOC_CTX *tmp_ctx = NULL;
struct gp_som *gp_som = NULL;
struct gp_gplink *gp_gplink = NULL;
struct gp_gpo **candidate_gpos = NULL;
int num_candidate_gpos = 0;
const char **enforced_gpo_dns = NULL;
const char **unenforced_gpo_dns = NULL;
int gpo_dn_idx = 0;
int num_enforced = 0;
int enforced_idx = 0;
int num_unenforced = 0;
int unenforced_idx = 0;
int i = 0;
int j = 0;
int ret;
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
}
while (som_list[i]) {
gp_som = som_list[i];
j = 0;
while (gp_som && gp_som->gplink_list && gp_som->gplink_list[j]) {
gp_gplink = gp_som->gplink_list[j];
if (gp_gplink == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "unexpected null gp_gplink\n");
ret = EINVAL;
goto done;
}
if (gp_gplink->enforced) {
num_enforced++;
} else {
num_unenforced++;
}
j++;
}
i++;
}
num_candidate_gpos = num_enforced + num_unenforced;
if (num_candidate_gpos == 0) {
*_candidate_gpos = NULL;
*_num_candidate_gpos = 0;
ret = EOK;
goto done;
}
enforced_gpo_dns = talloc_array(tmp_ctx, const char *, num_enforced + 1);
if (enforced_gpo_dns == NULL) {
ret = ENOMEM;
goto done;
}
unenforced_gpo_dns = talloc_array(tmp_ctx, const char *, num_unenforced + 1);
if (unenforced_gpo_dns == NULL) {
ret = ENOMEM;
goto done;
}
i = 0;
while (som_list[i]) {
gp_som = som_list[i];
j = 0;
while (gp_som && gp_som->gplink_list && gp_som->gplink_list[j]) {
gp_gplink = gp_som->gplink_list[j];
if (gp_gplink == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "unexpected null gp_gplink\n");
ret = EINVAL;
goto done;
}
if (gp_gplink->enforced) {
enforced_gpo_dns[enforced_idx] =
talloc_steal(enforced_gpo_dns, gp_gplink->gpo_dn);
if (enforced_gpo_dns[enforced_idx] == NULL) {
ret = ENOMEM;
goto done;
}
enforced_idx++;
} else {
unenforced_gpo_dns[unenforced_idx] =
talloc_steal(unenforced_gpo_dns, gp_gplink->gpo_dn);
if (unenforced_gpo_dns[unenforced_idx] == NULL) {
ret = ENOMEM;
goto done;
}
unenforced_idx++;
}
j++;
}
i++;
}
enforced_gpo_dns[num_enforced] = NULL;
unenforced_gpo_dns[num_unenforced] = NULL;
candidate_gpos = talloc_array(tmp_ctx,
struct gp_gpo *,
num_candidate_gpos + 1);
if (candidate_gpos == NULL) {
ret = ENOMEM;
goto done;
}
gpo_dn_idx = 0;
for (i = num_unenforced - 1; i >= 0; i--) {
candidate_gpos[gpo_dn_idx] = talloc_zero(candidate_gpos, struct gp_gpo);
if (candidate_gpos[gpo_dn_idx] == NULL) {
ret = ENOMEM;
goto done;
}
candidate_gpos[gpo_dn_idx]->gpo_dn =
talloc_steal(candidate_gpos[gpo_dn_idx], unenforced_gpo_dns[i]);
if (candidate_gpos[gpo_dn_idx]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC,
"candidate_gpos[%d]->gpo_dn: %s\n",
gpo_dn_idx, candidate_gpos[gpo_dn_idx]->gpo_dn);
gpo_dn_idx++;
}
for (i = 0; i < num_enforced; i++) {
candidate_gpos[gpo_dn_idx] = talloc_zero(candidate_gpos, struct gp_gpo);
if (candidate_gpos[gpo_dn_idx] == NULL) {
ret = ENOMEM;
goto done;
}
candidate_gpos[gpo_dn_idx]->gpo_dn =
talloc_steal(candidate_gpos[gpo_dn_idx], enforced_gpo_dns[i]);
if (candidate_gpos[gpo_dn_idx]->gpo_dn == NULL) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_FUNC,
"candidate_gpos[%d]->gpo_dn: %s\n",
gpo_dn_idx, candidate_gpos[gpo_dn_idx]->gpo_dn);
gpo_dn_idx++;
}
candidate_gpos[gpo_dn_idx] = NULL;
*_candidate_gpos = talloc_steal(mem_ctx, candidate_gpos);
*_num_candidate_gpos = num_candidate_gpos;
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
/*
* This function parses the input_path into its components, replaces each
* back slash ('\') with a forward slash ('/'), and populates the output params.
*
* The smb_server output is constructed by concatenating the following elements:
* - SMB_STANDARD_URI ("smb://")
* - server_hostname (which replaces domain_name in input path)
* The smb_share and smb_path outputs are extracted from the input_path.
*
* Example: if input_path = "\\foo.com\SysVol\foo.com\..." and
* server_hostname = "adserver.foo.com", then
* _smb_server = "smb://adserver.foo.com"
* _smb_share = "SysVol"
* _smb_path = "/foo.com/..."
*
* Note that the input_path must have at least four forward slash separators.
* For example, input_path = "\\foo.com\SysVol" is not a valid input_path,
* because it has only three forward slash separators.
*/
static errno_t
ad_gpo_extract_smb_components(TALLOC_CTX *mem_ctx,
char *server_hostname,
char *input_path,
const char **_smb_server,
const char **_smb_share,
const char **_smb_path)
{
char *ptr;
const char delim = '\\';
int ret;
int num_seps = 0;
char *smb_path = NULL;
char *smb_share = NULL;
DEBUG(SSSDBG_TRACE_ALL, "input_path: %s\n", input_path);
if (input_path == NULL ||
*input_path == '\0' ||
_smb_server == NULL ||
_smb_share == NULL ||
_smb_path == NULL) {
ret = EINVAL;
goto done;
}
ptr = input_path;
while ((ptr = strchr(ptr, delim))) {
num_seps++;
if (num_seps == 3) {
/* replace the slash before the share name with null string */
*ptr = '\0';
ptr++;
smb_share = ptr;
continue;
} else if (num_seps == 4) {
/* replace the slash after the share name with null string */
*ptr = '\0';
ptr++;
smb_path = ptr;
continue;
}
*ptr = '/';
ptr++;
}
if (num_seps == 0) {
ret = EINVAL;
goto done;
}
if (smb_path == NULL) {
ret = EINVAL;
goto done;
}
*_smb_server = talloc_asprintf(mem_ctx, "%s%s",
SMB_STANDARD_URI,
server_hostname);
if (*_smb_server == NULL) {
ret = ENOMEM;
goto done;
}
*_smb_share = talloc_asprintf(mem_ctx, "/%s", smb_share);
if (*_smb_share == NULL) {
ret = ENOMEM;
goto done;
}
*_smb_path = talloc_asprintf(mem_ctx, "/%s", smb_path);
if (*_smb_path == NULL) {
ret = ENOMEM;
goto done;
}
ret = EOK;
done:
return ret;
}
/*
* This function populates the _cse_guid_list output parameter by parsing the
* input raw_machine_ext_names_value into an array of cse_guid strings.
*
* The raw_machine_ext_names_value is a single string in the following format:
* "[{cse_guid_1}{tool_guid1}]...[{cse_guid_n}{tool_guid_n}]"
*/
static errno_t
ad_gpo_parse_machine_ext_names(TALLOC_CTX *mem_ctx,
char *raw_machine_ext_names_value,
const char ***_gpo_cse_guids,
int *_num_gpo_cse_guids)
{
TALLOC_CTX *tmp_ctx = NULL;
char *ptr;
char *first;
char *last;
char *cse_guid;
char *tool_guid;
const char delim = ']';
const char **gpo_cse_guids;
int i;
int ret;
int num_gpo_cse_guids = 0;
if (raw_machine_ext_names_value == NULL ||
*raw_machine_ext_names_value == '\0' ||
_gpo_cse_guids == NULL) {
return EINVAL;
}
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
ret = ENOMEM;
goto done;
}
ptr = raw_machine_ext_names_value;
while ((ptr = strchr(ptr, delim))) {
ptr++;
num_gpo_cse_guids++;
}
if (num_gpo_cse_guids == 0) {
ret = EINVAL;
goto done;
}
gpo_cse_guids = talloc_array(tmp_ctx, const char *, num_gpo_cse_guids + 1);
if (gpo_cse_guids == NULL) {
ret = ENOMEM;
goto done;
}
ptr = raw_machine_ext_names_value;
for (i = 0; i < num_gpo_cse_guids; i++) {
first = ptr + 1;
last = strchr(first, delim);
if (last == NULL) {
break;
}
*last = '\0';
last++;
cse_guid = first;
first ++;
tool_guid = strchr(first, '{');
if (tool_guid == NULL) {
break;
}
*tool_guid = '\0';
gpo_cse_guids[i] = talloc_strdup(gpo_cse_guids, cse_guid);
ptr = last;
}
gpo_cse_guids[i] = NULL;
DEBUG(SSSDBG_TRACE_ALL, "num_gpo_cse_guids: %d\n", num_gpo_cse_guids);
for (i = 0; i < num_gpo_cse_guids; i++) {
DEBUG(SSSDBG_TRACE_ALL,
"gpo_cse_guids[%d] is %s\n", i, gpo_cse_guids[i]);
}
*_gpo_cse_guids = talloc_steal(mem_ctx, gpo_cse_guids);
*_num_gpo_cse_guids = num_gpo_cse_guids;
ret = EOK;
done:
talloc_free(tmp_ctx);
return ret;
}
enum ndr_err_code
ad_gpo_ndr_pull_security_descriptor(struct ndr_pull *ndr, int ndr_flags,
struct security_descriptor *r);
/*
* This function parses the input data blob and assigns the resulting
* security_descriptor object to the _gpo_sd output parameter.
*/
static errno_t ad_gpo_parse_sd(TALLOC_CTX *mem_ctx,
uint8_t *data,
size_t length,
struct security_descriptor **_gpo_sd)
{
struct ndr_pull *ndr_pull = NULL;
struct security_descriptor sd;
DATA_BLOB blob;
enum ndr_err_code ndr_err;
blob.data = data;
blob.length = length;
ndr_pull = ndr_pull_init_blob(&blob, mem_ctx);
if (ndr_pull == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "ndr_pull_init_blob() failed.\n");
return EINVAL;
}
ndr_err = ad_gpo_ndr_pull_security_descriptor(ndr_pull,
NDR_SCALARS|NDR_BUFFERS,
&sd);
if (ndr_err != NDR_ERR_SUCCESS) {
DEBUG(SSSDBG_CRIT_FAILURE, "Failed to pull security descriptor\n");
return EINVAL;
}
*_gpo_sd = talloc_memdup(mem_ctx, &sd, sizeof(struct security_descriptor));
return EOK;
}
/* == ad_gpo_process_gpo_send/recv implementation ========================== */
struct ad_gpo_process_gpo_state {
struct tevent_context *ev;
struct sdap_id_op *sdap_op;
struct sdap_options *opts;
char *server_hostname;
int timeout;
struct gp_gpo **candidate_gpos;
int num_candidate_gpos;
int gpo_index;
};
static errno_t ad_gpo_get_gpo_attrs_step(struct tevent_req *req);
static void ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq);
/*
* This function uses the input som_list to populate a prioritized list of
* gp_gpo objects, prioritized based on SOM type, link order, and whether the
* GPO is "enforced". This list represents the initial set of candidate GPOs
* that might be applicable to the target. This list can not be expanded, but
* it might be reduced based on subsequent filtering steps. The GPO object DNs
* are used to retrieve certain LDAP attributes of each GPO object, that are
* parsed into the various fields of the gp_gpo object.
*/
struct tevent_req *
ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sdap_id_op *sdap_op,
struct sdap_options *opts,
char *server_hostname,
int timeout,
struct gp_som **som_list)
{
struct tevent_req *req;
struct ad_gpo_process_gpo_state *state;
errno_t ret;
req = tevent_req_create(mem_ctx, &state, struct ad_gpo_process_gpo_state);
if (req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
return NULL;
}
state->ev = ev;
state->sdap_op = sdap_op;
state->opts = opts;
state->server_hostname = server_hostname;
state->timeout = timeout;
state->gpo_index = 0;
state->candidate_gpos = NULL;
state->num_candidate_gpos = 0;
ret = ad_gpo_populate_candidate_gpos(state,
som_list,
&state->candidate_gpos,
&state->num_candidate_gpos);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Unable to retrieve GPO List: [%d](%s)\n",
ret, sss_strerror(ret));
ret = ENOENT;
goto immediately;
}
if (state->candidate_gpos == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "no gpos found\n");
ret = ENOENT;
goto immediately;
}
ret = ad_gpo_get_gpo_attrs_step(req);
immediately:
if (ret == EOK) {
tevent_req_done(req);
tevent_req_post(req, ev);
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static errno_t
ad_gpo_get_gpo_attrs_step(struct tevent_req *req)
{
const char *attrs[] = {AD_AT_NT_SEC_DESC, AD_AT_CN, AD_AT_FILE_SYS_PATH,
AD_AT_MACHINE_EXT_NAMES, AD_AT_FUNC_VERSION,
AD_AT_FLAGS, NULL};
struct tevent_req *subreq;
struct ad_gpo_process_gpo_state *state;
state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
struct gp_gpo *gp_gpo = state->candidate_gpos[state->gpo_index];
/* gp_gpo is NULL only after all GPOs have been processed */
if (gp_gpo == NULL) return EOK;
const char *gpo_dn = gp_gpo->gpo_dn;
subreq = sdap_sd_search_send(state, state->ev,
state->opts, sdap_id_op_handle(state->sdap_op),
gpo_dn, SECINFO_DACL, attrs, state->timeout);
if (subreq == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "sdap_get_generic_send failed.\n");
return ENOMEM;
}
tevent_req_set_callback(subreq, ad_gpo_get_gpo_attrs_done, req);
return EAGAIN;
}
static void
ad_gpo_get_gpo_attrs_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_process_gpo_state *state;
int ret;
int dp_error;
size_t num_results;
struct sysdb_attrs **results;
struct ldb_message_element *el = NULL;
const char *gpo_guid = NULL;
const char *raw_file_sys_path = NULL;
char *file_sys_path = NULL;
uint8_t *raw_machine_ext_names = NULL;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_process_gpo_state);
ret = sdap_sd_search_recv(subreq, state, &num_results, &results);
talloc_zfree(subreq);
if (ret != EOK) {
ret = sdap_id_op_done(state->sdap_op, ret, &dp_error);
DEBUG(SSSDBG_OP_FAILURE,
"Unable to get GPO attributes: [%d](%s)\n",
ret, sss_strerror(ret));
ret = ENOENT;
goto done;
}
if ((num_results < 1) || (results == NULL)) {
DEBUG(SSSDBG_OP_FAILURE, "no attrs found for GPO; try next GPO.\n");
state->gpo_index++;
ret = ad_gpo_get_gpo_attrs_step(req);
goto done;
}
else if (num_results > 1) {
DEBUG(SSSDBG_OP_FAILURE, "Received multiple replies\n");
ret = ERR_INTERNAL;
goto done;
}
struct gp_gpo *gp_gpo = state->candidate_gpos[state->gpo_index];
/* retrieve AD_AT_CN */
ret = sysdb_attrs_get_string(results[0], AD_AT_CN, &gpo_guid);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
gp_gpo->gpo_guid = talloc_steal(gp_gpo, gpo_guid);
if (gp_gpo->gpo_guid == NULL) {
ret = ENOMEM;
goto done;
}
DEBUG(SSSDBG_TRACE_ALL, "populating attrs for gpo_guid: %s\n",
gp_gpo->gpo_guid);
/* retrieve AD_AT_FILE_SYS_PATH */
ret = sysdb_attrs_get_string(results[0],
AD_AT_FILE_SYS_PATH,
&raw_file_sys_path);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_string failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
file_sys_path = talloc_strdup(gp_gpo, raw_file_sys_path);
ret = ad_gpo_extract_smb_components(gp_gpo, state->server_hostname,
file_sys_path, &gp_gpo->smb_server,
&gp_gpo->smb_share, &gp_gpo->smb_path);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"unable to extract smb components from file_sys_path: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
DEBUG(SSSDBG_TRACE_ALL, "smb_server: %s\n", gp_gpo->smb_server);
DEBUG(SSSDBG_TRACE_ALL, "smb_share: %s\n", gp_gpo->smb_share);
DEBUG(SSSDBG_TRACE_ALL, "smb_path: %s\n", gp_gpo->smb_path);
/* retrieve AD_AT_FUNC_VERSION */
ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FUNC_VERSION,
&gp_gpo->gpo_func_version);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_int32_t failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
DEBUG(SSSDBG_TRACE_ALL, "gpo_func_version: %d\n",
gp_gpo->gpo_func_version);
/* retrieve AD_AT_FLAGS */
ret = sysdb_attrs_get_int32_t(results[0], AD_AT_FLAGS,
&gp_gpo->gpo_flags);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"sysdb_attrs_get_int32_t failed: [%d](%s)\n",
ret, sss_strerror(ret));
goto done;
}
DEBUG(SSSDBG_TRACE_ALL, "gpo_flags: %d\n", gp_gpo->gpo_flags);
/* retrieve AD_AT_NT_SEC_DESC */
ret = sysdb_attrs_get_el(results[0], AD_AT_NT_SEC_DESC, &el);
if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
goto done;
}
if ((ret == ENOENT) || (el->num_values == 0)) {
DEBUG(SSSDBG_OP_FAILURE,
"nt_sec_desc attribute not found or has no value\n");
ret = ENOENT;
goto done;
}
ret = ad_gpo_parse_sd(gp_gpo, el[0].values[0].data, el[0].values[0].length,
&gp_gpo->gpo_sd);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "ad_gpo_parse_sd() failed\n");
goto done;
}
/* retrieve AD_AT_MACHINE_EXT_NAMES */
ret = sysdb_attrs_get_el(results[0], AD_AT_MACHINE_EXT_NAMES, &el);
if (ret != EOK && ret != ENOENT) {
DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_get_el() failed\n");
goto done;
}
if ((ret == ENOENT) || (el->num_values == 0)) {
/*
* if gpo has no machine_ext_names (which is perfectly valid: it could
* have only user_ext_names, for example), we continue to next gpo
*/
DEBUG(SSSDBG_TRACE_ALL,
"machine_ext_names attribute not found or has no value\n");
state->gpo_index++;
} else {
raw_machine_ext_names = el[0].values[0].data;
ret = ad_gpo_parse_machine_ext_names(gp_gpo,
(char *)raw_machine_ext_names,
&gp_gpo->gpo_cse_guids,
&gp_gpo->num_gpo_cse_guids);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"ad_gpo_parse_machine_ext_names() failed\n");
goto done;
}
state->gpo_index++;
}
ret = ad_gpo_get_gpo_attrs_step(req);
done:
if (ret == EOK) {
tevent_req_done(req);
} else if (ret != EAGAIN) {
tevent_req_error(req, ret);
}
}
int
ad_gpo_process_gpo_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx,
struct gp_gpo ***candidate_gpos,
int *num_candidate_gpos)
{
struct ad_gpo_process_gpo_state *state =
tevent_req_data(req, struct ad_gpo_process_gpo_state);
TEVENT_REQ_RETURN_ON_ERROR(req);
*candidate_gpos = talloc_steal(mem_ctx, state->candidate_gpos);
*num_candidate_gpos = state->num_candidate_gpos;
return EOK;
}
/* == ad_gpo_process_cse_send/recv helpers ================================= */
static errno_t
create_cse_send_buffer(TALLOC_CTX *mem_ctx,
const char *smb_server,
const char *smb_share,
const char *smb_path,
const char *smb_cse_suffix,
int cached_gpt_version,
struct io_buffer **io_buf)
{
struct io_buffer *buf;
size_t rp;
int smb_server_length;
int smb_share_length;
int smb_path_length;
int smb_cse_suffix_length;
smb_server_length = strlen(smb_server);
smb_share_length = strlen(smb_share);
smb_path_length = strlen(smb_path);
smb_cse_suffix_length = strlen(smb_cse_suffix);
buf = talloc(mem_ctx, struct io_buffer);
if (buf == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
return ENOMEM;
}
buf->size = 5 * sizeof(uint32_t);
buf->size += smb_server_length + smb_share_length + smb_path_length +
smb_cse_suffix_length;
DEBUG(SSSDBG_TRACE_ALL, "buffer size: %zu\n", buf->size);
buf->data = talloc_size(buf, buf->size);
if (buf->data == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
talloc_free(buf);
return ENOMEM;
}
rp = 0;
/* cached_gpt_version */
SAFEALIGN_SET_UINT32(&buf->data[rp], cached_gpt_version, &rp);
/* smb_server */
SAFEALIGN_SET_UINT32(&buf->data[rp], smb_server_length, &rp);
safealign_memcpy(&buf->data[rp], smb_server, smb_server_length, &rp);
/* smb_share */
SAFEALIGN_SET_UINT32(&buf->data[rp], smb_share_length, &rp);
safealign_memcpy(&buf->data[rp], smb_share, smb_share_length, &rp);
/* smb_path */
SAFEALIGN_SET_UINT32(&buf->data[rp], smb_path_length, &rp);
safealign_memcpy(&buf->data[rp], smb_path, smb_path_length, &rp);
/* smb_cse_suffix */
SAFEALIGN_SET_UINT32(&buf->data[rp], smb_cse_suffix_length, &rp);
safealign_memcpy(&buf->data[rp], smb_cse_suffix, smb_cse_suffix_length, &rp);
*io_buf = buf;
return EOK;
}
static errno_t
ad_gpo_parse_gpo_child_response(uint8_t *buf,
ssize_t size,
uint32_t *_sysvol_gpt_version,
uint32_t *_result)
{
int ret;
size_t p = 0;
uint32_t sysvol_gpt_version;
uint32_t result;
/* sysvol_gpt_version */
SAFEALIGN_COPY_UINT32_CHECK(&sysvol_gpt_version, buf + p, size, &p);
/* operation result code */
SAFEALIGN_COPY_UINT32_CHECK(&result, buf + p, size, &p);
*_sysvol_gpt_version = sysvol_gpt_version;
*_result = result;
ret = EOK;
return ret;
}
/* == ad_gpo_process_cse_send/recv implementation ========================== */
struct ad_gpo_process_cse_state {
struct tevent_context *ev;
struct sss_domain_info *domain;
int gpo_timeout_option;
const char *gpo_guid;
const char *smb_path;
const char *smb_cse_suffix;
pid_t child_pid;
uint8_t *buf;
ssize_t len;
struct child_io_fds *io;
};
static errno_t gpo_fork_child(struct tevent_req *req);
static void gpo_cse_step(struct tevent_req *subreq);
static void gpo_cse_done(struct tevent_req *subreq);
/*
* This cse-specific function (GP_EXT_GUID_SECURITY) sends the input smb uri
* components and cached_gpt_version to the gpo child, which, in turn,
* will download the GPT.INI file and policy files (as needed) and store
* them in the GPO_CACHE directory. Note that if the send_to_child input is
* false, this function simply completes the request.
*/
struct tevent_req *
ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
bool send_to_child,
struct sss_domain_info *domain,
const char *gpo_guid,
const char *smb_server,
const char *smb_share,
const char *smb_path,
const char *smb_cse_suffix,
int cached_gpt_version,
int gpo_timeout_option)
{
struct tevent_req *req;
struct tevent_req *subreq;
struct ad_gpo_process_cse_state *state;
struct io_buffer *buf = NULL;
errno_t ret;
req = tevent_req_create(mem_ctx, &state, struct ad_gpo_process_cse_state);
if (req == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
return NULL;
}
if (!send_to_child) {
/*
* if we don't need to talk to child (b/c cache timeout is still valid),
* we simply complete the request
*/
ret = EOK;
goto immediately;
}
state->ev = ev;
state->buf = NULL;
state->len = 0;
state->domain = domain;
state->gpo_timeout_option = gpo_timeout_option;
state->gpo_guid = gpo_guid;
state->smb_path = smb_path;
state->smb_cse_suffix = smb_cse_suffix;
state->io = talloc(state, struct child_io_fds);
if (state->io == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
ret = ENOMEM;
goto immediately;
}
state->io->write_to_child_fd = -1;
state->io->read_from_child_fd = -1;
talloc_set_destructor((void *) state->io, child_io_destructor);
/* prepare the data to pass to child */
ret = create_cse_send_buffer(state, smb_server, smb_share, smb_path,
smb_cse_suffix, cached_gpt_version, &buf);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "create_cse_send_buffer failed.\n");
goto immediately;
}
ret = gpo_fork_child(req);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "gpo_fork_child failed.\n");
goto immediately;
}
subreq = write_pipe_send(state, ev, buf->data, buf->size,
state->io->write_to_child_fd);
if (subreq == NULL) {
ret = ENOMEM;
goto immediately;
}
tevent_req_set_callback(subreq, gpo_cse_step, req);
return req;
immediately:
if (ret == EOK) {
tevent_req_done(req);
tevent_req_post(req, ev);
} else {
tevent_req_error(req, ret);
tevent_req_post(req, ev);
}
return req;
}
static void gpo_cse_step(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_process_cse_state *state;
int ret;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_process_cse_state);
ret = write_pipe_recv(subreq);
talloc_zfree(subreq);
if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
close(state->io->write_to_child_fd);
state->io->write_to_child_fd = -1;
subreq = read_pipe_send(state, state->ev, state->io->read_from_child_fd);
if (subreq == NULL) {
tevent_req_error(req, ENOMEM);
return;
}
tevent_req_set_callback(subreq, gpo_cse_done, req);
}
static void gpo_cse_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct ad_gpo_process_cse_state *state;
uint32_t sysvol_gpt_version = -1;
uint32_t child_result;
time_t now;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct ad_gpo_process_cse_state);
int ret;
ret = read_pipe_recv(subreq, state, &state->buf, &state->len);
talloc_zfree(subreq);
if (ret != EOK) {
tevent_req_error(req, ret);
return;
}
close(state->io->read_from_child_fd);
state->io->read_from_child_fd = -1;
ret = ad_gpo_parse_gpo_child_response(state->buf, state->len,
&sysvol_gpt_version, &child_result);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"ad_gpo_parse_gpo_child_response failed: [%d][%s]\n",
ret, strerror(ret));
tevent_req_error(req, ret);
return;
} else if (child_result != 0){
DEBUG(SSSDBG_CRIT_FAILURE,
"Error in gpo_child: [%d][%s]\n",
child_result, strerror(child_result));
tevent_req_error(req, child_result);
return;
}
now = time(NULL);
DEBUG(SSSDBG_TRACE_FUNC, "sysvol_gpt_version: %d\n", sysvol_gpt_version);
ret = sysdb_gpo_store_gpo(state->domain, state->gpo_guid, sysvol_gpt_version,
state->gpo_timeout_option, now);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Unable to store gpo cache entry: [%d](%s}\n",
ret, sss_strerror(ret));
tevent_req_error(req, ret);
return;
}
tevent_req_done(req);
return;
}
int ad_gpo_process_cse_recv(struct tevent_req *req)
{
TEVENT_REQ_RETURN_ON_ERROR(req);
return EOK;
}
static errno_t
gpo_fork_child(struct tevent_req *req)
{
int pipefd_to_child[2];
int pipefd_from_child[2];
pid_t pid;
int ret;
errno_t err;
struct ad_gpo_process_cse_state *state;
state = tevent_req_data(req, struct ad_gpo_process_cse_state);
ret = pipe(pipefd_from_child);
if (ret == -1) {
err = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
"pipe failed [%d][%s].\n", errno, strerror(errno));
return err;
}
ret = pipe(pipefd_to_child);
if (ret == -1) {
err = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
"pipe failed [%d][%s].\n", errno, strerror(errno));
return err;
}
pid = fork();
if (pid == 0) { /* child */
err = exec_child_ex(state,
pipefd_to_child, pipefd_from_child,
GPO_CHILD, gpo_child_debug_fd, NULL,
STDIN_FILENO, AD_GPO_CHILD_OUT_FILENO);
DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec gpo_child: [%d][%s].\n",
err, strerror(err));
return err;
} else if (pid > 0) { /* parent */
state->child_pid = pid;
state->io->read_from_child_fd = pipefd_from_child[0];
close(pipefd_from_child[1]);
state->io->write_to_child_fd = pipefd_to_child[1];
close(pipefd_to_child[0]);
sss_fd_nonblocking(state->io->read_from_child_fd);
sss_fd_nonblocking(state->io->write_to_child_fd);
ret = child_handler_setup(state->ev, pid, NULL, NULL, NULL);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Could not set up child signal handler\n");
return ret;
}
} else { /* error */
err = errno;
DEBUG(SSSDBG_CRIT_FAILURE,
"fork failed [%d][%s].\n", errno, strerror(errno));
return err;
}
return EOK;
}