/*
SSSD
IPA Helper routines - external users and groups with s2n plugin
Copyright (C) Sumit Bose <sbose@redhat.com> - 2011
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "util/strtonum.h"
#include "util/crypto/sss_crypto.h"
#include "providers/ldap/sdap_async_private.h"
#include "providers/ldap/sdap_async_ad.h"
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_idmap.h"
#include "providers/ipa/ipa_subdomains.h"
enum input_types {
};
enum request_types {
};
enum response_types {
};
/* ==Sid2Name Extended Operation============================================= */
struct ipa_s2n_exop_state {
char *retoid;
};
struct tevent_context *ev,
struct sdap_handle *sh,
bool is_v1,
int timeout,
{
int ret;
int msgid;
goto fail;
}
msgid);
if (ret) {
ret = ERR_INTERNAL;
goto fail;
}
return req;
fail:
return req;
}
{
struct ipa_s2n_exop_state);
int ret;
int result;
if (error) {
return;
}
NULL, 0);
if (ret != LDAP_SUCCESS) {
goto done;
}
"ldap_extended_operation result: %s(%d), %s.\n",
if (result != LDAP_SUCCESS) {
if (result == LDAP_NO_SUCH_OBJECT) {
} else {
"logs might contain more details.\n");
}
goto done;
}
if (ret != LDAP_SUCCESS) {
ret);
goto done;
}
goto done;
}
goto done;
}
goto done;
}
goto done;
}
done:
} else {
}
}
{
struct ipa_s2n_exop_state);
return EOK;
}
{
int ret;
if (ret == -1) {
goto done;
}
goto done;
}
goto done;
}
done:
ber_bvfree(bv);
} else {
}
return ret;
}
/* The extended operation expect the following ASN.1 encoded request data:
*
* ExtdomRequestValue ::= SEQUENCE {
* inputType ENUMERATED {
* sid (1),
* name (2),
* posix uid (3),
* posix gid (3)
* },
* requestType ENUMERATED {
* simple (1),
* full (2)
* full_with_members (3)
* },
* data InputData
* }
*
* InputData ::= CHOICE {
* sid OCTET STRING,
* name NameDomainData
* uid PosixUid,
* gid PosixGid
* }
*
* NameDomainData ::= SEQUENCE {
* domain_name OCTET STRING,
* object_name OCTET STRING
* }
*
* PosixUid ::= SEQUENCE {
* domain_name OCTET STRING,
* uid INTEGER
* }
*
* PosixGid ::= SEQUENCE {
* domain_name OCTET STRING,
* gid INTEGER
* }
*
*/
const char *domain_name,
int entry_type,
enum request_types request_type,
{
int ret;
return ENOMEM;
}
switch (entry_type) {
case BE_REQ_USER:
case BE_REQ_USER_AND_GROUP: /* the extdom exop does not care if the
ID belongs to a user or a group */
} else {
goto done;
}
break;
case BE_REQ_GROUP:
} else {
goto done;
}
break;
case BE_REQ_BY_SECID:
} else {
goto done;
}
break;
case BE_REQ_BY_CERT:
} else {
goto done;
}
break;
default:
goto done;
}
if (ret == -1) {
goto done;
}
goto done;
}
done:
return ret;
}
/* If the extendend operation is successful it returns the following ASN.1
* encoded response:
*
* ExtdomResponseValue ::= SEQUENCE {
* responseType ENUMERATED {
* sid (1),
* name (2),
* posix_user (3),
* posix_group (4),
* posix_user_grouplist (5),
* posix_group_members (6)
* },
* data OutputData
* }
*
* OutputData ::= CHOICE {
* sid OCTET STRING,
* name NameDomainData,
* user PosixUser,
* group PosixGroup,
* usergrouplist PosixUserGrouplist,
* groupmembers PosixGroupMembers
*
* }
*
* NameDomainData ::= SEQUENCE {
* domain_name OCTET STRING,
* object_name OCTET STRING
* }
*
* PosixUser ::= SEQUENCE {
* domain_name OCTET STRING,
* user_name OCTET STRING,
* uid INTEGER
* gid INTEGER
* }
*
* PosixGroup ::= SEQUENCE {
* domain_name OCTET STRING,
* group_name OCTET STRING,
* gid INTEGER
* }
*
* PosixUserGrouplist ::= SEQUENCE {
* domain_name OCTET STRING,
* user_name OCTET STRING,
* uid INTEGER,
* gid INTEGER,
* gecos OCTET STRING,
* home_directory OCTET STRING,
* shell OCTET STRING,
* grouplist GroupNameList
* }
*
* GroupNameList ::= SEQUENCE OF OCTET STRING
*
* PosixGroupMembers ::= SEQUENCE {
* domain_name OCTET STRING,
* group_name OCTET STRING,
* gid INTEGER,
* members GroupMemberList
* }
*
* GroupMemberList ::= SEQUENCE OF OCTET STRING
*/
struct name_list {
char *domain_name;
char *name;
};
struct resp_attrs {
char *domain_name;
union {
char *sid_str;
char *name;
} a;
char **groups;
char **name_list;
};
{
char *ber_cookie;
char *name;
struct ldb_val v;
int ret;
size_t c;
return ENOMEM;
}
}
tag != LBER_DEFAULT;
if (tag == LBER_ERROR) {
return EINVAL;
}
"base64 encoded certificate not 0-terminated.\n");
return EINVAL;
}
return EINVAL;
}
} else {
}
return ret;
}
}
}
return EOK;
}
struct resp_attrs *attrs)
{
int ret;
if (tag == LBER_ERROR) {
goto done;
}
} else {
goto done;
}
}
} else {
goto done;
}
}
} else {
goto done;
}
}
if (tag == LBER_ERROR) {
goto done;
}
goto done;
}
"Cannot parse member %s\n", list[c]);
continue;
}
if (obj_domain == NULL) {
return ENOMEM;
}
} else {
}
goto done;
}
gc++;
}
}
if (tag == LBER_SEQUENCE) {
goto done;
}
}
done:
ber_memvfree((void **) list);
return ret;
}
struct sss_domain_info *dom,
struct resp_attrs *attrs)
{
int ret;
if (tag == LBER_ERROR) {
goto done;
}
goto done;
}
"Cannot parse member %s\n", list[c]);
continue;
}
}
goto done;
}
mc++;
}
}
} else {
goto done;
}
}
if (tag == LBER_SEQUENCE) {
goto done;
}
}
done:
ber_memvfree((void **) list);
return ret;
}
struct resp_attrs *attrs,
struct resp_attrs *simple_attrs,
const char *view_name,
struct sysdb_attrs *override_attrs,
struct sysdb_attrs *mapped_attrs,
bool update_initgr_timeout);
struct sss_domain_info *dom,
char *retoid,
struct resp_attrs **resp_attrs)
{
int ret;
char *sid_str;
bool is_v1 = false;
return EINVAL;
}
is_v1 = true;
is_v1 = false;
} else {
"Result has wrong OID, expected [%s] or [%s], got [%s].\n",
return EINVAL;
}
return EINVAL;
}
if (tag == LBER_ERROR) {
goto done;
}
goto done;
}
switch (type) {
case RESP_USER:
case RESP_USER_GROUPLIST:
if (tag == LBER_ERROR) {
goto done;
}
/* Winbind is not consistent with the case of the returned user
* name. In general all names should be lower case but there are
* bug in some version of winbind which might lead to upper case
* letters in the name. To be on the safe side we explicitly
* lowercase the name. */
goto done;
}
goto done;
}
goto done;
}
}
if (tag == LBER_ERROR) {
goto done;
}
break;
case RESP_GROUP:
case RESP_GROUP_MEMBERS:
if (tag == LBER_ERROR) {
goto done;
}
/* Winbind is not consistent with the case of the returned user
* name. In general all names should be lower case but there are
* bug in some version of winbind which might lead to upper case
* letters in the name. To be on the safe side we explicitly
* lowercase the name. */
goto done;
}
goto done;
}
goto done;
}
}
if (tag == LBER_ERROR) {
goto done;
}
break;
case RESP_SID:
if (tag == LBER_ERROR) {
goto done;
}
goto done;
}
break;
case RESP_NAME:
if (tag == LBER_ERROR) {
goto done;
}
goto done;
}
break;
case RESP_NAME_LIST:
if (tag == LBER_ERROR) {
goto done;
}
if (tag == LBER_ERROR) {
goto done;
}
"sss_create_internal_fqname failed.\n");
goto done;
}
fq_name);
} else {
"[%s] from root domain, skipping.\n", fq_name);
}
domain_name = NULL;
goto done;
}
}
if (tag == LBER_ERROR) {
goto done;
}
break;
default:
type);
goto done;
}
goto done;
}
}
done:
*resp_attrs = attrs;
} else {
}
return ret;
}
{
switch (request_type) {
case REQ_SIMPLE:
return "REQ_SIMPLE";
case REQ_FULL:
return "REQ_FULL";
case REQ_FULL_WITH_MEMBERS:
return "REQ_FULL_WITH_MEMBERS";
default:
break;
}
return "Unknown request type";
}
{
case REQ_INP_NAME:
break;
case REQ_INP_SECID:
break;
case REQ_INP_CERT:
break;
case REQ_INP_ID:
break;
}
}
return str;
}
struct ipa_s2n_get_list_state {
char **list;
int exop_timeout;
int entry_type;
};
struct tevent_context *ev,
struct ipa_id_ctx *ipa_ctx,
struct sss_domain_info *dom,
struct sdap_handle *sh,
int exop_timeout,
int entry_type,
enum request_types request_type,
enum req_input_type list_type,
char **list,
struct sysdb_attrs *mapped_attrs)
{
int ret;
return NULL;
}
goto done;
}
goto done;
}
done:
}
return req;
}
{
int ret;
struct ipa_s2n_get_list_state);
char *endptr;
bool need_v1 = false;
case REQ_INP_NAME:
&domain_name, &short_name);
return ret;
}
if (domain_name) {
domain_name, true);
return ENOMEM;
}
} else {
}
break;
case REQ_INP_ID:
errno = 0;
return EINVAL;
}
break;
case REQ_INP_SECID:
"find_domain_by_sid failed for SID [%s].\n",
return EINVAL;
}
break;
default:
return EINVAL;
}
return ret;
}
need_v1 = true;
}
"Sending request_type: [%s] for object [%s].\n",
}
return ENOMEM;
}
return EOK;
}
{
int ret;
struct tevent_req);
struct ipa_s2n_get_list_state);
const char *sid_str;
goto fail;
}
goto fail;
}
goto fail;
}
return;
}
&sid_str);
"Object [%s] has no SID, please check the "
"ipaNTSecurityIdentifier attribute on the server-side",
goto fail;
}
goto fail;
}
ar);
goto fail;
}
return;
fail:
return;
}
{
int ret;
struct tevent_req);
struct ipa_s2n_get_list_state);
goto fail;
}
goto fail;
}
return;
fail:
return;
}
{
int ret;
struct ipa_s2n_get_list_state);
false);
return ret;
}
return EOK;
}
return ret;
}
return EAGAIN;
}
{
return EOK;
}
struct ipa_s2n_get_user_state {
int entry_type;
int exop_timeout;
};
struct tevent_context *ev,
struct ipa_id_ctx *ipa_ctx,
struct sdap_options *opts,
struct sss_domain_info *dom,
struct sysdb_attrs *override_attrs,
struct sdap_handle *sh,
int entry_type,
{
const char *input;
bool is_v1 = false;
return NULL;
}
is_v1 = true;
is_v1 = false;
} else {
"cannot resolve objects from trusted domains.\n");
goto fail;
}
if (entry_type == BE_REQ_BY_CERT) {
/* Only REQ_SIMPLE is supported for BE_REQ_BY_CERT */
}
goto fail;
}
if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) {
"Sending request_type: [%s] for trust user [%s] to IPA server\n",
input);
}
goto fail;
}
return req;
fail:
return req;
}
bool is_default_view,
struct sysdb_attrs *group_attrs,
char **members,
{
int ret;
size_t c;
const char *dn_str;
if (_missing_members != NULL) {
*_missing_members = NULL;
}
return EOK;
}
return ENOMEM;
}
/* count members */
if (missing_members == NULL) {
goto done;
}
}
if (obj_domain == NULL) {
goto done;
}
&msg);
|| (!is_default_view
/* only add ghost if the member is really missing */
members[c]);
/* There were cases where the server returned the same user
* multiple times */
members[c]);
"sysdb_attrs_add_string failed.\n");
goto done;
}
}
if (missing_members != NULL) {
members[c]);
goto done;
}
miss_count++;
}
} else {
if (group_attrs != NULL) {
goto done;
}
dn_str);
"sysdb_attrs_add_string_safe failed.\n");
goto done;
}
}
}
} else {
goto done;
}
}
if (_missing_members != NULL) {
if (miss_count == 0) {
*_missing_members = NULL;
} else {
} else {
"Missing memory context for missing members list.\n");
goto done;
}
}
}
done:
return ret;
}
bool is_default_view,
struct sss_domain_info *dom,
char ***_missing_groups)
{
int ret;
size_t c;
return ENOMEM;
}
goto done;
}
for (c = 0; c < ngroups; c++) {
if (obj_domain == NULL) {
goto done;
}
&msg);
|| (!is_default_view
groups[c]);
goto done;
}
n_missing++;
} else {
goto done;
}
n_dns++;
}
} else {
goto done;
}
}
if (n_missing != 0) {
} else {
*_missing_groups = NULL;
}
if (n_dns != 0) {
} else {
}
done:
return ret;
}
{
struct tevent_req);
struct ipa_s2n_get_user_state);
int ret;
const char *sid_str;
"Maybe the server does not support lookups by "
"certificates.\n");
}
goto done;
}
switch (state->request_type) {
case REQ_FULL_WITH_MEMBERS:
case REQ_FULL:
&attrs);
goto done;
}
"expected [%s] or [%s], got [%s].\n",
attrs->domain_name);
goto done;
}
if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) {
size_t c;
}
}
goto done;
}
if (missing_list != NULL) {
missing_list, NULL);
"ipa_s2n_get_list_send failed.\n");
goto done;
}
req);
return;
}
break;
&missing_list);
goto done;
}
if (missing_list != NULL) {
missing_list, NULL);
"ipa_s2n_get_list_send failed.\n");
goto done;
}
req);
return;
}
break;
}
/* We already know the SID, we do not have to read it. */
break;
}
&bv_req);
goto done;
}
goto done;
}
return;
case REQ_SIMPLE:
&state->simple_attrs);
goto done;
}
/* No results from sub-domains, nothing to do */
goto done;
}
goto done;
}
goto done;
}
"ipa_s2n_get_list_send failed.\n");
goto done;
}
req);
return;
}
break;
default:
goto done;
}
goto done;
}
&sid_str);
} else {
}
goto done;
}
goto done;
}
ar);
goto done;
}
req);
return;
} else {
goto done;
}
done:
} else {
}
return;
}
{
int ret;
int c;
char **dn_list;
return EOK;
}
/* To handle cross-domain memberships we have to check the domain for
* each group the member should be added or deleted. Since sub-domains
* use fully-qualified names by default any short name can only belong
* the domain given in the first argument if the second argument is a
* a short name hence we always use root_domain as first argument. */
if (root_domain->fqnames) {
"Root domain uses fully-qualified names, " \
"objects might not be correctly added to groups with " \
"short names.\n");
}
return ENOMEM;
}
goto done;
}
"Cannot find domain for [%s].\n", name_list[c]);
goto done;
}
/* If the group name is overridden in the default view we have to
* search for the name and cannot construct it because the extdom
* plugin will return the overridden name but the DN of the related
* group object in the cache will contain the original name. */
&msg);
} else {
/* best effort, try to construct the DN */
"sysdb_search_group_by_name failed with [%d], "
"generating DN for [%s] in domain [%s].\n",
}
goto done;
}
}
done:
return ret;
}
struct sss_domain_info *dom)
{
int ret;
const char **emails;
size_t c;
return ENOMEM;
}
&emails);
"Failed to add lower-cased version of email [%s] "
"into the alias list\n", emails[c]);
goto done;
}
}
}
} else {
"sysdb_attrs_get_string_array failed, skipping ...\n");
}
done:
return ret;
}
struct resp_attrs *attrs,
struct resp_attrs *simple_attrs,
const char *view_name,
struct sysdb_attrs *override_attrs,
struct sysdb_attrs *mapped_attrs,
bool update_initgr_timeout)
{
int ret;
char *realm;
const char *sid_str;
const char *tmp_str;
char **sysdb_grouplist;
char **add_groups_dns;
char **del_groups_dns;
char **groups_dns;
bool in_transaction = false;
int tret;
/* The list of elements that might be missing are:
* - SYSDB_ORIG_MEMBEROF
* - SYSDB_SSH_PUBKEY
* - SYSDB_USER_CERT
* Note that the list includes the trailing NULL at the end. */
return ENOMEM;
}
goto done;
}
}
goto done;
}
} else {
goto done;
}
tmp_str);
"sysdb_attrs_add_lc_name_alias_safe failed.\n");
goto done;
}
goto done;
}
goto done;
}
} else {
goto done;
}
}
attrs->domain_name, true);
goto done;
}
}
switch (attrs->response_type) {
case RESP_USER:
case RESP_USER_GROUPLIST:
if (dom->subdomain_homedir
&homedir_ctx);
goto done;
}
}
}
"sysdb_attrs_add_lc_name_alias_safe failed.\n");
goto done;
}
"add_emails_to_aliases failed, skipping ...\n");
}
/* We also have to store a fake UPN here, because otherwise the
* krb5 child later won't be able to properly construct one as
* the username is fully qualified but the child doesn't have
* access to the regex to deconstruct it */
/* FIXME: The real UPN is available from the PAC, we should get
* it from there. */
if (!realm) {
goto done;
}
&short_name, NULL);
"Cannot parse internal name %s\n",
goto done;
}
if (!upn) {
goto done;
}
/* We might already have the SID or the UPN from other sources
* hence sysdb_attrs_add_string_safe is used to avoid double
* entries. */
upn);
"sysdb_attrs_add_string failed.\n");
goto done;
}
}
"sysdb_attrs_add_string failed.\n");
goto done;
}
}
if (simple_attrs != NULL
simple_attrs->a.sid_str);
"sysdb_attrs_add_string failed.\n");
goto done;
}
}
&& update_initgr_timeout) {
/* Since RESP_USER_GROUPLIST contains all group memberships it
* is effectively an initgroups request hence
* SYSDB_INITGR_EXPIRE will be set.*/
"sysdb_attrs_add_time_t failed.\n");
goto done;
}
}
gid = 0;
} else {
/* The extdom plugin always returns the objects with the
* default view applied. Since the GID is handled specially
* for MPG domains we have add any overridden GID separately.
*/
&orig_gid);
if (gid_override_attrs == NULL) {
"sysdb_new_attrs failed.\n");
goto done;
}
"sysdb_attrs_add_uint32 failed.\n");
goto done;
}
}
} else {
"sysdb_attrs_get_uint32_t failed.\n");
goto done;
}
}
SYSDB_ORIG_MEMBEROF, false, &el);
}
SYSDB_SSH_PUBKEY, false, &el);
}
SYSDB_USER_CERT, false, &el);
}
goto done;
}
in_transaction = true;
: discard_const(missing),
/* This handles the case where getgrgid() was called for
* this user, so a group was created in the cache
*/
/* Fail even on ENOENT, the group must be around */
"Could not delete MPG group [%d]: %s\n",
goto done;
}
"sysdb_delete_group failed for MPG group [%d]: %s\n",
goto done;
}
"sysdb_store_user failed for MPG user [%d]: %s\n",
goto done;
}
"sysdb_store_user failed [%d]: %s\n",
goto done;
}
if (mapped_attrs != NULL) {
goto done;
}
}
if (gid_override_attrs != NULL) {
goto done;
}
}
goto done;
}
goto done;
}
&del_groups_dns, NULL);
goto done;
}
name);
(const char *const *) add_groups_dns,
(const char *const *) del_groups_dns);
goto done;
}
}
goto done;
}
in_transaction = false;
break;
case RESP_GROUP:
case RESP_GROUP_MEMBERS:
}
"sysdb_attrs_add_lc_name_alias_safe failed.\n");
goto done;
}
/* We might already have the SID from other sources hence
* sysdb_attrs_add_string_safe is used to avoid double entries. */
"sysdb_attrs_add_string failed.\n");
goto done;
}
}
if (simple_attrs != NULL
simple_attrs->a.sid_str);
"sysdb_attrs_add_string failed.\n");
goto done;
}
}
goto done;
}
now);
goto done;
}
break;
default:
goto done;
}
"Cannot find SID of object.\n");
"Object [%s] has no SID, please check the "
"ipaNTSecurityIdentifier attribute on the server-side.\n",
name);
}
goto done;
}
"Cannot find object with override with SID [%s].\n", sid_str);
goto done;
}
if (!is_default_view(view_name)) {
/* For the default view the data return by the extdom plugin already
* contains all needed data and it is not expected to have a separate
* override object. */
goto done;
}
}
done:
if (in_transaction) {
}
}
return ret;
}
{
int ret;
struct tevent_req);
struct ipa_s2n_get_user_state);
const char *sid_str;
return;
}
/* If this is a request by certificate we are done */
} else {
}
return;
}
&sid_str);
goto fail;
}
return;
goto fail;
}
goto fail;
}
ar);
goto fail;
}
req);
} else {
return;
}
}
return;
fail:
return;
}
{
int ret;
struct tevent_req);
struct ipa_s2n_get_user_state);
return;
}
override_attrs, NULL, true);
return;
}
return;
}
{
return EOK;
}
char *username;
char **missing_sids;
char **cached_groups;
};
struct tevent_context *ev,
struct sdap_handle *sh,
struct ipa_id_ctx *ipa_ctx,
struct sss_domain_info *dom,
struct ldb_message *user_msg)
{
int ret;
char *user_sid;
char *primary_group_sid;
char **group_sids;
return NULL;
}
&num_sids, &group_sids);
goto done;
}
&state->cached_groups);
"sdap_ad_tokengroups_get_posix_members failed.\n");
goto done;
}
if (state->num_missing_sids == 0) {
}
goto done;
}
goto done;
}
return req;
done:
} else {
}
return req;
}
{
int ret;
struct tevent_req);
char **cached_groups;
return;
}
/* from ad_pac.c */
"sdap_ad_tokengroups_get_posix_members failed [%d]: %s\n",
goto done;
}
goto done;
}
/* update membership of existing groups */
goto done;
}
done:
} else {
}
return;
}
{
return EOK;
}