diff --git a/configure.ac b/configure.ac index ad9d53d..c620e39 100644 --- a/configure.ac +++ b/configure.ac @@ -75,6 +75,9 @@ AC_CHECK_LIB([oauth], [oauth_init_nss], [AC_MSG_ERROR([Missing required oauth lib])]) AC_SUBST([OAUTH_LIB]) +dnl XXX Copy how -loauth is done above +LIBS="$LIBS -lgssapi_krb5" + PKG_CHECK_MODULES([HAIL],[libhail >= 0.8]) AC_SUBST([HAIL_LIBS]) AC_SUBST([HAIL_CFLAGS]) @@ -105,6 +108,12 @@ AC_DEFINE_UNQUOTED([JANSSON_LOAD_FLAG], $iw_cv_func_jansson_flag, AC_CHECK_HEADER([oauth.h], , [AC_MSG_ERROR([Missing OAuth development library: liboauth-devel])]) +AC_CHECK_HEADER([gssapi.h], [], + [AC_MSG_ERROR([Missing gssapi.h header])]) +dnl Maybe we should use setenv("KRB5_KTNAME",...) after all? That always works. +AC_CHECK_HEADER([gssapi/gssapi_krb5.h], [], + [AC_MSG_ERROR([Missing gssapi/gssapi_krb5.h header])]) + # from http://www.gnu.org/software/autoconf-archive/ AX_BOOST_BASE AX_BOOST_SYSTEM diff --git a/rest.c b/rest.c index 1aca0af..5f1cf1e 100644 --- a/rest.c +++ b/rest.c @@ -36,8 +36,11 @@ #include #include /* only for ARRAY_SIZE at this point */ #include +#include +#include #include +#include "base64.h" #include "configmake.h" /* for LOCALEDIR */ #include "dirname.h" #include "iwh.h" @@ -121,6 +124,68 @@ static pthread_mutex_t my_lock = PTHREAD_MUTEX_INITIALIZER; static const char *log_name; static char my_hostname[HOST_NAME_MAX+1]; static bool oauth = false; +static bool kerberos = false; +/* XXX Make keytab location to follow --prefix. */ +static const char *krb_keytab = "/etc/iwhd/krb.keytab"; +static const char *krb_svc_name = "IWHD"; +#if 0 /* RFC says use default */ +static gss_cred_id_t gss_creds; /* struct* */ /* XXX needed? */ +#endif + +static void log_gss_error(const char *msg, unsigned int gss_min) +{ + char buf[700]; + int len, rem; + int rc; + unsigned int gss_maj, dummy_min; + unsigned int msg_ctx = 0; + gss_buffer_desc gss_str; + + rem = sizeof(buf); + len = 0; + buf[0] = 0; + + rc = snprintf(buf+len, rem, "%s", msg); + if (rc >= rem) + goto out; + rem -= rc; + len += rc; + + /* + * See RFC-2744 clause 5.11. However, the practical code is different. + * - Everyone relies on the returned string nul-terminated, + * while RFC uses gss_buf.length. + * - Some people parse major codes (using different mech_type), + * while RFC gives no hint how. + */ + do { + gss_maj = gss_display_status(&dummy_min, gss_min, + GSS_C_MECH_CODE, GSS_C_NO_OID, + &msg_ctx, &gss_str); + if (gss_maj != GSS_S_COMPLETE) { + /* + * If we put "unknown" here, someone may localize it. + */ + rc = snprintf(buf+len, rem, ": UNK"); + if (rc >= rem) + goto out; + rem -= rc; + len += rc; + goto out; + } + + rc = snprintf(buf+len, rem, ": %s", (char*)gss_str.value); + if (rc >= rem) + goto out; + rem -= rc; + len += rc; + + gss_release_buffer(&dummy_min, &gss_str); + } while(msg_ctx); + out: + /* there's always a nul at buf[len] */ + log_msg(0, "%s\n", buf); +} static void conn_to_client(struct MHD_Connection *conn, char *buf, size_t len) @@ -266,6 +331,13 @@ do_resp (struct MHD_Connection *conn, my_state *ms, return MHD_NO; } + /* + * This special-casing seems asking for some kind of a generic header + * list, but every time we try to write it, it comes out even worse. + */ + if (ms && ms->auth_hdr) { + MHD_add_response_header(resp,"WWW-Authenticate",ms->auth_hdr); + } if (etag) { MHD_add_response_header(resp,"ETag",etag); } @@ -291,6 +363,9 @@ do_resp_from_cb (struct MHD_Connection *conn, my_state *ms, simple_closer(ms); return MHD_NO; } + if (ms->auth_hdr) { + MHD_add_response_header(resp,"WWW-Authenticate",ms->auth_hdr); + } MHD_queue_response(conn,MHD_HTTP_OK,resp); MHD_destroy_response(resp); ms->rc = MHD_HTTP_OK; @@ -495,9 +570,11 @@ proxy_get_data (void *cctx, struct MHD_Connection *conn, const char *url, child_closer(pp); return MHD_NO; } + if (ms->auth_hdr) { + MHD_add_response_header(resp,"WWW-Authenticate",ms->auth_hdr); + } MHD_queue_response(conn,ms->rc,resp); MHD_destroy_response(resp); - return MHD_YES; } @@ -1373,6 +1450,9 @@ control_api_root (void *cctx, struct MHD_Connection *conn, const char *url, if (!resp) { return MHD_NO; } + if (ms->auth_hdr) { + MHD_add_response_header(resp,"WWW-Authenticate", ms->auth_hdr); + } MHD_queue_response(conn,rc,resp); MHD_destroy_response(resp); ms->rc = rc; @@ -2431,6 +2512,170 @@ auth_hdr_split (int *pargc, char ***pargv, const char *val) return true; } +static bool +nego_token (char **pbuf, size_t *plen, const char *val) +{ + char *buf; + size_t len; + const char *p; + bool rcb; + + if (!(p = auth_type_check(val, "Negotiate"))) + return false; + + len = strlen(p); + if (len == 0) { + return false; + } + + /* actually should be 3/4, but whatever */ + buf = malloc(len); + if (!buf) + return false; // or maybe just abort()? + + rcb = base64_decode(p, len, buf, &len); + if (!rcb) { + // free(buf); + return false; + } + + *pbuf = buf; + *plen = len; + return true; +} + +static int +authenticate (struct MHD_Connection *conn, my_state *ms) +{ + const char *val; + char *buf; + char *p; + size_t len; + unsigned int gss_maj, gss_min, dummy_min; + gss_ctx_id_t gss_ctx = GSS_C_NO_CONTEXT; + gss_buffer_desc gss_itok; + gss_buffer_desc gss_otok = GSS_C_EMPTY_BUFFER; + gss_name_t gss_cli_name = GSS_C_NO_NAME; + gss_buffer_desc gss_nbuf; + gss_cred_id_t gss_dcred = GSS_C_NO_CREDENTIAL; + struct MHD_Response *resp; + unsigned int gss_flags; + bool rcb; + int ret; + + val = MHD_lookup_connection_value(conn,MHD_HEADER_KIND,"Authorization"); + if (!val) { + resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO); + if (!resp) { + return -1; + } + MHD_add_response_header(resp,"WWW-Authenticate","Negotiate"); + MHD_queue_response(conn,MHD_HTTP_UNAUTHORIZED,resp); + MHD_destroy_response(resp); + ms->rc = MHD_HTTP_UNAUTHORIZED; + return 1; + } + + rcb = nego_token(&buf, &gss_itok.length, val); + if (!rcb) { + do_resp(conn,ms,MHD_HTTP_BAD_REQUEST,NULL,"400\r\n"); + return 1; + } + gss_itok.value = buf; /* gcc */ + + gss_maj = gss_accept_sec_context(&gss_min, + &gss_ctx, + GSS_C_NO_CREDENTIAL, + &gss_itok, + GSS_C_NO_CHANNEL_BINDINGS, + &gss_cli_name, + NULL, + &gss_otok, + &gss_flags, + NULL, + &gss_dcred); + if (gss_otok.length) { + len = ((gss_otok.length+2)/3) * 4; + len += sizeof("Negotiate ")-1; + buf = malloc(len + 1); + if (!buf) { + do_resp(conn,ms,MHD_HTTP_INTERNAL_SERVER_ERROR, + NULL,NULL); + ret = 1; + goto out; + } + strcpy(buf, "Negotiate "); + base64_encode(gss_otok.value, gss_otok.length, + buf + (sizeof("Negotiate ")-1), + len - (sizeof("Negotiate ")-1)); + buf[len] = 0; /* base64_encode zero-pads if smaller */ + + /* GC frees the previous one, right? */ + ms->auth_hdr = buf; + } + if (GSS_ERROR(gss_maj)) { + if (verbose) { + log_gss_error("gss_accept_sec_context", gss_min); + } + do_resp(conn,ms,MHD_HTTP_FORBIDDEN,NULL,"403\r\n"); + ret = 1; + goto out; + } + + if (gss_maj & GSS_S_CONTINUE_NEEDED) { + /* Continuations are impossible in Kerberos, right? */ + error (EXIT_FAILURE, 0, _("gss_accept_sec_context continuing")); + } + + gss_maj = gss_display_name(&gss_min, gss_cli_name, &gss_nbuf, NULL); + if (GSS_ERROR(gss_maj)) { + /* + * We authenticated successfuly, but if we cannot get the + * client name, it is no use: we will not be able to match. + * Have to refuse this. Return 500 for now, this is a problem. + */ + log_gss_error("gss_display_name error", gss_min); + ret = -1; + goto out; + } + p = memchr((char*)gss_nbuf.value,'@',gss_nbuf.length); + if (p) { + buf = strndup((char*)gss_nbuf.value,p-(char*)gss_nbuf.value); + if (!buf) { + ret = -1; + goto out; + } + } + else { + buf = strndup((char*)gss_nbuf.value,gss_nbuf.length); + if (!buf) { + ret = -1; + goto out; + } + } + ms->auth_user = buf; + DPRINTF("auth user %s\n", buf); + + ret = 0; + out: + if (gss_nbuf.length != 0) { + gss_release_buffer(&dummy_min, &gss_nbuf); + } + if (gss_otok.length != 0) { + gss_release_buffer(&dummy_min, &gss_otok); + } + if (gss_ctx != GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&dummy_min, &gss_ctx, GSS_C_NO_BUFFER); + } + if (gss_dcred != GSS_C_NO_CREDENTIAL) { + gss_release_cred(&dummy_min, &gss_dcred); + } + if (gss_cli_name != GSS_C_NO_NAME) { + gss_release_name(&dummy_min, &gss_cli_name); + } + return ret; +} + /* Extract the value from key="val". */ static char * oauth_param_val(const char *arg) @@ -2692,6 +2937,13 @@ access_handler_0 (void *cctx, struct MHD_Connection *conn, const char *url, if (ms->state == MS_NEW) log_http_request(conn, method, url); + if (kerberos) { + rc = authenticate(conn, ms); + if (rc < 0) + goto out_no; + if (rc > 0) + return MHD_YES; + } if (oauth) { rc = do_oauth(conn,ms,url,method); if (rc < 0) @@ -2740,6 +2992,13 @@ access_handler_0 (void *cctx, struct MHD_Connection *conn, const char *url, pthread_mutex_lock(&my_lock); LIST_INSERT_HEAD(&my_states,ms,gc_link); pthread_mutex_unlock(&my_lock); + if (kerberos) { + rc = authenticate(conn,ms); + if (rc < 0) + goto out_no; + if (rc > 0) + return MHD_YES; + } if (oauth) { rc = do_oauth(conn,ms,url,method); if (rc < 0) @@ -2825,9 +3084,12 @@ static const struct option my_options[] = { { "config", required_argument, NULL, 'c' }, { "db", required_argument, NULL, 'd' }, { "logfile", required_argument, NULL, 'l' }, + { "kerberos", no_argument, NULL, 'k' }, { "master", required_argument, NULL, 'm' }, + { "krbsvcnm", required_argument, NULL, 'n' }, { "oauth", no_argument, NULL, 'o' }, { "port", required_argument, NULL, 'p' }, + { "krbkeytab", required_argument, NULL, 't' }, { "usercred", required_argument, NULL, 'U' }, { "userlist", required_argument, NULL, 'u' }, { "verbose", no_argument, NULL, 'v' }, @@ -2856,9 +3118,12 @@ A configuration file must be specified.\n\ -c, --config=FILE config file [required]\n\ -d, --db=HOST_PORT database server as ip[:port]\n\ -l, --logfile=FILE logfile (default stdout/stderr)\n\ + -k, --kerberos enable Kerberos\n\ -m, --master=HOST_PORT master (upstream) server as ip[:port]\n\ + -n, --krbsvcnm=NAME Kerberos service name (default IWHD)\n\ -o, --oauth enable OAuth\n\ -p, --port=PORT alternate listen port (default 9090)\n\ + -t, --krbkeytab=FILE location of Kerberos keytab\n\ -u, --userlist=FILE list of user names, secrets (0600 permissions)\n\ -v, --verbose verbose/debug output\n\ \n\ @@ -2879,6 +3144,79 @@ Report %s bugs to %s.\n\ exit (status); } +static void gss_init(void) +{ + char *gss_sname; + gss_buffer_desc gss_nbuf; /* struct */ + unsigned int gss_maj, gss_min, dummy_min; + gss_name_t gss_iname; /* struct* */ + int rc; + + gss_maj = krb5_gss_register_acceptor_identity(krb_keytab); + if (gss_maj != 0) + error (EXIT_FAILURE, 0, _("cannot set keytab %s"), krb_keytab); + + if (gethostname(my_hostname, HOST_NAME_MAX) != 0) + error (EXIT_FAILURE, errno, _("gethostname")); + my_hostname[HOST_NAME_MAX] = 0; + + if (strchr(krb_svc_name, '@') != NULL || + strchr(krb_svc_name, '/') != NULL) { + gss_sname = strdup(krb_svc_name); + if (!gss_sname) + error (EXIT_FAILURE, 0, _("out of memory")); + } + else { + rc = asprintf(&gss_sname, "%s@%s", krb_svc_name, my_hostname); + if (rc < 0) + error (EXIT_FAILURE, 0, _("out of memory")); + } + + gss_nbuf.length = strlen(gss_sname)+1; + gss_nbuf.value = gss_sname; + + gss_maj = gss_import_name(&gss_min, &gss_nbuf, + GSS_C_NT_HOSTBASED_SERVICE, &gss_iname); + if (GSS_ERROR(gss_maj)) { + log_gss_error("gss_import_name error", gss_min); + exit(EXIT_FAILURE); + } + + /* + * Why re-import only to print? We just exported the gss_sname, right? + * Well, who knows, maybe something went pear-shaped. + * Not sure what to do with the type OID though, so just NULL it. + */ + if (verbose) { + gss_maj = gss_display_name(&gss_min, gss_iname, &gss_nbuf,NULL); + if (GSS_ERROR(gss_maj)) { + log_gss_error("gss_display_name error", gss_min); + exit(EXIT_FAILURE); + } + printf("kerberos service: %s\n", (char*)gss_nbuf.value); + gss_release_buffer(&dummy_min, &gss_nbuf); + } + +#if 0 /* RFC says use default */ + /* + * The GSS_C_INDEFINITE does not yield a credential with an + * indefinite lifetime, merely "the maximum permitted lifetime". + * So, this function has to be called from the main loop somewhere. XXX + * Then, we need to call gss_release_cred(), too. + */ + gss_maj = gss_acquire_cred(&gss_min, gss_iname, GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, GSS_C_ACCEPT, &gss_creds, + NULL, NULL); + if (GSS_ERROR(gss_maj)) { + log_gss_error("gss_acquire_cred error", gss_min); + exit(EXIT_FAILURE); + } +#endif + + gss_release_name(&dummy_min, &gss_iname); + // free(gss_sname); -- don't crash GC +} + static void oauth_init(void) { if (gethostname(my_hostname, HOST_NAME_MAX) != 0) @@ -2958,7 +3296,7 @@ main (int argc, char **argv) db_host = xstrdup ("localhost"); for (;;) - switch (getopt_long(argc,argv,"ac:d:l:m:op:u:U:v",my_options,NULL)) + switch (getopt_long(argc,argv,"ac:d:kl:m:n:op:t:u:U:v",my_options,NULL)) { case 'a': autostart = true; @@ -2975,11 +3313,17 @@ main (int argc, char **argv) assert (optarg); log_name = optarg; break; + case 'k': + kerberos = true; + break; case 'm': assert (optarg); free (master_host); master_host = NULL; extract_host_port (optarg, &master_host, &master_port); break; + case 'n': + krb_svc_name = optarg; + break; case 'o': oauth = true; break; @@ -2987,6 +3331,9 @@ main (int argc, char **argv) assert (optarg); my_port = get_port (optarg); break; + case 't': + krb_keytab = optarg; + break; case 'U': /* Uppercase because very temporary until -u appears. */ usercred = optarg; break; @@ -3063,6 +3410,8 @@ args_done: usage (EXIT_FAILURE); } + if (kerberos) + gss_init(); if (oauth) oauth_init(); diff --git a/state_defs.h b/state_defs.h index 6b09c55..7e09b73 100644 --- a/state_defs.h +++ b/state_defs.h @@ -50,6 +50,8 @@ typedef struct _my_state { MHD_AccessHandlerCallback handler; ms_state state; bool authorized; + char *auth_hdr; + char *auth_user; /* for GC library */ LIST_ENTRY(_my_state) gc_link; /* for proxy ops */ diff --git a/t/.gitignore b/t/.gitignore index e5baa0c..75cf95c 100644 --- a/t/.gitignore +++ b/t/.gitignore @@ -1,2 +1,3 @@ *.o +gssapi_test.so poke diff --git a/t/Makefile.am b/t/Makefile.am index 77094b9..3515eb0 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -26,12 +26,14 @@ TESTS = \ timeout \ auto \ registration \ - oauth + oauth \ + kerberos lock_dir = $(abs_builddir)/lock-dir clean-local: $(AM_V_GEN)rm -rf "$(lock_dir)" $(AM_V_GEN)rm -f poke poke.o hstor.o + $(AM_V_GEN)rm -f gssapi_test.so .PHONY: prereq prereq: @@ -47,10 +49,16 @@ LDADD = ../lib/libiwhd.a oauth: poke +gssapi_test.so: $(srcdir)/gssapi_test.c + gcc -Wall -I$(top_builddir) -O2 -fpic -shared -o gssapi_test.so $< + +kerberos: gssapi_test.so + EXTRA_DIST = \ $(TESTS) \ init.cfg \ - init.sh + init.sh \ + gssapi_test.c TESTS_ENVIRONMENT = \ tmp__=$$TMPDIR; test -d "$$tmp__" || tmp__=.; \ diff --git a/t/gssapi_test.c b/t/gssapi_test.c new file mode 100644 index 0000000..7d5ca11 --- /dev/null +++ b/t/gssapi_test.c @@ -0,0 +1,261 @@ +/* Copyright (C) 2011 Red Hat, Inc. + + 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 . */ + +#include /* not needed but for "make syntax-check" */ + +#include +#include +#include +#include +#include +#include +/* #include */ +#include + +struct gss_name_struct { + const char *name; +}; + +struct gss_ctx_id_struct { + unsigned char tag[4]; +}; + +static const unsigned char tag0[4] = { 'A', 'b', '5', '3' }; + +static struct gss_name_struct *new_name(char *p, size_t len) +{ + struct gss_name_struct *ret; + char *nm; + + ret = malloc(sizeof(struct gss_name_struct)); + if (!ret) { + return NULL; + } + + nm = strndup(p, len); + if (!nm) { + free(ret); + return NULL; + } + + ret->name = nm; + return ret; +} + +OM_uint32 KRB5_CALLCONV +gss_import_name( + OM_uint32 *minor_status, + gss_buffer_t input_name_buffer, + gss_OID input_name_type, + gss_name_t *output_name) +{ + struct gss_name_struct *out; + + out = new_name(input_name_buffer->value, input_name_buffer->length); + if (!out) { + *minor_status = 1; + return GSS_S_FAILURE; + } + *output_name = out; + return 0; + // return GSS_S_CALL_BAD_STRUCTURE; +} + +OM_uint32 KRB5_CALLCONV +gss_display_status( + OM_uint32 *minor_status, + OM_uint32 status_value, + int status_type, + gss_OID mech_type, + OM_uint32 *message_context, + gss_buffer_t status_string) +{ + char *out; + + out = malloc(30); + if (!out) { + /* This is so going to crash in the app later... */ + status_string->value = NULL; + *message_context = 0; + *minor_status = 1; + return GSS_S_FAILURE; + } + snprintf(out, 30, "maj=0x%x", status_value); + status_string->value = out; + status_string->length = strlen(out); + *message_context = 0; + *minor_status = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_display_name( + OM_uint32 *minor_status, + gss_name_t input_name, + gss_buffer_t output_name_buffer, + gss_OID *output_name_type) +{ + char *out; + + out = strdup(input_name->name); + if (!out) { + output_name_buffer->value = NULL; + output_name_buffer->length = 0; + *minor_status = 1; + return GSS_S_FAILURE; + } + + output_name_buffer->value = out; + output_name_buffer->length = strlen(out); + return 0; +} + +OM_uint32 KRB5_CALLCONV +gss_init_sec_context( + OM_uint32 *minor_status, + gss_cred_id_t claimant_cred_handle, + gss_ctx_id_t *context_handle, /* return this (later) */ + gss_name_t target_name, + gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, /* return this as necessary */ + OM_uint32 *ret_flags, + OM_uint32 *time_rec) +{ + unsigned char *obuf; + struct gss_ctx_id_struct *ctx; + char pwbuf[200]; + struct passwd pwb, *pw; + size_t len; + int rc; + + rc = getpwuid_r(getuid(), &pwb, pwbuf, sizeof(pwbuf), &pw); + if (rc != 0 || pw == NULL) { + *minor_status = 1; + return GSS_S_FAILURE; + } + + /* Kerberos has no server challenge (input_token), so ignore it. */ + + ctx = *context_handle; + if (!ctx) { + ctx = malloc(sizeof(struct gss_ctx_id_struct)); + if (!ctx) { + *minor_status = 1; + return GSS_S_FAILURE; + } + memcpy(ctx->tag, tag0, 4); + *context_handle = ctx; + } + + len = strlen(pw->pw_name); + obuf = malloc(4 + len); + if (!obuf) { + free(ctx); + *minor_status = 2; + return GSS_S_FAILURE; + } + memcpy(obuf, tag0, 4); + memcpy(obuf+4, pw->pw_name, len); + output_token->value = obuf; + output_token->length = 4 + len; + + return 0; +} + +OM_uint32 KRB5_CALLCONV +gss_accept_sec_context( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_cred_id_t acceptor_cred_handle, + gss_buffer_t input_token_buffer, + gss_channel_bindings_t input_chan_bindings, + gss_name_t *src_name, + gss_OID *mech_type, + gss_buffer_t output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec, + gss_cred_id_t *delegated_cred_handle) +{ + struct gss_name_struct *out; + + if (input_token_buffer->length <= 4) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; /* 0x90000 */ + } + if (memcmp(input_token_buffer->value, tag0, 4) != 0) { + *minor_status = 1; + return GSS_S_DEFECTIVE_TOKEN; /* 0x90000 */ + } + + out = new_name(input_token_buffer->value + 4, + input_token_buffer->length - 4); + if (!out) { + *minor_status = 1; + return GSS_S_FAILURE; + } + + *src_name = out; + return GSS_S_COMPLETE; +} + +OM_uint32 KRB5_CALLCONV +gss_delete_sec_context( + OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) +{ + /* We leak on purpose: afraid to crash with GC. */ + return 0; +} + +OM_uint32 KRB5_CALLCONV +gss_release_buffer( + OM_uint32 *minstat, /* minor_status */ + gss_buffer_t buf) /* buffer */ +{ + return 0; +} + +OM_uint32 KRB5_CALLCONV +gss_release_name( + OM_uint32 *minstat, /* minor_status */ + gss_name_t *name) /* input_name */ +{ + return 0; +} + +#if 0 +OM_uint32 KRB5_CALLCONV +gss_acquire_cred( + OM_uint32 *, /* minor_status */ + gss_name_t, /* desired_name */ + OM_uint32, /* time_req */ + gss_OID_set, /* desired_mechs */ + gss_cred_usage_t, /* cred_usage */ + gss_cred_id_t *, /* output_cred_handle */ + gss_OID_set *, /* actual_mechs */ + OM_uint32 *); /* time_rec */ +#endif + +OM_uint32 KRB5_CALLCONV krb5_gss_register_acceptor_identity(const char *fname) +{ + /* Just ignore the keytab for the moment. */ + return 0; +} diff --git a/t/kerberos b/t/kerberos new file mode 100755 index 0000000..b42158d --- /dev/null +++ b/t/kerberos @@ -0,0 +1,52 @@ +#!/bin/sh +# Test authentication functionality. + +. "${srcdir=.}/init.sh"; path_prepend_ .. + +mkdir FS mongod iwhd || framework_failure_ mkdir failed + +# This actually mostly helps against weirdness with global variables. +# The makefile ensures that we only ever get here with the .so built. +test -f ${abs_top_builddir}/t/gssapi_test.so \ + || fail_ "no $abs_top_builddir/t/gssapi_test.so" +#test -f ./t/gssapi_test.so || fail_ "no ./t/gssapi_test.so" + +m_port=$(get_port $mongo_base_port $lock_dir/m-) \ + || fail_ "failed to get mongodb port" + +mongod --port $m_port --pidfilepath mongod/pid --dbpath mongod > mongod.log 2>&1 & +mongo_pid=$! +cleanup_() { kill -9 $mongo_pid; } + +# Wait for up to 5 seconds for mongod to begin listening. +wait_for .1 50 'mongo localhost:$m_port < /dev/null' \ + || framework_failure_ mongod failed to start + +port=$(get_port 9095 $lock_dir/i-) || fail_ "failed to get iwhd port" + +ulimit -c unlimited + +printf '[{"path": "FS", "type": "fs", "name": "primary"}]\n' \ + > iwhd.cfg || fail=1 + +LD_PRELOAD=${abs_top_builddir}/t/gssapi_test.so iwhd -v -k \ + -p $port -c iwhd.cfg -d localhost:$m_port & +iwhd_pid=$! +cleanup_() { kill -9 $mongo_pid; kill $iwhd_pid; } + +## Wait for up to 5 seconds for iwhd to begin listening on $port. +#wait_for .1 50 "curl -s http://localhost:$port" \ +# || { echo iwhd failed to listen; Exit 1; } +sleep 5 + +fail=0 + +# LD_PRELOAD=/q/zaitcev/hail/iwhd-tip/t/gssapi_test.so curl -0 -v --negotiate -u : http://localhost:9090/ +# We use -0 because of a mysterious problem with curl attempting to reuse +# the server connection and failing in a spectacular fashion. Perhaps iwhd +# does not implement HTTP 1.1 pipelining properly. +LD_PRELOAD=${abs_top_builddir}/t/gssapi_test.so curl -0 -v \ + --negotiate -u : http://localhost:$port/ \ + || fail=1 + +Exit $fail