ad_gpo.c revision 03e5f1528184a558fd990e66f083157b404dce08
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder/*
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder SSSD
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder Authors:
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder Yassir Elley <yelley@redhat.com>
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder Copyright (C) 2013 Red Hat
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder This program is free software; you can redistribute it and/or modify
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder it under the terms of the GNU General Public License as published by
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder the Free Software Foundation; either version 3 of the License, or
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder (at your option) any later version.
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder This program is distributed in the hope that it will be useful,
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder but WITHOUT ANY WARRANTY; without even the implied warranty of
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder GNU General Public License for more details.
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder You should have received a copy of the GNU General Public License
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder along with this program. If not, see <http://www.gnu.org/licenses/>.
e84c877ad38ce9312eab222a79f44da2015572d2Christian Maeder*/
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder/*
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder * This file implements the following pair of *public* functions (see header):
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder * ad_gpo_access_send/recv: provides client-side GPO processing
945e82ed7877917f3ab1657f555e71991372546aChristian Maeder *
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder * This file also implements the following pairs of *private* functions (which
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder * are used by the public functions):
945e82ed7877917f3ab1657f555e71991372546aChristian Maeder * ad_gpo_process_som_send/recv: populate list of gp_som objects
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder * ad_gpo_process_gpo_send/recv: populate list of gp_gpo objects
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder * ad_gpo_process_cse_send/recv: retrieve policy file data
d97700a22b2585ece83b05f3fff945fdfd0c44b4Christian Maeder */
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#include <security/pam_modules.h>
fd94a6f66ccb5cef99aa42069b61e4b8734dbd3fChristian Maeder#include <syslog.h>
fd94a6f66ccb5cef99aa42069b61e4b8734dbd3fChristian Maeder#include <fcntl.h>
fd94a6f66ccb5cef99aa42069b61e4b8734dbd3fChristian Maeder#include <ini_configobj.h>
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#include "util/util.h"
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#include "util/strtonum.h"
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder#include "util/child_common.h"
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder#include "providers/data_provider.h"
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder#include "providers/dp_backend.h"
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder#include "providers/ad/ad_access.h"
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder#include "providers/ad/ad_common.h"
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder#include "providers/ad/ad_domain_info.h"
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder#include "providers/ad/ad_gpo.h"
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#include "providers/ldap/sdap_access.h"
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#include "providers/ldap/sdap_async.h"
945e82ed7877917f3ab1657f555e71991372546aChristian Maeder#include "providers/ldap/sdap.h"
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder#include "providers/ldap/sdap_idmap.h"
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#include "util/util_sss_idmap.h"
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#include <ndr.h>
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder#include <gen_ndr/security.h>
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder/* == gpo-ldap constants =================================================== */
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder#define AD_AT_DN "distinguishedName"
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#define AD_AT_UAC "userAccountControl"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder#define AD_AT_CONFIG_NC "configurationNamingContext"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder#define AD_AT_GPLINK "gPLink"
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#define AD_AT_GPOPTIONS "gpOptions"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder#define AD_AT_NT_SEC_DESC "nTSecurityDescriptor"
6a6689ad6d4c70af2ce3389f39a50982f20fd939Christian Maeder#define AD_AT_CN "cn"
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder#define AD_AT_FILE_SYS_PATH "gPCFileSysPath"
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder#define AD_AT_MACHINE_EXT_NAMES "gPCMachineExtensionNames"
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder#define AD_AT_FUNC_VERSION "gPCFunctionalityVersion"
945e82ed7877917f3ab1657f555e71991372546aChristian Maeder#define AD_AT_FLAGS "flags"
6a6689ad6d4c70af2ce3389f39a50982f20fd939Christian Maeder
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#define UAC_WORKSTATION_TRUST_ACCOUNT 0x00001000
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder#define AD_AGP_GUID "edacfd8f-ffb3-11d1-b41d-00a0c968f939"
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#define AD_AUTHENTICATED_USERS_SID "S-1-5-11"
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder/* == gpo-smb constants ==================================================== */
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#define SMB_STANDARD_URI "smb://"
6a6689ad6d4c70af2ce3389f39a50982f20fd939Christian Maeder#define BUFSIZE 65536
6a6689ad6d4c70af2ce3389f39a50982f20fd939Christian Maeder
6a6689ad6d4c70af2ce3389f39a50982f20fd939Christian Maeder#define RIGHTS_SECTION "Privilege Rights"
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder#define ALLOW_LOGON_INTERACTIVE "SeInteractiveLogonRight"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder#define DENY_LOGON_INTERACTIVE "SeDenyInteractiveLogonRight"
6a6689ad6d4c70af2ce3389f39a50982f20fd939Christian Maeder#define ALLOW_LOGON_REMOTE_INTERACTIVE "SeRemoteInteractiveLogonRight"
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder#define DENY_LOGON_REMOTE_INTERACTIVE "SeDenyRemoteInteractiveLogonRight"
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder#define ALLOW_LOGON_NETWORK "SeNetworkLogonRight"
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder#define DENY_LOGON_NETWORK "SeDenyNetworkLogonRight"
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder#define ALLOW_LOGON_BATCH "SeBatchLogonRight"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder#define DENY_LOGON_BATCH "SeDenyBatchLogonRight"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder#define ALLOW_LOGON_SERVICE "SeServiceLogonRight"
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder#define DENY_LOGON_SERVICE "SeDenyServiceLogonRight"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder#define GP_EXT_GUID_SECURITY "{827D319E-6EAC-11D2-A4EA-00C04F79F83A}"
6a6689ad6d4c70af2ce3389f39a50982f20fd939Christian Maeder#define GP_EXT_GUID_SECURITY_SUFFIX "/Machine/Microsoft/Windows NT/SecEdit/GptTmpl.inf"
d27b1887e61f1dc53d77c37f59dbf5019242a686Christian Maeder
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder#ifndef SSSD_LIBEXEC_PATH
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder#error "SSSD_LIBEXEC_PATH not defined"
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder#else
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder#define GPO_CHILD SSSD_LIBEXEC_PATH"/gpo_child"
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder#endif
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder/* fd used by the gpo_child process for logging */
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maederint gpo_child_debug_fd = -1;
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder/* == common data structures and declarations ============================= */
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maederstruct gp_som {
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder const char *som_dn;
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder struct gp_gplink **gplink_list;
e84c877ad38ce9312eab222a79f44da2015572d2Christian Maeder int num_gplinks;
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder};
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maederstruct gp_gplink {
7c661ba558707feaa5d8a299365c2191e1afabb2Christian Maeder const char *gpo_dn;
acd9ca9d8e79b7d7ba8227631afbcf839f0c4a5eChristian Maeder bool enforced;
c1bca76471ad22ac47f05b03576974762d364339Christian Maeder};
acd9ca9d8e79b7d7ba8227631afbcf839f0c4a5eChristian Maeder
c1bca76471ad22ac47f05b03576974762d364339Christian Maederstruct gp_gpo {
c1bca76471ad22ac47f05b03576974762d364339Christian Maeder struct security_descriptor *gpo_sd;
c1bca76471ad22ac47f05b03576974762d364339Christian Maeder const char *gpo_dn;
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder const char *gpo_guid;
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder const char *smb_server;
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder const char *smb_share;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder const char *smb_path;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder const char **gpo_cse_guids;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder int num_gpo_cse_guids;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder int gpo_func_version;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder int gpo_flags;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder bool send_to_child;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder const char *policy_filename;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder};
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maederenum ace_eval_status {
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder AD_GPO_ACE_DENIED,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder AD_GPO_ACE_ALLOWED,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder AD_GPO_ACE_NEUTRAL
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder};
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maederstruct tevent_req *ad_gpo_process_som_send(TALLOC_CTX *mem_ctx,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder struct tevent_context *ev,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder struct sdap_id_conn_ctx *conn,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder struct ldb_context *ldb_ctx,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder struct sdap_id_op *sdap_op,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder struct sdap_options *opts,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder int timeout,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder const char *target_dn,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder const char *domain_name);
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maederint ad_gpo_process_som_recv(struct tevent_req *req,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder TALLOC_CTX *mem_ctx,
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder struct gp_som ***som_list);
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maederstruct tevent_req *ad_gpo_process_gpo_send(TALLOC_CTX *mem_ctx,
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder struct tevent_context *ev,
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder struct sdap_id_op *sdap_op,
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder struct sdap_options *opts,
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder char *server_hostname,
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder int timeout,
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder struct gp_som **som_list);
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maederint ad_gpo_process_gpo_recv(struct tevent_req *req,
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder TALLOC_CTX *mem_ctx,
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder struct gp_gpo ***candidate_gpos,
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder int *num_candidate_gpos);
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maederstruct tevent_req *ad_gpo_process_cse_send(TALLOC_CTX *mem_ctx,
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder struct tevent_context *ev,
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder bool send_to_child,
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder struct sss_domain_info *domain,
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder const char *gpo_guid,
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder const char *smb_server,
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder const char *smb_share,
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder const char *smb_path,
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder const char *smb_cse_suffix,
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder int cached_gpt_version,
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder int gpo_timeout_option);
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maederint ad_gpo_process_cse_recv(struct tevent_req *req);
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder/* == ad_gpo_parse_map_options and helpers ==================================*/
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder#define GPO_LOGIN "login"
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder#define GPO_SU "su"
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder#define GPO_SU_L "su-l"
fd94a6f66ccb5cef99aa42069b61e4b8734dbd3fChristian Maeder#define GPO_GDM_FINGERPRINT "gdm-fingerprint"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder#define GPO_GDM_PASSWORD "gdm-password"
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder#define GPO_GDM_SMARTCARD "gdm-smartcard"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder#define GPO_KDM "kdm"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder#define GPO_SSHD "sshd"
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder#define GPO_FTP "ftp"
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder#define GPO_SAMBA "samba"
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder#define GPO_CROND "crond"
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder#define GPO_SUDO "sudo"
fd94a6f66ccb5cef99aa42069b61e4b8734dbd3fChristian Maeder#define GPO_SUDO_I "sudo-i"
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder#define GPO_SYSTEMD_USER "systemd-user"
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maederstruct gpo_map_option_entry {
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder enum gpo_map_type gpo_map_type;
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder enum ad_basic_opt ad_basic_opt;
4ed68712ee368cbebfeaa327968583cb022e3c72Christian Maeder const char **gpo_map_defaults;
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder const char *allow_key;
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder const char *deny_key;
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder};
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder
d97700a22b2585ece83b05f3fff945fdfd0c44b4Christian Maederconst char *gpo_map_interactive_defaults[] =
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder {GPO_LOGIN, GPO_SU, GPO_SU_L,
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder GPO_GDM_FINGERPRINT, GPO_GDM_PASSWORD, GPO_GDM_SMARTCARD, GPO_KDM, NULL};
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maederconst char *gpo_map_remote_interactive_defaults[] = {GPO_SSHD, NULL};
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maederconst char *gpo_map_network_defaults[] = {GPO_FTP, GPO_SAMBA, NULL};
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maederconst char *gpo_map_batch_defaults[] = {GPO_CROND, NULL};
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maederconst char *gpo_map_service_defaults[] = {NULL};
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maederconst char *gpo_map_permit_defaults[] = {GPO_SUDO, GPO_SUDO_I,
275698320a734a6fd647ea6a461d6ce38862da1dChristian Maeder GPO_SYSTEMD_USER, NULL};
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maederconst char *gpo_map_deny_defaults[] = {NULL};
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maederstruct gpo_map_option_entry gpo_map_option_entries[] = {
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder {GPO_MAP_INTERACTIVE, AD_GPO_MAP_INTERACTIVE, gpo_map_interactive_defaults,
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder ALLOW_LOGON_INTERACTIVE, DENY_LOGON_INTERACTIVE},
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder {GPO_MAP_REMOTE_INTERACTIVE, AD_GPO_MAP_REMOTE_INTERACTIVE,
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder gpo_map_remote_interactive_defaults,
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder ALLOW_LOGON_REMOTE_INTERACTIVE, DENY_LOGON_REMOTE_INTERACTIVE},
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder {GPO_MAP_NETWORK, AD_GPO_MAP_NETWORK, gpo_map_network_defaults,
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder ALLOW_LOGON_NETWORK, DENY_LOGON_NETWORK},
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder {GPO_MAP_BATCH, AD_GPO_MAP_BATCH, gpo_map_batch_defaults,
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder ALLOW_LOGON_BATCH, DENY_LOGON_BATCH},
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder {GPO_MAP_SERVICE, AD_GPO_MAP_SERVICE, gpo_map_service_defaults,
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder ALLOW_LOGON_SERVICE, DENY_LOGON_SERVICE},
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder {GPO_MAP_PERMIT, AD_GPO_MAP_PERMIT, gpo_map_permit_defaults, NULL, NULL},
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder {GPO_MAP_DENY, AD_GPO_MAP_DENY, gpo_map_deny_defaults, NULL, NULL},
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder};
6a6689ad6d4c70af2ce3389f39a50982f20fd939Christian Maeder
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maederconst char* gpo_map_type_string(int gpo_map_type)
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder{
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder switch(gpo_map_type) {
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder case GPO_MAP_INTERACTIVE: return "Interactive";
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder case GPO_MAP_REMOTE_INTERACTIVE: return "Remote Interactive";
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder case GPO_MAP_NETWORK: return "Network";
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder case GPO_MAP_BATCH: return "Batch";
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder case GPO_MAP_SERVICE: return "Service";
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder case GPO_MAP_PERMIT: return "Permitted";
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder case GPO_MAP_DENY: return "Denied";
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder }
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder return NULL;
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder}
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder
a43c1a7fa08c12524415386aa13a566cc9e53a4fChristian Maederstatic inline bool
990cfa5c6abdd4d093cea540c34bb5522bf540d2Christian Maederad_gpo_service_in_list(char **list, size_t nlist, const char *str)
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder{
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder size_t i;
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder for (i = 0; i < nlist; i++) {
b0bf54186358372d2be6a95e36ed3ef5fd64b7a3Christian Maeder if (strcasecmp(list[i], str) == 0) {
7c661ba558707feaa5d8a299365c2191e1afabb2Christian Maeder break;
7c661ba558707feaa5d8a299365c2191e1afabb2Christian Maeder }
7c661ba558707feaa5d8a299365c2191e1afabb2Christian Maeder }
7c661ba558707feaa5d8a299365c2191e1afabb2Christian Maeder
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder return (i < nlist) ? true : false;
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder}
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maedererrno_t
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maederad_gpo_parse_map_option_helper(enum gpo_map_type gpo_map_type,
7c661ba558707feaa5d8a299365c2191e1afabb2Christian Maeder hash_key_t key,
7c661ba558707feaa5d8a299365c2191e1afabb2Christian Maeder hash_table_t *options_table)
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder{
5bedf8c26d27eac08962c78379bcb2e5cb529036Christian Maeder hash_value_t val;
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder int hret;
5896f38ba2934056542cb7cb3e6359e88a622547Christian Maeder int ret;
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder hret = hash_lookup(options_table, &key, &val);
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder if (hret != HASH_SUCCESS && hret != HASH_ERROR_KEY_NOT_FOUND) {
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder DEBUG(SSSDBG_OP_FAILURE, "Error checking hash table: [%s]\n",
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder hash_error_string(hret));
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder ret = EINVAL;
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder goto done;
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder } else if (hret == HASH_SUCCESS) {
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder /* handle unexpected case where mapping for key already exists */
43d8d7472d3a0a78d9a2c85122815a81deb8689aChristian Maeder if (val.i == gpo_map_type) {
43d8d7472d3a0a78d9a2c85122815a81deb8689aChristian Maeder /* mapping for key exists for same map type; no error */
1add44b8c31aa6014f89dbfe743f01a88542c761Christian Maeder DEBUG(SSSDBG_TRACE_FUNC,
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder "PAM service %s maps to %s multiple times\n", key.str,
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder gpo_map_type_string(gpo_map_type));
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder ret = EOK;
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder } else {
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder /* mapping for key exists for different map type; error! */
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder DEBUG(SSSDBG_CRIT_FAILURE,
cdae1f4c0c6e32f5463a7ce9986600e27f8ac253Christian Maeder "PAM service %s maps to both %s and %s\n", key.str,
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder gpo_map_type_string(val.i), gpo_map_type_string(gpo_map_type));
cdae1f4c0c6e32f5463a7ce9986600e27f8ac253Christian Maeder ret = EINVAL;
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder }
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder goto done;
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder } else {
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder /* handle expected case where mapping for key doesn't already exist */
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder val.type = HASH_VALUE_INT;
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder val.i = gpo_map_type;
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder hret = hash_enter(options_table, &key, &val);
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder if (hret != HASH_SUCCESS) {
5727dbcb926ede2e7b05f43685476ce656ce01e4Christian Maeder DEBUG(SSSDBG_OP_FAILURE, "Error checking hash table: [%s]\n",
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder hash_error_string(hret));
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder ret = EIO;
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder goto done;
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder }
18548c6cc2dff13bf9f5f08b3f6cde6ca914df1dChristian Maeder ret = EOK;
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder }
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder
473f5af6e4803fbeecc814065952396f2501039bChristian Maederdone:
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder return ret;
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder}
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maedererrno_t
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maederad_gpo_parse_map_option(TALLOC_CTX *mem_ctx,
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder enum gpo_map_type gpo_map_type,
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder hash_table_t *options_table,
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder char *conf_str,
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder const char **defaults)
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder{
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder TALLOC_CTX *tmp_ctx;
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder errno_t ret;
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder char **conf_list = NULL;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder int conf_list_size = 0;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder char **add_list = NULL;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder char **remove_list = NULL;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder int ai = 0, ri = 0;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder int i;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder hash_key_t key;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder tmp_ctx = talloc_new(NULL);
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder if (tmp_ctx == NULL) {
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder ret = ENOMEM;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder goto done;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder }
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder DEBUG(SSSDBG_TRACE_ALL, "gpo_map_type: %s\n",
569b102de9c99536086f4c2ec60a5599604d9e8aChristian Maeder gpo_map_type_string(gpo_map_type));
569b102de9c99536086f4c2ec60a5599604d9e8aChristian Maeder
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder if (conf_str) {
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder ret = split_on_separator(tmp_ctx, conf_str, ',', true, true,
569b102de9c99536086f4c2ec60a5599604d9e8aChristian Maeder &conf_list, &conf_list_size);
8f021fe6b83359551eaf6ff82d02fe8c3d7fed5dChristian Maeder if (ret != EOK) {
569b102de9c99536086f4c2ec60a5599604d9e8aChristian Maeder DEBUG(SSSDBG_OP_FAILURE,
569b102de9c99536086f4c2ec60a5599604d9e8aChristian Maeder "Cannot parse list of service names %s: %d\n", conf_str, ret);
569b102de9c99536086f4c2ec60a5599604d9e8aChristian Maeder ret = EINVAL;
569b102de9c99536086f4c2ec60a5599604d9e8aChristian Maeder goto done;
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder }
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder
569b102de9c99536086f4c2ec60a5599604d9e8aChristian Maeder add_list = talloc_zero_array(tmp_ctx, char *, conf_list_size);
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder remove_list = talloc_zero_array(tmp_ctx, char *, conf_list_size);
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder if (add_list == NULL || remove_list == NULL) {
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder ret = ENOMEM;
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder goto done;
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder }
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder }
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder for (i = 0; i < conf_list_size; i++) {
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder switch (conf_list[i][0]) {
473f5af6e4803fbeecc814065952396f2501039bChristian Maeder case '+':
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder add_list[ai] = conf_list[i] + 1;
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder ai++;
cdae1f4c0c6e32f5463a7ce9986600e27f8ac253Christian Maeder continue;
cdae1f4c0c6e32f5463a7ce9986600e27f8ac253Christian Maeder case '-':
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder remove_list[ri] = conf_list[i] + 1;
cdae1f4c0c6e32f5463a7ce9986600e27f8ac253Christian Maeder ri++;
65c8e8b1cf766534fa0c9aec33e538dc3894ea13Christian Maeder continue;
65c8e8b1cf766534fa0c9aec33e538dc3894ea13Christian Maeder default:
65c8e8b1cf766534fa0c9aec33e538dc3894ea13Christian Maeder DEBUG(SSSDBG_CRIT_FAILURE, "ad_gpo_map values must start with"
65c8e8b1cf766534fa0c9aec33e538dc3894ea13Christian Maeder "either '+' (for adding service) or '-' (for removing service), "
65c8e8b1cf766534fa0c9aec33e538dc3894ea13Christian Maeder "got '%s'\n",
65c8e8b1cf766534fa0c9aec33e538dc3894ea13Christian Maeder conf_list[i]);
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder ret = EINVAL;
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder goto done;
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder }
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder }
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder /* Start by adding explicitly added services ('+') to hashtable */
aed438ce70de05d86d51932a078d829a3f6be5c2Christian Maeder for (i = 0; i < ai; i++) {
8dfcec5538467723b1cf9d1651de7c9c89fe4aabChristian Maeder /* if the service is explicitly configured to be removed, skip it */
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder if (ad_gpo_service_in_list(remove_list, ri, add_list[i])) {
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder continue;
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder }
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder key.type = HASH_KEY_STRING;
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder key.str = (char *)add_list[i];
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder ret = ad_gpo_parse_map_option_helper(gpo_map_type, key, options_table);
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder if (ret != EOK) {
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder DEBUG(SSSDBG_OP_FAILURE, "Invalid configuration: %d\n", ret);
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder goto done;
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder }
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder DEBUG(SSSDBG_TRACE_ALL, "Explicitly added service: %s\n", key.str);
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder }
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder /* Add defaults to hashtable */
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maeder for (i = 0; defaults[i]; i++) {
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder /* if the service is explicitly configured to be removed, skip it */
74ce8ed78c62ad2b1ec30fa9292c6589e2b4d171Christian Maeder if (ad_gpo_service_in_list(remove_list, ri, defaults[i])) {
74ce8ed78c62ad2b1ec30fa9292c6589e2b4d171Christian Maeder continue;
74ce8ed78c62ad2b1ec30fa9292c6589e2b4d171Christian Maeder }
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder
74ce8ed78c62ad2b1ec30fa9292c6589e2b4d171Christian Maeder key.type = HASH_KEY_STRING;
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder key.str = talloc_strdup(mem_ctx, defaults[i]);
74ce8ed78c62ad2b1ec30fa9292c6589e2b4d171Christian Maeder
74ce8ed78c62ad2b1ec30fa9292c6589e2b4d171Christian Maeder ret = ad_gpo_parse_map_option_helper(gpo_map_type, key, options_table);
74ce8ed78c62ad2b1ec30fa9292c6589e2b4d171Christian Maeder if (ret != EOK) {
74ce8ed78c62ad2b1ec30fa9292c6589e2b4d171Christian Maeder DEBUG(SSSDBG_OP_FAILURE, "Invalid configuration: %d\n", ret);
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder goto done;
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder }
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder DEBUG(SSSDBG_TRACE_ALL, "Default service (not explicitly removed): %s\n",
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder key.str);
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder }
d2111c9d2eb254b2a5ab71b27a6a07f1e353aec7Christian Maeder
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder ret = EOK;
a07a6a1143844a6f061a49aefc550e3bdd28f331Christian Maederdone:
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder talloc_free(tmp_ctx);
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder return ret;
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder}
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder
a4ead69b4f9c601989b4c20fbe50bde93231a159Christian Maedererrno_t
d746dda8407e83621a309fe67b1378d6f1cc3312Christian Maederad_gpo_parse_map_options(struct ad_access_ctx *access_ctx)
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder{
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder char *gpo_default_right_config;
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder enum gpo_map_type gpo_default_right;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder errno_t ret;
a4ead69b4f9c601989b4c20fbe50bde93231a159Christian Maeder int i;
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder for (i = 0; i < GPO_MAP_NUM_OPTS; i++) {
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder struct gpo_map_option_entry entry = gpo_map_option_entries[i];
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder char *entry_config = dp_opt_get_string(access_ctx->ad_options,
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder entry.ad_basic_opt);
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder ret = ad_gpo_parse_map_option(access_ctx, entry.gpo_map_type,
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder access_ctx->gpo_map_options_table,
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder entry_config, entry.gpo_map_defaults);
5fb6343a5a2b4bbc67bc83479c84a92d23d30edfChristian Maeder
584a14bf82bb60d8ffca9b45967c512d80369606Christian Maeder if (ret != EOK) {
fd94a6f66ccb5cef99aa42069b61e4b8734dbd3fChristian Maeder DEBUG(SSSDBG_OP_FAILURE, "Invalid configuration: %d\n", ret);
f0221ee7b9f8660e7c7add896e151f7a4ac8b4adChristian Maeder ret = EINVAL;
f0221ee7b9f8660e7c7add896e151f7a4ac8b4adChristian Maeder goto fail;
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder }
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder }
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder
df67ddf64192bfcae6ece65255ad796a17cbe532Christian Maeder /* default right (applicable for services without any mapping) */
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder gpo_default_right_config =
b579e1a98aa30fb6093cbbdfeae51edbd5ba26adChristian Maeder dp_opt_get_string(access_ctx->ad_options, AD_GPO_DEFAULT_RIGHT);
b579e1a98aa30fb6093cbbdfeae51edbd5ba26adChristian Maeder
b579e1a98aa30fb6093cbbdfeae51edbd5ba26adChristian Maeder DEBUG(SSSDBG_TRACE_ALL, "gpo_default_right_config: %s\n",
187e09da24d6ed264eeeef48eb3fcc2b8663ca46Christian Maeder gpo_default_right_config);
187e09da24d6ed264eeeef48eb3fcc2b8663ca46Christian Maeder
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder /* if default right not set in config, set them to DENY */
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder if (gpo_default_right_config == NULL) {
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder gpo_default_right = GPO_MAP_DENY;
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder } else if (strncasecmp(gpo_default_right_config, "interactive",
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder strlen("interactive")) == 0) {
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder gpo_default_right = GPO_MAP_INTERACTIVE;
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder } else if (strncasecmp(gpo_default_right_config, "remote_interactive",
7710f7c3425e45af11af124ff37bec27229d24f7Christian Maeder strlen("remote_interactive")) == 0) {
b579e1a98aa30fb6093cbbdfeae51edbd5ba26adChristian Maeder gpo_default_right = GPO_MAP_REMOTE_INTERACTIVE;
b579e1a98aa30fb6093cbbdfeae51edbd5ba26adChristian Maeder } else if (strncasecmp(gpo_default_right_config, "network",
b579e1a98aa30fb6093cbbdfeae51edbd5ba26adChristian Maeder strlen("network")) == 0) {
f0221ee7b9f8660e7c7add896e151f7a4ac8b4adChristian Maeder gpo_default_right = GPO_MAP_NETWORK;
b579e1a98aa30fb6093cbbdfeae51edbd5ba26adChristian Maeder } else if (strncasecmp(gpo_default_right_config, "batch",
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder strlen("batch")) == 0) {
f0221ee7b9f8660e7c7add896e151f7a4ac8b4adChristian Maeder gpo_default_right = GPO_MAP_BATCH;
d97700a22b2585ece83b05f3fff945fdfd0c44b4Christian Maeder } else if (strncasecmp(gpo_default_right_config, "service",
b579e1a98aa30fb6093cbbdfeae51edbd5ba26adChristian Maeder strlen("service")) == 0) {
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder gpo_default_right = GPO_MAP_SERVICE;
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder } else if (strncasecmp(gpo_default_right_config, "permit",
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder strlen("permit")) == 0) {
0bbc28585c7d42913167ff78a862ef25ea59e85eChristian Maeder gpo_default_right = GPO_MAP_PERMIT;
d97700a22b2585ece83b05f3fff945fdfd0c44b4Christian Maeder } else if (strncasecmp(gpo_default_right_config, "deny",
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder strlen("deny")) == 0) {
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder gpo_default_right = GPO_MAP_DENY;
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder } else {
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder ret = EINVAL;
526e7f36639cb58e3c99a54bea082499a6b04a25Christian Maeder goto fail;
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder }
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder DEBUG(SSSDBG_TRACE_ALL, "gpo_default_right: %d\n", gpo_default_right);
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder access_ctx->gpo_default_right = gpo_default_right;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maederfail:
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder return ret;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder}
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder/* == ad_gpo_access_send/recv helpers =======================================*/
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maederstatic bool
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maederad_gpo_dom_sid_equal(const struct dom_sid *sid1, const struct dom_sid *sid2)
31a81edf1285dc338211bfe86ba50a1f4128d9d2Christian Maeder{
c1bca76471ad22ac47f05b03576974762d364339Christian Maeder int i;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder if (sid1 == sid2) {
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder return true;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder }
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder if (!sid1 || !sid2) {
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder return false;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder }
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder if (sid1->sid_rev_num != sid2->sid_rev_num) {
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder return false;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder }
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder for (i = 0; i < 6; i++) {
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder if (sid1->id_auth[i] != sid2->id_auth[i]) {
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder return false;
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder }
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder }
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder if (sid1->num_auths != sid2->num_auths) {
a150c2c1cf35ba98a6dda2163c96bca7c6453025Christian Maeder 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 *user_domain,
struct sss_domain_info *host_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, host_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, host_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,
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 *user_domain;
struct sss_domain_info *host_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;
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;
goto immediately;
default:
ret = EINVAL;
goto immediately;
}
}
/* GPO Operations all happen against the enrolled domain,
* not the user's domain (which may be a trusted realm)
*/
state->user_domain = domain;
state->host_domain = get_domains_head(domain);
state->gpo_map_type = gpo_map_type;
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(state->host_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, state->host_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);
return req;
immediately:
if (ret == EOK) {
tevent_req_done(req);
} else {
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 *user_domain,
struct sss_domain_info *host_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,
user_domain,
host_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->user_domain,
state->host_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->host_domain->name, &domain_dn);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Cannot convert domain name [%s] to base DN [%d]: %s\n",
state->host_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->host_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->user_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->host_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->host_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->host_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->host_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->user_domain,
state->host_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)) {
const char *gpo_dn = state->candidate_gpos[state->gpo_index]->gpo_dn;
DEBUG(SSSDBG_OP_FAILURE,
"BUG: No attrs found for GPO [%s]. This was likely caused by "
"the GPO entry being a referred to another domain controller."
" SSSD does not yet support this configuration. See upstream "
"ticket #2645 for more information.\n",
gpo_dn);
ret = ERR_INTERNAL;
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;
}