diff options
Diffstat (limited to 'daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c')
-rw-r--r-- | daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c | 498 |
1 files changed, 498 insertions, 0 deletions
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 new file mode 100644 index 000000000..294b00d50 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c @@ -0,0 +1,498 @@ +/** BEGIN COPYRIGHT BLOCK + * This program is free software; you can redistribute it and/or modify + * 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/>. + * + * Additional permission under GPLv3 section 7: + * + * In the following paragraph, "GPL" means the GNU General Public + * License, version 3 or any later version, and "Non-GPL Code" means + * code that is governed neither by the GPL nor a license + * compatible with the GPL. + * + * You may link the code of this Program with Non-GPL Code and convey + * linked combinations including the two, provided that such Non-GPL + * Code only links to the code of this Program through those well + * defined interfaces identified in the file named EXCEPTION found in + * the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline + * functions from the Approved Interfaces without causing the resulting + * work to be covered by the GPL. Only the copyright holders of this + * Program may make changes or additions to the list of Approved + * Interfaces. + * + * Authors: + * Sumit Bose <sbose@redhat.com> + * + * Copyright (C) 2011 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 /* for asprintf() */ +#endif + +#include <errno.h> +#include <stdio.h> + +#include "ipa_extdom.h" +#include "util.h" + +int parse_request_data(struct berval *req_val, struct extdom_req **_req) +{ + BerElement *ber = NULL; + ber_tag_t tag; + ber_int_t input_type; + ber_int_t request_type; + ber_int_t id; + struct extdom_req *req; + +/* We expect the following request: + * ExtdomRequestValue ::= SEQUENCE { + * inputType ENUMERATED { + * sid (1), + * name (2), + * posix uid (3), + * posix gid (3) + * }, + * requestType ENUMERATED { + * simple (1), + * full (2) + * }, + * 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 + * } + */ + + if (req_val == NULL || req_val->bv_val == NULL || req_val->bv_len == 0) { + return LDAP_PROTOCOL_ERROR; + } + + ber = ber_init(req_val); + if (ber == NULL) { + return LDAP_PROTOCOL_ERROR; + } + + tag = ber_scanf(ber, "{ee", &input_type, &request_type); + if (tag == LBER_ERROR) { + ber_free(ber, 1); + return LDAP_PROTOCOL_ERROR; + } + + req = calloc(sizeof(struct extdom_req), 1); + if (req == NULL) { + return LDAP_OPERATIONS_ERROR; + } + + req->input_type = input_type; + req->request_type = request_type; + + switch (req->input_type) { + case INP_NAME: + tag = ber_scanf(ber, "{aa}}", &req->data.name.domain_name, + &req->data.name.object_name); + break; + case INP_SID: + tag = ber_scanf(ber, "a}", &req->data.sid); + break; + case INP_POSIX_UID: + tag = ber_scanf(ber, "{ai}}", &req->data.posix_uid.domain_name, + &id); + req->data.posix_uid.uid = (uid_t) id; + break; + case INP_POSIX_GID: + tag = ber_scanf(ber, "{ai}}", &req->data.posix_gid.domain_name, + &id); + req->data.posix_gid.gid = (gid_t) id; + break; + default: + ber_free(ber, 1); + return LDAP_PROTOCOL_ERROR; + } + ber_free(ber, 1); + if (tag == LBER_ERROR) { + return LDAP_PROTOCOL_ERROR; + } + + *_req = req; + + return LDAP_SUCCESS; +} + +static void free_domain_info(struct domain_info *domain_info) +{ + if (domain_info == NULL) { + return; + } + + sss_idmap_free(domain_info->idmap_ctx); + slapi_ch_free((void **) &domain_info->guid); + slapi_ch_free((void **) &domain_info->sid); + slapi_ch_free((void **) &domain_info->flat_name); + free(domain_info); +} + +/* TODO: A similar call is used in ipa_cldap_netlogon.c, maybe a candidate for + * a common library */ +static int get_domain_info(struct ipa_extdom_ctx *ctx, const char *domain_name, + struct domain_info **_domain_info) +{ + struct domain_info *domain_info = NULL; + Slapi_PBlock *pb = NULL; + Slapi_Entry **e = NULL; + char *filter = NULL; + int ret; + enum idmap_error_code err; + struct sss_idmap_range range; + + pb = slapi_pblock_new(); + if (pb == NULL) { + return ENOMEM; + } + + ret = asprintf(&filter, "(&(|(cn=%s)(ipaNTTrustPartner=%s)(ipaNTFlatName=%s))(objectclass=ipaNTTrustedDomain))", + domain_name, domain_name, domain_name); + if (ret == -1) { + ret = ENOMEM; + goto done; + } + + slapi_search_internal_set_pb(pb, ctx->base_dn, + LDAP_SCOPE_SUBTREE, filter, + NULL, 0, NULL, NULL, ctx->plugin_id, 0); + + slapi_search_internal_pb(pb); + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret); + + if (ret != EOK) { + ret = ENOENT; + goto done; + } + + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &e); + if (!e || !e[0] || e[1]) { + /* no matches or too many matches */ + ret = ENOENT; + goto done; + } + + domain_info = calloc(1, sizeof(struct domain_info)); + if (domain_info == NULL) { + ret = ENOMEM; + goto done; + } + + domain_info->guid = slapi_entry_attr_get_charptr(e[0], "ipaNTDomainGUID"); + domain_info->sid = slapi_entry_attr_get_charptr(e[0], + "ipaNTTrustedDomainSID"); + domain_info->flat_name = slapi_entry_attr_get_charptr(e[0], + "ipaNTFlatName"); + + /* TODO: read range from LDAP server */ + range.min = 200000; + range.max = 400000; + + err = sss_idmap_init(NULL, NULL, NULL, &domain_info->idmap_ctx); + if (err == IDMAP_SUCCESS) { + err = sss_idmap_add_domain(domain_info->idmap_ctx, domain_name, + domain_info->sid, &range); + } + if (err != IDMAP_SUCCESS) { + free_domain_info(domain_info); + ret = EFAULT; + goto done; + } + + *_domain_info = domain_info; + + ret = 0; + +done: + slapi_free_search_results_internal(pb); + slapi_pblock_destroy(pb); + free(filter); + return ret; + +} + +int handle_request(struct ipa_extdom_ctx *ctx, struct extdom_req *req, + struct extdom_res **res) +{ + wbcErr werr; + int ret; + struct wbcDomainSid sid; + char *domain_name; + char *name; + enum wbcSidType name_type; + struct domain_info *domain_info = NULL; + + ret = get_domain_info(ctx, req->data.name.domain_name, &domain_info); + if (ret != 0) { + return LDAP_OPERATIONS_ERROR; + } + + switch (req->input_type) { + case INP_SID: + werr = wbcStringToSid(req->data.sid, &sid); + if (!WBC_ERROR_IS_OK(werr)) { + ret = LDAP_OPERATIONS_ERROR; + goto done; + } + + werr = wbcLookupSid(&sid, &domain_name, &name, &name_type); + if (!WBC_ERROR_IS_OK(werr)) { + ret = LDAP_OPERATIONS_ERROR; + goto done; + } + + ret = create_response(req, domain_info, domain_name, name, &sid, + name_type, res); + if (ret != 0) { + ret = LDAP_OPERATIONS_ERROR; + goto done; + } + + break; + case INP_NAME: + werr = wbcLookupName(domain_info->flat_name, + req->data.name.object_name, &sid, &name_type); + if (!WBC_ERROR_IS_OK(werr)) { + ret = LDAP_OPERATIONS_ERROR; + goto done; + } + + ret = create_response(req, domain_info, req->data.name.domain_name, + req->data.name.object_name, &sid, name_type, + res); + if (ret != 0) { + ret = LDAP_OPERATIONS_ERROR; + goto done; + } + + break; + default: + ret = LDAP_PROTOCOL_ERROR; + goto done; + } + + ret = LDAP_SUCCESS; + +done: + free_domain_info(domain_info); + + return ret; +} + +int create_response(struct extdom_req *req, struct domain_info *domain_info, + const char *domain_name, + const char *name, struct wbcDomainSid *sid, + enum wbcSidType name_type, struct extdom_res **_res) +{ + int ret = EFAULT; + int len; + struct extdom_res *res; + uint32_t id; + enum idmap_error_code err; + char sid_str[WBC_SID_STRING_BUFLEN + 1]; + + res = malloc(sizeof(struct extdom_res)); + if (res == NULL) { + return ENOMEM; + } + + switch (req->request_type) { + case REQ_SIMPLE: + switch (req->input_type) { + case INP_SID: + res->response_type = RESP_NAME; + res->data.name.domain_name = domain_name; + res->data.name.object_name = name; + break; + case INP_NAME: + res->response_type = RESP_SID; + + len = wbcSidToStringBuf(sid, sid_str, + WBC_SID_STRING_BUFLEN); + if (len + 1 > WBC_SID_STRING_BUFLEN) { + ret = EINVAL; + goto done; + } + + res->data.sid = sid_str; + break; + default: + ret = EINVAL; + goto done; + } + break; + case REQ_FULL: + len = wbcSidToStringBuf(sid, sid_str, WBC_SID_STRING_BUFLEN); + if (len + 1 > WBC_SID_STRING_BUFLEN) { + ret = EINVAL; + goto done; + } + + err = sss_idmap_sid_to_unix(domain_info->idmap_ctx, sid_str, &id); + if (err != IDMAP_SUCCESS) { + ret = EINVAL; + goto done; + } + switch (name_type) { + case WBC_SID_NAME_USER: + res->response_type = RESP_USER; + res->data.user.domain_name = domain_name; + res->data.user.user_name = name; + + res->data.user.uid = (uid_t) id; + + /* We use MPGs for external users */ + res->data.user.gid = (gid_t) id; + break; + case WBC_SID_NAME_DOM_GRP: + res->response_type = RESP_GROUP; + res->data.group.domain_name = domain_name; + res->data.group.group_name = name; + + res->data.group.gid = (gid_t) id; + break; + default: + ret = EINVAL; + goto done; + } + break; + default: + ret = EINVAL; + goto done; + } + + ret = 0; + +done: + if (ret == 0) { + *_res = res; + } else { + free(res); + } + + return ret; +} + +int pack_response(struct extdom_res *res, struct berval **ret_val) +{ + BerElement *ber = NULL; + int ret; + +/* We send to follwing response: + * ExtdomResponseValue ::= SEQUENCE { + * responseType ENUMERATED { + * sid (1), + * name (2), + * posix_user (3), + * posix_group (4) + * }, + * data OutputData + * } + * + * OutputData ::= CHOICE { + * sid OCTET STRING, + * name NameDomainData, + * user PosixUser, + * group PosixGroup + * } + * + * 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 + * } + */ + + ber = ber_alloc_t( LBER_USE_DER ); + if (ber == NULL) { + return LDAP_OPERATIONS_ERROR; + } + + switch (res->response_type) { + case RESP_SID: + ret = ber_printf(ber,"{es}", res->response_type, res->data.sid); + break; + case RESP_NAME: + ret = ber_printf(ber,"{e{ss}}", res->response_type, + res->data.name.domain_name, + res->data.name.object_name); + break; + case RESP_USER: + ret = ber_printf(ber,"{e{ssii}}", res->response_type, + res->data.user.domain_name, + res->data.user.user_name, + res->data.user.uid, + res->data.user.gid); + break; + case RESP_GROUP: + ret = ber_printf(ber,"{e{ssi}}", res->response_type, + res->data.group.domain_name, + res->data.group.group_name, + res->data.group.gid); + break; + default: + ber_free(ber, 1); + return LDAP_OPERATIONS_ERROR; + } + + if (ret == -1) { + ber_free(ber, 1); + return LDAP_OPERATIONS_ERROR; + } + + ret = ber_flatten(ber, ret_val); + if (ret == -1) { + ber_free(ber, 1); + return LDAP_OPERATIONS_ERROR; + } + + ber_free(ber, 1); + + return LDAP_SUCCESS; +} |