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 <abokovoy@redhat.com>
This commit is contained in:
Tomas Halman authored and Alexander Bokovoy committed 2019-09-12 10:48:13 +03:00
1 parent 123c93f92c
commit e5f04258b5
3 files changed
+199 -87

No files matched your search

@@ -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 {
@@ -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");
@@ -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;
}