/* * gums.c * * Copyright (c) 2004 The Regents of the University of Michigan. * All rights reserved. * * Olga Kornievskaia * * 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 #include #include #include #include #include #include #include #include #include "nfsidmap.h" #include "nfsidmap_internal.h" #include "cfg.h" #include #include #include #include #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 = conf_get_str("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