diff options
Diffstat (limited to '')
-rw-r--r-- | support/nfsidmap/gums.c | 787 |
1 files changed, 787 insertions, 0 deletions
diff --git a/support/nfsidmap/gums.c b/support/nfsidmap/gums.c new file mode 100644 index 0000000..1d6eb31 --- /dev/null +++ b/support/nfsidmap/gums.c @@ -0,0 +1,787 @@ +/* + * gums.c + * + * Copyright (c) 2004 The Regents of the University of Michigan. + * All rights reserved. + * + * Olga Kornievskaia <aglo@umich.edu> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <pwd.h> +#include <grp.h> +#include <err.h> +#include <syslog.h> +#include "nfsidmap.h" +#include "nfsidmap_plugin.h" + +#include <voms_apic.h> + +#include <prima_logger.h> +#include <prima_soap_client.h> +#include <prima_saml_support.h> + +#define DEFAULT_PRIMA_CONF_LOCATION "/etc/grid-security/prima-authz.conf" +#define DEFAULT_VOMSDIR "/etc/grid-security/vomsdir" +#define DEFAULT_CADIR "/etc/grid-security/certificates" +#define X509_DN_SIZE 1024 + +//#define DEBUG_PRINT_VOMS + +#define USING_TEST_PROGRAM +#ifdef USING_TEST_PROGRAM +nfs4_idmap_log_function_t idmap_log_func = printf; +int idmap_verbosity = 10; +#endif + +/* + * GUMS Translation Methods + * + */ + +/* global variables. voms/gums configuration attributes*/ +static char prima_conf[] = DEFAULT_PRIMA_CONF_LOCATION; +typedef struct _plugin_config_params { + char *saml_schema_dir; + int saml_log_level; + char *server_cert; + char *server_key; + char *ca_dir; + char *gums_server_location; + char *voms_dir; +} plugin_config_params; +plugin_config_params conf; + +#ifdef VOMS_BUG +static void my_VOMS_Delete(struct voms *v) +{ + int i; + + if (!v) return; + if (v->user) + free(v->user); + if (v->server) + free(v->server); + if (v->fqan) { + for (i = 0; v->fqan[i] != NULL; i++) + free(v->fqan[i]); + free(v->fqan); + } + free(v); +} + +static struct voms *my_VOMS_Copy(struct voms *v, int *err) +{ + struct voms *cv; + int i; + + cv = calloc(1, sizeof(struct voms)); + if (cv == NULL) + goto out; + cv->user = strdup(v->user); + if (cv->user == NULL) + goto out; + cv->server = strdup(v->server); + if (cv->server == NULL) + goto out; + for (i = 0; v->fqan[i] != NULL; i++) { + if (v->fqan[i] == NULL) + break; + } + cv->fqan = calloc(i+1, sizeof(char *)); + if (cv->fqan == NULL) + goto out; + cv->fqan[i] = NULL; + for (i = 0; v->fqan[i] != NULL; i++) { + cv->fqan[i] = strdup(v->fqan[i]); + if (cv->fqan[i] == NULL) + goto out; + } + return cv; +out: + if (cv) + my_VOMS_Delete(cv); + + return NULL; +} +#endif + + +#ifdef DEBUG_PRINT_VOMS +void printvoms(struct voms *v) +{ + int j; + + printf("SIGLEN: %d\nUSER: %s\n", v->siglen, v->user); + printf("UCA: %s\nSERVER: %s\n", v->userca, v->server); + printf("SCA: %s\nVO: %s\n", v->serverca, v->voname); + printf("URI: %s\nDATE1: %s\n", v->uri, v->date1); + printf("DATE2: %s\n", v->date2); + + switch (v->type) { + case TYPE_NODATA: + printf("NO DATA\n"); + break; + case TYPE_CUSTOM: + printf("%*s\n", v->datalen - 10, v->custom); + break; + case TYPE_STD: + j = 0; + while (v->std[j]) { + printf("GROUP: %s\nROLE: %s\nCAP: %s\n",v->std[j]->group, + v->std[j]->role,v->std[j]->cap); + j++; + } + } +} + +void print(struct vomsdata *d) +{ + struct voms **vo = d->data; + struct voms *v; + int k = 0; + + while(vo[k]) { + v = vo[k++]; + printf("%d *******************************************\n",k); + printvoms(v); + } + + if (d->workvo) + printf("WORKVO: %s\n", d->workvo); + + if (d->extra_data) + printf("EXTRA: %s\n", d->extra_data); +} +#endif + +static void free_plugin_config_params() +{ + if (conf.saml_schema_dir) + free(conf.saml_schema_dir); + conf.saml_schema_dir = NULL; + if (conf.server_cert) + free(conf.server_cert); + conf.server_cert = NULL; + if (conf.server_key) + free(conf.server_key); + conf.server_key = NULL; + if (conf.ca_dir) + free(conf.ca_dir); + conf.ca_dir = NULL; + if (conf.voms_dir) + free(conf.voms_dir); + conf.voms_dir = NULL; +} + +static int validate_plugin_config_params() +{ + if (conf.saml_schema_dir == NULL || + conf.server_cert == NULL || + conf.server_key == NULL || + conf.gums_server_location == NULL) + return -1; + + if (conf.ca_dir == NULL) { + conf.ca_dir = strdup(DEFAULT_CADIR); + if (conf.ca_dir == NULL) + return -1; + } + if (conf.voms_dir == NULL) { + conf.voms_dir = strdup(DEFAULT_VOMSDIR); + if (conf.voms_dir == NULL) + return -1; + } + return 0; +} + +static int gums_init(void) +{ + FILE *f = NULL; + int ret = -1, i = 0; + char buf[512], type[128], value[256]; + char *alt_conf = NULL; + + alt_conf = nfsidmap_config_get("GUMS", "Conf_File"); + if (alt_conf == NULL) + f = fopen(prima_conf, "r"); + else + f = fopen(alt_conf, "r"); + if (f == NULL) + goto out; + + while (fgets(buf, 512, f)) { + i = 0; + while(buf[i] == ' ' || buf[i] == '\t') + i++; + if (buf[i] == '#' || buf[i] == '\0' || buf[i] == '\n') + continue; + if (sscanf(&buf[i], "%127s%255s",type,value) < 2) { + IDMAP_LOG(0, ("ERROR: malformed line: %s\n", &buf[i])); + goto out; + } + IDMAP_LOG(1, ("PRIMA conf: type=%s value=%s\n", type, value)); + if (strncmp(type, "imsContact", 10) == 0) { + conf.gums_server_location = strdup(value); + } else if (strncmp(type, "serviceCert", 11) == 0) { + conf.server_cert = strdup(value); + } else if (strncmp(type, "serviceKey", 10) == 0) { + conf.server_key = strdup(value); + } else if (strncmp(type, "caCertDir", 9) == 0) { + conf.ca_dir = strdup(value); + } else if (strncmp(type, "samlSchemaDir", 13) == 0) { + conf.saml_schema_dir = strdup(value); + } else if (strncmp(type, "logLevel", 8) == 0) { + if (strncmp(value, "debug", 5) == 0) + conf.saml_log_level = PRIMA_LOG_DEBUG; + else if (strncmp(value, "error", 5) == 0) + conf.saml_log_level = PRIMA_LOG_ERROR; + else if (strncmp(value, "none", 4) == 0) + conf.saml_log_level = PRIMA_LOG_NONE; + else + conf.saml_log_level = PRIMA_LOG_INFO; + } + } + + if (validate_plugin_config_params() != 0) + goto out; + + ret = 0; +out: + if (f) + fclose(f); + if (ret) + free_plugin_config_params(); + + return ret; +} + +static int retrieve_attributes(X509 *cert, STACK_OF(X509) *cas, + struct voms **attrs) +{ + int ret = -1, err = 0; + struct vomsdata *vd = NULL; + + vd = VOMS_Init(conf.voms_dir, conf.ca_dir); + if (vd == NULL) { + IDMAP_LOG(0, ("VOMS_Init failed\n")); + return -1; + } + ret = VOMS_Retrieve(cert, cas, RECURSE_CHAIN, vd, &err); + if (err) { + char *err_msg; + err_msg = VOMS_ErrorMessage(vd, err, NULL, 0); + if (err == VERR_NOEXT) + ret = 0; + else + IDMAP_LOG(0, ("VOMS error %s\n", err_msg)); + goto out; + } else if (ret) { + struct voms *v, *v2; +#ifdef DEBUG_PRINT_VOMS + print(vd); +#endif + v = VOMS_DefaultData(vd, &err); + if (err == VERR_NONE) { +#ifdef DEBUG_PRINT_VOMS + printvoms(v); + while (v->fqan[i] != NULL) + IDMAP_LOG(1, ("user's fqan: %s\n", v->fqan[i++])); +#endif +#ifdef VOMS_BUG + v2 = my_VOMS_Copy(v, &err); +#else + v2 = VOMS_Copy(v, &err); +#endif + if (v2 == NULL) { + IDMAP_LOG(0, ("VOMS_Copy failed err=%d\n", err)); + goto out; + } + *attrs = v2; + } + } + ret = 0; +out: + if (vd) + VOMS_Destroy(vd); + return ret; +} + +static int get_server_dn(unsigned char **server_dn) +{ + BIO *tmp = NULL; + X509 *cert = NULL; + int ret = -1; + char dn[X509_DN_SIZE]; + + tmp = BIO_new(BIO_s_file()); + if (tmp == NULL) + goto out; + + ret = BIO_read_filename(tmp, conf.server_cert); + if (ret == 0) { + ret = errno; + goto out; + } + + cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL); + if (cert == NULL) + goto out; + + X509_NAME_oneline(X509_get_subject_name(cert), dn, sizeof(dn)); + + *server_dn = strdup(dn); + if (*server_dn == NULL) + goto out; + + ret = 0; +out: + if (tmp) + BIO_free(tmp); + if (cert) + X509_free(cert); + + return ret; +} + +static int create_saml_request(char *dn, struct voms *attrs, char **saml_req) +{ + int ret = -1, i; + char *req = NULL; + unsigned char *server_dn = NULL; + prima_saml_fqans fqans; + + IDMAP_LOG(2, ("create_saml_request start\n")); + ret = initPrimaSAMLFQANs(&fqans); + if (ret) { + IDMAP_LOG(0, ("initPrimaSAMLFQANs failed with %d\n", ret)); + goto out; + } + + if (attrs) { + for (i = 0; attrs->fqan[i] != NULL; i++) { + ret = addPrimaSAMLFQAN(&fqans, attrs->server, attrs->fqan[i]); + IDMAP_LOG(1, ("addPrimaSAMLFQAN returned %d\n", ret)); + } + dn = attrs->user; + } else + IDMAP_LOG(1, ("No VOMS attributes present in the cert\n")); + + if (get_server_dn(&server_dn) != 0) + goto out; + req = createSAMLQueryAndRequest(server_dn, dn, &fqans); + if (req == NULL) { + IDMAP_LOG(0, ("createSAMLQueryAndRequest failed to create " + "SAML request\n")); + goto out; + } + IDMAP_LOG(1, ("SAML Request %s\n", req)); + + ret = 0; + *saml_req = req; +out: + cleanupPrimaSAMLFQANs(&fqans); + + if (server_dn) + free(server_dn); + + IDMAP_LOG(2, ("create_saml_request returning %d\n", ret)); + return ret; +} + +static int process_parameters(extra_mapping_params **ex, X509 **user_cert, + STACK_OF(X509) **user_chain) +{ + + int ret = -1, i; + X509 *cert = NULL, *x; + STACK_OF(X509) *chain = NULL; + unsigned char *p; + + if (ex[0]->content_type != X509_CERT) + return -1; + + /* get user's x509 certificate */ + p = ex[0]->content; + cert = d2i_X509(NULL, &p, ex[0]->content_len); + if (cert == NULL) + goto out; + + /* get user's other certificates */ + chain = sk_X509_new_null(); + if (chain == NULL) + goto out; + for (i = 1; ex[i] != NULL; i++) { + if (ex[i]->content_type != X509_CERT) + continue; + p = ex[i]->content; + x = d2i_X509(NULL, &p, ex[i]->content_len); + if (x == NULL) + goto out; + sk_X509_push(chain, x); + } + ret = 0; + + *user_cert = cert; + *user_chain = chain; +out: + if (ret) { + int num; + if (cert) + X509_free(cert); + if (chain) + sk_X509_pop_free(chain, X509_free); + } + + return ret; +} + +struct pwbuf { + struct passwd pwbuf; + char buf[1]; +}; + +static int translate_to_uid(char *local_uid, uid_t *uid, uid_t *gid) +{ + int ret = -1; + struct passwd *pw = NULL; + struct pwbuf *buf = NULL; + size_t buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + + buf = malloc(sizeof(*buf) + buflen); + if (buf == NULL) + goto out; + + ret = getpwnam_r(local_uid, &buf->pwbuf, buf->buf, buflen, &pw); + if (pw == NULL) { + IDMAP_LOG(0, ("getpwnam: name %s not found\n", local_uid)); + goto out; + } + *uid = pw->pw_uid; + *gid = pw->pw_gid; + + ret = 0; +out: + if (buf) + free(buf); + return ret; +} + +static int translate_to_gid(char *local_gid, uid_t *gid) +{ + struct group *gr = NULL; + struct group grbuf; + char *buf = NULL; + size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); + int ret = -1; + + do { + buf = malloc(buflen); + if (buf == NULL) + goto out; + + ret = -getgrnam_r(local_gid, &grbuf, buf, buflen, &gr); + if (gr == NULL && !ret) + ret = -ENOENT; + if (ret == -ERANGE) { + buflen *= 2; + free(buf); + } + } while (ret == -ERANGE); + + if (ret) + goto out; + + *gid = gr->gr_gid; + + ret = 0; +out: + if (buf) + free(buf); + return ret; +} + +static int gums_gss_princ_to_ids(char *secname, char *princ, + uid_t *uid, uid_t *gid, + extra_mapping_params **ex) +{ + int ret = -1, size, i; + X509 *cert = NULL; + STACK_OF(X509) *cas = NULL; + char dn[X509_DN_SIZE]; + struct voms *attrs = NULL; + char *saml_req = NULL, *saml_resp = NULL; + int saml_result; + char *local_uid = NULL, *local_gid = NULL, *p; + + /* accept only spkm3 translations */ + if (strcmp(secname, "spkm3")) + return -EINVAL; + + /* must supply either a DN and/or at least 1 binary blob */ + if (princ == NULL && (ex == NULL || (ex && ex[0] == NULL))) + return -EINVAL; + + /* process extra parameters */ + if (process_parameters(ex, &cert, &cas) != 0) + goto out; + + IDMAP_LOG(1, ("Processing name translation of client\n")); + X509_NAME_oneline(X509_get_subject_name(cert), dn, sizeof(dn)); + IDMAP_LOG(1, ("DN=%s\n", dn)); + size = sk_X509_num(cas); + IDMAP_LOG(1, ("Including following CAs (%d)\n", size)); + for (i=0; i < size; i++) { + X509_NAME_oneline(X509_get_subject_name(sk_X509_value(cas, i)), + dn, sizeof(dn)); + IDMAP_LOG(1, ("DN=%s\n", dn)); + } + + /* retrieve VOMS attributes */ + if (retrieve_attributes(cert, cas, &attrs) != 0) + goto out; + if (attrs == NULL) + X509_NAME_oneline(X509_get_subject_name(cert), dn, sizeof(dn)); + + /* initialize SAML library */ + if (initPrimaSAMLSupport(conf.saml_schema_dir, + conf.saml_log_level) != 0) { + IDMAP_LOG(0, ("initPrimaSAMLSupport failed\n")); + goto out; + } + + /* create SAML request */ + if (create_saml_request(dn, attrs, &saml_req) != 0) + goto out; + + /* contact GUMS server */ + saml_resp = queryIdentityMappingService(conf.gums_server_location, + saml_req, conf.server_cert, conf.server_key, + conf.ca_dir); + if (saml_resp != NULL) { + saml_result = processResponse(saml_resp, saml_req, &local_uid, + &local_gid); + IDMAP_LOG(1, ("processResponse returned %d\n", saml_result)); + if (saml_result || local_uid == NULL) { + IDMAP_LOG(0, ("processResponse failed to return " + "local id\n")); + ret = -ENOENT; + goto out; + } + IDMAP_LOG(1, ("GUMS returned uid=%s gid=%s\n", local_uid, + local_gid)); + } + + /* translate account name to uid */ + if (translate_to_uid(local_uid, uid, gid)) + goto out; + if (local_gid) + if (translate_to_gid(local_gid, gid)) + goto out; + + ret = 0; +out: + if (cert) + X509_free(cert); + + if (cas) + sk_X509_pop_free(cas, X509_free); + + if (attrs) +#ifdef VOMS_BUG + my_VOMS_Delete(attrs); +#else + VOMS_Delete(attrs); +#endif + + if (saml_req) + free(saml_req); + + if (saml_resp) + free(saml_resp); + + cleanupPrimaSAMLSupport(); + + return ret; +} + +struct trans_func gums_trans = { + .name = "gums", + .init = gums_init, + .princ_to_ids = gums_gss_princ_to_ids, + .name_to_uid = NULL, + .name_to_gid = NULL, + .uid_to_name = NULL, + .gid_to_name = NULL, + .gss_princ_to_grouplist = NULL, +}; + +struct trans_func *libnfsidmap_plugin_init() +{ + return (&gums_trans); +} + +#ifdef USING_TEST_PROGRAM +static STACK_OF(X509) *load_chain(char *certfile) +{ + STACK_OF(X509_INFO) *sk=NULL; + STACK_OF(X509) *stack=NULL, *ret=NULL; + BIO *in=NULL; + X509_INFO *xi; + int first = 1; + + if (!(stack = sk_X509_new_null())) { + printf("memory allocation failure\n"); + goto end; + } + + if (!(in=BIO_new_file(certfile, "r"))) { + printf("error opening the file, %s\n",certfile); + goto end; + } + + /* This loads from a file, a stack of x509/crl/pkey sets */ + if (!(sk=(STACK_OF(X509_INFO) *)PEM_X509_INFO_read_bio(in,NULL,NULL,NULL))) { + /* if (!(sk=PEM_X509_read_bio(in,NULL,NULL,NULL))) { */ + printf("error reading the file, %s\n",certfile); + goto end; + } + + /* scan over it and pull out the certs */ + while (sk_X509_INFO_num(sk)) { + /* skip first cert */ + if (first) { + xi=sk_X509_INFO_shift(sk); + X509_INFO_free(xi); + first = 0; + continue; + } + xi=sk_X509_INFO_shift(sk); + if (xi->x509 != NULL) { + sk_X509_push(stack,xi->x509); + xi->x509=NULL; + } + X509_INFO_free(xi); + } + if (!sk_X509_num(stack)) { + printf("no certificates in file, %s\n",certfile); + sk_X509_free(stack); + goto end; + } + ret=stack; +end: + BIO_free(in); + sk_X509_INFO_free(sk); + return(ret); +} + +void create_params(X509 *cert, STACK_OF(X509) *cas, + extra_mapping_params ***ret_params) +{ + int len = 0, i, size = 0; + unsigned char *p, *buf = NULL; + extra_mapping_params **params = NULL; + X509 *x; + + if (cas) + size = sk_X509_num(cas); + params = malloc((size+2)*sizeof(extra_mapping_params *)); + params[size+1] = NULL; + + /* 1st element is user's certificate */ + len = i2d_X509(cert, NULL); + p = buf = malloc(len); + i2d_X509(cert, &p); + params[0] = malloc(sizeof(extra_mapping_params)); + params[0]->content_type = X509_CERT; + params[0]->content = buf; + params[0]->content_len = len; + + /* add other certificates to the array */ + for (i = 0; i < size; i++) { + x = sk_X509_value(cas, i); + params[i+1] = malloc(sizeof(extra_mapping_params)); + len = i2d_X509(x, NULL); + p = buf = malloc(len); + i2d_X509(x, &p); + params[i+1]->content_type = X509_CERT; + params[i+1]->content = buf; + params[i+1]->content_len = len; + } + *ret_params = params; +} + +int main(void) +{ + int uid, gid, ret, i; + extra_mapping_params **params = NULL; + BIO *tmp = NULL; + X509 *cert = NULL, *x; + STACK_OF(X509) *cas = NULL; + unsigned char *proxy_file; + + if (gums_init()) + return -1; + proxy_file = getenv("X509_USER_PROXY"); + if (proxy_file == NULL) { + fprintf(stderr, "X509_USER_PROXY is not set\n"); + return -1; + } + tmp = BIO_new(BIO_s_file()); + BIO_read_filename(tmp, proxy_file); + cert = (X509 *) PEM_read_bio_X509(tmp, NULL, NULL, NULL); + cas = load_chain(proxy_file); + create_params(cert, cas, ¶ms); + ret = gums_gss_princ_to_ids("spkm3", NULL, &uid, &gid, params); + fprintf(stderr, "gums_gss_princ_to_ids returns %d uid=%d gid=%d\n", + ret, uid, gid); + + if (tmp) + BIO_free(tmp); + if (cert) + X509_free(cert); + if (cas) + sk_X509_pop_free(cas, X509_free); + + free_plugin_config_params(); + + if (params) { + for (i=0; params[i] != NULL; i++) { + free(params[i]->content); + free(params[i]); + } + free(params); + } + + return 0; +} +#endif |