blob: 1d6eb3185e11f9b76a98af2ea0594ae4978309d0 [file]
/*
* 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, &params);
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