From e5f04258b5b3fb6c04c28ddd38ae251c822e80bc Mon Sep 17 00:00:00 2001 From: Tomas Halman Date: Tue, 3 Sep 2019 16:33:54 +0200 Subject: [PATCH] extdom: plugin doesn't allow @ in group name Old implementation handles username and group names with one common call. Character @ is used in the call to detect UPN. Group name can legaly contain this character and therefore the common approach doesn't work in such case. Also the original call is less efficient because it tries to resolv username allways then it fallback to group resolution. Here we implement two new separate calls for resolving users and groups. Fixes: https://bugzilla.redhat.com/1746951 Reviewed-By: Alexander Bokovoy --- .../ipa-extdom-extop/ipa_extdom.h | 8 +- .../ipa-extdom-extop/ipa_extdom_common.c | 275 ++++++++++++------ .../ipa-extdom-extop/ipa_extdom_extop.c | 3 + 3 files changed, 199 insertions(+), 87 deletions(-) diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h index e01e74ca5..81691ca1b 100644 --- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h +++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom.h @@ -63,6 +63,7 @@ #define EXOP_EXTDOM_OID "2.16.840.1.113730.3.8.10.4" #define EXOP_EXTDOM_V1_OID "2.16.840.1.113730.3.8.10.4.1" +#define EXOP_EXTDOM_V2_OID "2.16.840.1.113730.3.8.10.4.2" #define IPA_EXTDOM_PLUGIN_NAME "ipa-extdom-extop" #define IPA_EXTDOM_FEATURE_DESC "IPA trusted domain ID mapper" @@ -72,7 +73,8 @@ enum extdom_version { EXTDOM_V0 = 0, - EXTDOM_V1 + EXTDOM_V1, + EXTDOM_V2 }; enum input_types { @@ -80,7 +82,9 @@ enum input_types { INP_NAME, INP_POSIX_UID, INP_POSIX_GID, - INP_CERT + INP_CERT, + INP_USERNAME, + INP_GROUPNAME }; enum request_types { diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c index 65c723ce6..92a7bea70 100644 --- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c +++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c @@ -271,7 +271,9 @@ int parse_request_data(struct berval *req_val, struct extdom_req **_req) * sid (1), * name (2), * posix uid (3), - * posix gid (3) + * posix gid (4), + * username (5), + * groupname (6) * }, * requestType ENUMERATED { * simple (1), @@ -337,6 +339,8 @@ int parse_request_data(struct berval *req_val, struct extdom_req **_req) switch (req->input_type) { case INP_NAME: + case INP_USERNAME: + case INP_GROUPNAME: tag = ber_scanf(ber, "{aa}}", &req->data.name.domain_name, &req->data.name.object_name); break; @@ -378,6 +382,8 @@ void free_req_data(struct extdom_req *req) switch (req->input_type) { case INP_NAME: + case INP_USERNAME: + case INP_GROUPNAME: ber_memfree(req->data.name.domain_name); ber_memfree(req->data.name.object_name); break; @@ -407,6 +413,12 @@ int check_request(struct extdom_req *req, enum extdom_version version) } } + if (version == EXTDOM_V0 || version == EXTDOM_V1) { + if (req->input_type == INP_USERNAME || req->input_type == INP_GROUPNAME) { + return LDAP_PROTOCOL_ERROR; + } + } + return LDAP_SUCCESS; } @@ -1157,17 +1169,46 @@ done: return ret; } -static int handle_name_request(struct ipa_extdom_ctx *ctx, - struct extdom_req *req, - enum request_types request_type, - const char *name, const char *domain_name, - struct berval **berval) + +static int handle_simple_request(struct extdom_req *req, + const char *fq_name, + struct berval **berval) +{ + int ret; + char *sid_str = NULL; + enum sss_id_type id_type; + + ret = sss_nss_getsidbyname(fq_name, &sid_str, &id_type); + switch(ret) { + case 0: + ret = pack_ber_sid(sid_str, berval); + break; + case ENOENT: + ret = LDAP_NO_SUCH_OBJECT; + break; + case ETIMEDOUT: + case ETIME: + ret = LDAP_TIMELIMIT_EXCEEDED; + break; + default: + set_err_msg(req, "Failed to lookup SID by name"); + ret = LDAP_OPERATIONS_ERROR; + break; + } + + free(sid_str); + return ret; +} + +static int handle_username_request(struct ipa_extdom_ctx *ctx, + struct extdom_req *req, + enum request_types request_type, + const char *name, const char *domain_name, + struct berval **berval) { int ret; char *fq_name = NULL; struct passwd pwd; - struct group grp; - char *sid_str = NULL; enum sss_id_type id_type; size_t buf_len; char *buf = NULL; @@ -1189,104 +1230,156 @@ static int handle_name_request(struct ipa_extdom_ctx *ctx, } if (request_type == REQ_SIMPLE) { - ret = sss_nss_getsidbyname(fq_name, &sid_str, &id_type); - if (ret != 0) { - if (ret == ENOENT) { - ret = LDAP_NO_SUCH_OBJECT; - } else if (ret == ETIMEDOUT || ret == ETIME) { - ret = LDAP_TIMELIMIT_EXCEEDED; - } else { - set_err_msg(req, "Failed to lookup SID by name"); - ret = LDAP_OPERATIONS_ERROR; - } - goto done; - } + /* REQ_SIMPLE */ + ret = handle_simple_request(req, fq_name, berval); + goto done; + } - ret = pack_ber_sid(sid_str, berval); - } else { - ret = get_buffer(&buf_len, &buf); - if (ret != LDAP_SUCCESS) { - goto done; - } + /* REQ_FULL || REQ_FULL_WITH_GROUPS */ + ret = get_buffer(&buf_len, &buf); + if (ret != LDAP_SUCCESS) { + goto done; + } - ret = getpwnam_r_wrapper(ctx, fq_name, &pwd, &buf, &buf_len); - if (ret == 0) { - if (request_type == REQ_FULL_WITH_GROUPS) { - ret = sss_nss_getorigbyname(pwd.pw_name, &kv_list, &id_type); - if (ret != 0 || !(id_type == SSS_ID_TYPE_UID - || id_type == SSS_ID_TYPE_BOTH)) { - set_err_msg(req, "Failed to read original data"); - if (ret == ENOENT) { - ret = LDAP_NO_SUCH_OBJECT; - } else if (ret == ETIMEDOUT || ret == ETIME) { - ret = LDAP_TIMELIMIT_EXCEEDED; - } else { - ret = LDAP_OPERATIONS_ERROR; - } - goto done; - } - } - ret = pack_ber_user(ctx, - (request_type == REQ_FULL ? RESP_USER - : RESP_USER_GROUPLIST), - domain_name, pwd.pw_name, pwd.pw_uid, - pwd.pw_gid, pwd.pw_gecos, pwd.pw_dir, - pwd.pw_shell, kv_list, berval); - } else if (ret == ENOMEM || ret == ERANGE) { - ret = LDAP_OPERATIONS_ERROR; - goto done; - } else if (ret == ETIMEDOUT) { - ret = LDAP_TIMELIMIT_EXCEEDED; - goto done; - } else { /* no user entry found */ - /* according to the getpwnam() man page there are a couple of - * error codes which can indicate that the user was not found. To - * be on the safe side we fail back to the group lookup on all - * errors. */ - ret = getgrnam_r_wrapper(ctx, fq_name, &grp, &buf, &buf_len); - if (ret != 0) { + ret = getpwnam_r_wrapper(ctx, fq_name, &pwd, &buf, &buf_len); + switch(ret) { + case 0: + if (request_type == REQ_FULL_WITH_GROUPS) { + ret = sss_nss_getorigbyname(pwd.pw_name, &kv_list, &id_type); + if (ret != 0 || !(id_type == SSS_ID_TYPE_UID + || id_type == SSS_ID_TYPE_BOTH)) { + set_err_msg(req, "Failed to read original data"); if (ret == ENOENT) { ret = LDAP_NO_SUCH_OBJECT; - } else if (ret == ETIMEDOUT) { + } else if (ret == ETIMEDOUT || ret == ETIME) { ret = LDAP_TIMELIMIT_EXCEEDED; } else { ret = LDAP_OPERATIONS_ERROR; } goto done; } - - if (request_type == REQ_FULL_WITH_GROUPS) { - ret = sss_nss_getorigbyname(grp.gr_name, &kv_list, &id_type); - if (ret != 0 || !(id_type == SSS_ID_TYPE_GID - || id_type == SSS_ID_TYPE_BOTH)) { - if (ret == ENOENT) { - ret = LDAP_NO_SUCH_OBJECT; - } else if (ret == ETIMEDOUT || ret == ETIME) { - ret = LDAP_TIMELIMIT_EXCEEDED; - } else { - set_err_msg(req, "Failed to read original data"); - ret = LDAP_OPERATIONS_ERROR; - } - goto done; - } - } - - ret = pack_ber_group((request_type == REQ_FULL ? RESP_GROUP - : RESP_GROUP_MEMBERS), - domain_name, grp.gr_name, grp.gr_gid, - grp.gr_mem, kv_list, berval); } + ret = pack_ber_user(ctx, + (request_type == REQ_FULL ? RESP_USER + : RESP_USER_GROUPLIST), + domain_name, pwd.pw_name, pwd.pw_uid, + pwd.pw_gid, pwd.pw_gecos, pwd.pw_dir, + pwd.pw_shell, kv_list, berval); + break; + case ENOMEM: + case ERANGE: + ret = LDAP_OPERATIONS_ERROR; + break; + case ETIMEDOUT: + ret = LDAP_TIMELIMIT_EXCEEDED; + break; + default: + ret = LDAP_NO_SUCH_OBJECT; + break; } done: sss_nss_free_kv(kv_list); free(fq_name); - free(sid_str); free(buf); return ret; } +static int handle_groupname_request(struct ipa_extdom_ctx *ctx, + struct extdom_req *req, + enum request_types request_type, + const char *name, const char *domain_name, + struct berval **berval) +{ + int ret; + char *fq_name = NULL; + struct group grp; + enum sss_id_type id_type; + size_t buf_len; + char *buf = NULL; + struct sss_nss_kv *kv_list = NULL; + + /* with groups we can be sure that name doesn't contain the domain_name */ + ret = asprintf(&fq_name, "%s%c%s", name, SSSD_DOMAIN_SEPARATOR, + domain_name); + if (ret == -1) { + ret = LDAP_OPERATIONS_ERROR; + set_err_msg(req, "Failed to create fully qualified name"); + fq_name = NULL; /* content is undefined according to + asprintf(3) */ + goto done; + } + + if (request_type == REQ_SIMPLE) { + /* REQ_SIMPLE */ + ret = handle_simple_request(req, fq_name, berval); + goto done; + } + + /* REQ_FULL || REQ_FULL_WITH_GROUPS */ + ret = get_buffer(&buf_len, &buf); + if (ret != LDAP_SUCCESS) { + goto done; + } + + ret = getgrnam_r_wrapper(ctx, fq_name, &grp, &buf, &buf_len); + if (ret != 0) { + if (ret == ENOMEM || ret == ERANGE) { + ret = LDAP_OPERATIONS_ERROR; + } else { + ret = LDAP_NO_SUCH_OBJECT; + } + goto done; + } + + if (request_type == REQ_FULL_WITH_GROUPS) { + ret = sss_nss_getorigbyname(grp.gr_name, &kv_list, &id_type); + if (ret != 0 || !(id_type == SSS_ID_TYPE_GID + || id_type == SSS_ID_TYPE_BOTH)) { + if (ret == ENOENT) { + ret = LDAP_NO_SUCH_OBJECT; + } else { + set_err_msg(req, "Failed to read original data"); + ret = LDAP_OPERATIONS_ERROR; + } + goto done; + } + } + + ret = pack_ber_group((request_type == REQ_FULL ? RESP_GROUP + : RESP_GROUP_MEMBERS), + domain_name, grp.gr_name, grp.gr_gid, + grp.gr_mem, kv_list, berval); + +done: + sss_nss_free_kv(kv_list); + free(fq_name); + free(buf); + + return ret; +} + +static int handle_name_request(struct ipa_extdom_ctx *ctx, + struct extdom_req *req, + enum request_types request_type, + const char *name, const char *domain_name, + struct berval **berval) +{ + int ret; + + + ret = handle_username_request(ctx, req, request_type, + name, domain_name, berval); + if (ret == LDAP_NO_SUCH_OBJECT) { + ret = handle_groupname_request(ctx, req, request_type, + name, domain_name, berval); + } + + return ret; +} + + int handle_request(struct ipa_extdom_ctx *ctx, struct extdom_req *req, struct berval **berval) { @@ -1318,6 +1411,18 @@ int handle_request(struct ipa_extdom_ctx *ctx, struct extdom_req *req, req->data.name.object_name, req->data.name.domain_name, berval); + break; + case INP_GROUPNAME: + ret = handle_groupname_request(ctx, req, req->request_type, + req->data.name.object_name, + req->data.name.domain_name, berval); + + break; + case INP_USERNAME: + ret = handle_username_request(ctx, req, req->request_type, + req->data.name.object_name, + req->data.name.domain_name, berval); + break; default: set_err_msg(req, "Unknown input type"); diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c index 48fcecc1e..5f9714f98 100644 --- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c +++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c @@ -54,6 +54,7 @@ Slapi_PluginDesc ipa_extdom_plugin_desc = { static char *ipa_extdom_oid_list[] = { EXOP_EXTDOM_OID, EXOP_EXTDOM_V1_OID, + EXOP_EXTDOM_V2_OID, NULL }; @@ -196,6 +197,8 @@ static int ipa_extdom_extop(Slapi_PBlock *pb) version = EXTDOM_V0; } else if (strcasecmp(oid, EXOP_EXTDOM_V1_OID) == 0) { version = EXTDOM_V1; + } else if (strcasecmp(oid, EXOP_EXTDOM_V2_OID) == 0) { + version = EXTDOM_V2; } else { return SLAPI_PLUGIN_EXTENDED_NOT_HANDLED; }