[patch RFC] Add Kerberos authentication
Pete Zaitcev
zaitcev at redhat.com
Wed Jul 20 22:19:17 UTC 2011
This patch adds an authentication with Kerberos, which is tested to work
with curl client. It also features an attempt at a build-time test.
diff --git a/configure.ac b/configure.ac
index e23d1fa..eb0c4c1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -70,6 +70,9 @@ AC_CHECK_LIB([uuid], [uuid_generate_random],
[AC_MSG_ERROR([Missing required uuid lib])])
AC_SUBST([UUID_LIB])
+dnl How to check properly?
+LIBS="$LIBS -lgssapi_krb5"
+
PKG_CHECK_MODULES([HAIL],[libhail >= 0.8])
AC_SUBST([HAIL_LIBS])
AC_SUBST([HAIL_CFLAGS])
@@ -97,6 +100,12 @@ AC_CACHE_CHECK([whether json_load_file takes 3 arguments],
AC_DEFINE_UNQUOTED([JANSSON_LOAD_FLAG], $iw_cv_func_jansson_flag,
[Define to "0," if json_load_file and json_loads require a flags arguments.])
+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 11b2971..d470de2 100644
--- a/rest.c
+++ b/rest.c
@@ -29,10 +29,13 @@
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
+#include <stdbool.h>
#include <microhttpd.h>
#include <hstor.h> /* only for ARRAY_SIZE at this point */
#include <curl/curl.h>
+#include <gssapi.h>
+#include <gssapi/gssapi_krb5.h>
#include "configmake.h" /* for LOCALEDIR */
#include "dirname.h"
@@ -50,6 +53,7 @@
#include "mpipe.h"
#include "state_defs.h"
#include "version-etc.h"
+#include "base64.h"
const char version_etc_copyright[] =
/* Do *not* mark this string for translation. %s is a copyright
@@ -104,7 +108,70 @@ typedef struct {
MHD_AccessHandlerCallback handler;
} rule;
+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] */
+ fprintf(stderr, "%s\n", buf);
+}
+
static unsigned short my_port = MY_PORT;
+static char my_hostname[HOST_NAME_MAX+1];
+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 const char *const (reserved_name[]) = {"_default", "_new", "_policy", "_query", NULL};
static const char *const (reserved_attr[]) = {"_attrs", "_bucket", "_date", "_etag", "_key", "_loc", "_size", NULL};
@@ -163,6 +230,35 @@ validate_url (const char *url)
return !is_reserved(slash+1,reserved_name);
}
+static int do_resp(struct MHD_Connection *conn, my_state *ms,
+ unsigned int status_code, const char *etag,
+ const char *datastr)
+{
+ struct MHD_Response *resp;
+ size_t datalen = datastr? strlen(datastr): 0;
+
+ resp = MHD_create_response_from_data(datalen,(void *)datastr,
+ MHD_NO,MHD_NO);
+ if (!resp) {
+ 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);
+ }
+
+ MHD_queue_response(conn,status_code,resp);
+ MHD_destroy_response(resp);
+ return MHD_YES;
+}
+
/**********
* The proxy has MHD on one side and CURL on the other. The CURL side is
* always run in a child thread. Yes, there are both context switches
@@ -307,11 +403,7 @@ proxy_get_data (void *cctx, struct MHD_Connection *conn, const char *url,
conn, MHD_HEADER_KIND, "If-None-Match");
if (user_etag && !strcmp(user_etag,my_etag)) {
DPRINTF("ETag match!\n");
- resp = MHD_create_response_from_data(0,NULL,
- MHD_NO,MHD_NO);
- MHD_queue_response(conn,MHD_HTTP_NOT_MODIFIED,resp);
- MHD_destroy_response(resp);
- return MHD_YES;
+ return do_resp(conn,ms,MHD_HTTP_NOT_MODIFIED,NULL,NULL);
}
ms->from_master = 0;
}
@@ -319,11 +411,7 @@ proxy_get_data (void *cctx, struct MHD_Connection *conn, const char *url,
DPRINTF("%s/%s not found locally\n",ms->bucket,ms->key);
if (!master_host) {
DPRINTF(" that means it doesn't exist\n");
- resp = MHD_create_response_from_data(0,NULL,
- MHD_NO,MHD_NO);
- MHD_queue_response(conn,MHD_HTTP_NOT_FOUND,resp);
- MHD_destroy_response(resp);
- return MHD_YES;
+ return do_resp(conn,ms,MHD_HTTP_NOT_FOUND,NULL,NULL);
}
DPRINTF(" will fetch from %s:%u\n", master_host,master_port);
ms->from_master = 1;
@@ -369,6 +457,9 @@ 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);
@@ -419,7 +510,6 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
size_t *data_size, void **rctx)
{
- struct MHD_Response *resp;
my_state *ms = *rctx;
int rc;
@@ -432,14 +522,7 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
if (ms->state == MS_NEW) {
if (!validate_put(conn) || !validate_url(url)) {
DPRINTF("rejecting %s\n",url);
- resp = MHD_create_response_from_data(0,NULL,
- MHD_NO,MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
- MHD_queue_response(conn,MHD_HTTP_FORBIDDEN,resp);
- MHD_destroy_response(resp);
- return MHD_YES;
+ return do_resp(conn,ms,MHD_HTTP_FORBIDDEN,NULL,NULL);
}
ms->state = MS_NORMAL;
ms->url = strdup(url);
@@ -474,14 +557,8 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
rc = pipe_prod_wait_init(&ms->pipe);
if (rc != 0) {
DPRINTF("producer wait failed\n");
- resp = MHD_create_response_from_data(0,NULL,
- MHD_NO,MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
- MHD_queue_response(conn,MHD_HTTP_INTERNAL_SERVER_ERROR,
- resp);
- MHD_destroy_response(resp);
+ return do_resp(conn,ms,MHD_HTTP_INTERNAL_SERVER_ERROR,
+ NULL,NULL);
} else if (rc > 0) {
/*
* Note that we fail here even if 1 of N replicas fail.
@@ -490,14 +567,8 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
*/
DPRINTF("producer replicas failed (%u of %u)\n",
rc, ms->pipe.cons_total);
- resp = MHD_create_response_from_data(0,NULL,
- MHD_NO,MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
- MHD_queue_response(conn,MHD_HTTP_INTERNAL_SERVER_ERROR,
- resp);
- MHD_destroy_response(resp);
+ return do_resp(conn,ms,MHD_HTTP_INTERNAL_SERVER_ERROR,
+ NULL,NULL);
} else {
DPRINTF("producer proceeding\n");
}
@@ -535,15 +606,7 @@ proxy_put_data (void *cctx, struct MHD_Connection *conn, const char *url,
recheck_replication(ms,NULL);
rc = MHD_HTTP_CREATED;
}
- resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
- if (etag) {
- MHD_add_response_header(resp,"ETag",etag);
- }
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
+ return do_resp(conn,ms,rc,etag,NULL);
}
return MHD_YES;
@@ -576,14 +639,11 @@ proxy_list_attrs (void *cctx, struct MHD_Connection *conn, const char *url,
* means a request-specific routine (e.g. show_parts)
* created its own response. Therefore we shouldn't.
*/
- struct MHD_Response *resp
- = MHD_create_response_from_data(0,NULL, MHD_NO,MHD_NO);
- if (!resp) {
+ if (do_resp(conn,ms,rc,NULL,NULL) != MHD_YES) {
fprintf(stderr,"MHD_crfd failed\n");
return MHD_NO;
}
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
+ return MHD_YES;
}
}
@@ -595,10 +655,8 @@ proxy_get_attr (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
size_t *data_size, void **rctx)
{
- struct MHD_Response *resp;
char *fixed;
my_state *ms = *rctx;
- int rc = MHD_HTTP_NOT_FOUND;
(void)cctx;
(void)method;
@@ -609,20 +667,11 @@ proxy_get_attr (void *cctx, struct MHD_Connection *conn, const char *url,
DPRINTF("PROXY GET ATTR %s\n",url);
if (meta_get_value(ms->bucket,ms->key,ms->attr,&fixed) == 0) {
- resp = MHD_create_response_from_data(strlen(fixed),fixed,
- MHD_NO,MHD_NO);
- rc = MHD_HTTP_OK;
+ return do_resp(conn,ms,MHD_HTTP_OK,NULL,fixed);
}
else {
- resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
+ return do_resp(conn,ms,MHD_HTTP_NOT_FOUND,NULL,NULL);
}
- if (!resp) {
- return MHD_NO;
- }
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
-
- return MHD_YES;
}
static int
@@ -630,7 +679,6 @@ proxy_put_attr (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
size_t *data_size, void **rctx)
{
- struct MHD_Response *resp;
my_state *ms = *rctx;
int send_resp = 0;
@@ -679,15 +727,7 @@ proxy_put_attr (void *cctx, struct MHD_Connection *conn, const char *url,
return MHD_NO;
}
if (is_reserved(ms->attr,reserved_attr)) {
- resp = MHD_create_response_from_data(
- 0,NULL,MHD_NO,MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
- MHD_queue_response(conn,MHD_HTTP_BAD_REQUEST,
- resp);
- MHD_destroy_response(resp);
- return MHD_YES;
+ return do_resp(conn,ms,MHD_HTTP_BAD_REQUEST,NULL,NULL);
}
meta_set_value(ms->bucket,ms->key,ms->attr,ms->pipe.data_ptr);
/*
@@ -700,12 +740,7 @@ proxy_put_attr (void *cctx, struct MHD_Connection *conn, const char *url,
}
if (send_resp) {
- resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
- MHD_queue_response(conn,MHD_HTTP_CREATED,resp);
- MHD_destroy_response(resp);
+ return do_resp(conn,ms,MHD_HTTP_CREATED,NULL,NULL);
/*
* TBD: check if the attribute was a replication policy, and
* start/stop replication activities as appropriate.
@@ -930,11 +965,7 @@ proxy_query (void *cctx, struct MHD_Connection *conn, const char *url,
}
int rc = proxy_query_init(ms, ms->pipe.data_ptr);
if (rc != MHD_HTTP_OK) {
- resp = MHD_create_response_from_data(0,NULL,
- MHD_NO,MHD_NO);
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
- return MHD_YES;
+ return do_resp(conn,ms,rc,NULL,NULL);
}
resp = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN,
CB_BLOCK_SIZE, proxy_query_func, ms, simple_closer);
@@ -943,6 +974,10 @@ proxy_query (void *cctx, struct MHD_Connection *conn, const char *url,
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);
}
@@ -967,10 +1002,7 @@ proxy_list_objs (void *cctx, struct MHD_Connection *conn, const char *url,
int rc = proxy_query_init(ms, NULL);
if (rc != MHD_HTTP_OK) {
- resp = MHD_create_response_from_data(0,NULL, MHD_NO,MHD_NO);
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
- return MHD_YES;
+ return do_resp(conn,ms,rc,NULL,NULL);
}
resp = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN,
CB_BLOCK_SIZE, proxy_query_func, ms, simple_closer);
@@ -979,7 +1011,9 @@ proxy_list_objs (void *cctx, struct MHD_Connection *conn, const char *url,
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);
return MHD_YES;
@@ -991,7 +1025,6 @@ proxy_delete (void *cctx, struct MHD_Connection *conn, const char *url,
size_t *data_size, void **rctx)
{
my_state *ms = *rctx;
- struct MHD_Response *resp;
char *copied_url;
char *bucket;
char *key;
@@ -1019,15 +1052,7 @@ proxy_delete (void *cctx, struct MHD_Connection *conn, const char *url,
replicate_delete(url,ms);
}
- resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
- error (0, 0, "DELETE BUCKET: rc=%d", rc);
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
-
- return MHD_YES;
+ return do_resp(conn,ms,rc,NULL,NULL);
}
/* TBD: get actual bucket list */
@@ -1153,11 +1178,13 @@ proxy_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);
return MHD_YES;
-
}
static int
@@ -1329,8 +1356,8 @@ control_api_root (void *cctx, struct MHD_Connection *conn, const char *url,
}
if (len >= (int)sizeof(buf)) {
- len = 0;
- rc = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ return do_resp(conn,ms,MHD_HTTP_INTERNAL_SERVER_ERROR,
+ NULL,NULL);
}
/* NB The last arg tells MHD to copy the arg and free it later. */
@@ -1338,6 +1365,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);
@@ -1349,7 +1379,6 @@ proxy_bucket_post (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
size_t *data_size, void **rctx)
{
- struct MHD_Response *resp;
my_state *ms = *rctx;
char *key;
@@ -1398,13 +1427,10 @@ proxy_bucket_post (void *cctx, struct MHD_Connection *conn, const char *url,
else {
DPRINTF("A parameter is MISSING (fail)\n");
}
- resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
- if (!resp) {
+ if (do_resp(conn,ms,rc,NULL,NULL) != MHD_YES) {
fprintf(stderr,"MHD_crfd failed\n");
return MHD_NO;
}
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
}
return MHD_YES;
@@ -1554,6 +1580,9 @@ show_parts (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);
return MHD_HTTP_PROCESSING;
@@ -1564,7 +1593,6 @@ proxy_object_post (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
size_t *data_size, void **rctx)
{
- struct MHD_Response *resp;
my_state *ms = *rctx;
char *op;
@@ -1625,19 +1653,14 @@ proxy_object_post (void *cctx, struct MHD_Connection *conn, const char *url,
* means a request-specific routine (e.g. show_parts)
* created its own response. Therefore we shouldn't.
*/
- resp = MHD_create_response_from_data(0,NULL,
- MHD_NO,MHD_NO);
- if (!resp) {
+ if (do_resp(conn,ms,rc,NULL,NULL) != MHD_YES) {
fprintf(stderr,"MHD_crfd failed\n");
return MHD_NO;
}
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
}
}
return MHD_YES;
-
}
/* Derived from gnulib's x2nrealloc. */
@@ -1844,6 +1867,9 @@ proxy_list_provs (void *cctx, struct MHD_Connection *conn, const char *url,
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);
@@ -1886,6 +1912,7 @@ proxy_primary_prov (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
size_t *data_size, void **rctx)
{
+ my_state *ms = *rctx;
(void)cctx;
(void)method;
(void)version;
@@ -1894,23 +1921,12 @@ proxy_primary_prov (void *cctx, struct MHD_Connection *conn, const char *url,
DPRINTF("PROXY GET PRIMARY PROVIDER (%s)\n", url);
// "/_providers/_primary" is the only one we accept for now.
- bool valid = strcmp (url, "/_providers/_primary") == 0;
- unsigned int rc = (valid ? MHD_HTTP_OK : MHD_HTTP_BAD_REQUEST);
- if (!valid)
+ if (strcmp (url, "/_providers/_primary") != 0) {
error (0, 0, _("invalid request: %s"), url);
-
- const char *name = get_main_provider()->name;
- struct MHD_Response *resp;
- resp = MHD_create_response_from_data(valid ? strlen (name) : 0,
- valid ? (void *) name : NULL,
- MHD_NO, MHD_NO);
- if (!resp) {
- return MHD_NO;
+ return do_resp(conn,ms,MHD_HTTP_BAD_REQUEST,NULL,NULL);
}
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
- return MHD_YES;
+ return do_resp(conn,ms,MHD_HTTP_OK,NULL,get_main_provider()->name);
}
static int
@@ -1918,6 +1934,7 @@ proxy_set_primary (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
size_t *data_size, void **rctx)
{
+ my_state *ms = *rctx;
(void)cctx;
(void)method;
(void)version;
@@ -1955,16 +1972,7 @@ proxy_set_primary (void *cctx, struct MHD_Connection *conn, const char *url,
}
bad_set:;
-
- struct MHD_Response *resp;
- resp = MHD_create_response_from_data(0, NULL, MHD_NO, MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
-
- return MHD_YES;
+ return do_resp(conn,ms,rc,NULL,NULL);
}
static int
@@ -1972,6 +1980,7 @@ proxy_delete_prov (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
size_t *data_size, void **rctx)
{
+ my_state *ms = *rctx;
DPRINTF("PROXY DELETE PROVIDER %s\n",url);
(void)cctx;
(void)method;
@@ -1979,31 +1988,19 @@ proxy_delete_prov (void *cctx, struct MHD_Connection *conn, const char *url,
(void)data;
(void)data_size;
- struct MHD_Response *resp
- = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
-
char *prov_name = url_to_provider_name (url);
provider_t *prov = find_provider (prov_name);
+ if (!prov)
+ return do_resp(conn,ms,MHD_HTTP_NOT_FOUND,NULL,NULL);
+
// don't allow removal of current main provider.
if (prov == get_main_provider())
- prov = NULL;
-
- int rc = prov ? MHD_HTTP_OK : MHD_HTTP_NOT_FOUND;
-
- DPRINTF("PROXY DELETE PROVIDER prov=%s rc=%d\n", prov_name, rc);
-
- if (prov) {
- prov->deleted = 1;
- }
-
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
+ return do_resp(conn,ms,MHD_HTTP_BAD_REQUEST,NULL,NULL);
- return MHD_YES;
+ DPRINTF("PROXY DELETE PROVIDER prov=%s rc=%d\n", prov_name,MHD_HTTP_OK);
+ prov->deleted = 1;
+ return do_resp(conn,ms,MHD_HTTP_OK,NULL,NULL);
}
static int
@@ -2012,7 +2009,6 @@ proxy_add_prov (void *cctx, struct MHD_Connection *conn, const char *url,
size_t *data_size, void **rctx)
{
DPRINTF("PROXY ADD PROVIDER %s\n",url);
- struct MHD_Response *resp;
my_state *ms = *rctx;
(void)cctx;
@@ -2073,13 +2069,10 @@ proxy_add_prov (void *cctx, struct MHD_Connection *conn, const char *url,
}
add_fail:
- resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
- if (!resp) {
+ if (do_resp(conn,ms,rc,NULL,NULL) != MHD_YES) {
fprintf(stderr,"MHD_crfd failed\n");
return MHD_NO;
}
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
}
return MHD_YES;
@@ -2102,15 +2095,10 @@ proxy_create_bucket (void *cctx, struct MHD_Connection *conn, const char *url,
/* curl -T moo.empty http://localhost:9090/_new by accident */
int rc = create_bucket(ms->bucket,ms);
- struct MHD_Response *resp
- = MHD_create_response_from_data(0,NULL, MHD_NO,MHD_NO);
- if (!resp) {
+ if (do_resp(conn,ms,rc,NULL,NULL) != MHD_YES) {
fprintf(stderr,"MHD_crfd failed\n");
return MHD_NO;
}
- MHD_queue_response(conn,rc,resp);
- MHD_destroy_response(resp);
-
return MHD_YES;
}
@@ -2216,11 +2204,195 @@ parse_url (const char *url, my_state *ms)
else if (eindex == URL_ATTR && !strcmp (parts[URL_ATTR], "_attrs"))
eindex = URL_LIST_ATTRS;
- DPRINTF("parse_url: %d: %s %s %s", eindex, parts[URL_BUCKET],
+ DPRINTF("parse_url: %d: %s %s %s\n", eindex, parts[URL_BUCKET],
parts[URL_OBJECT], parts[URL_ATTR]);
return eindex;
}
+static bool nego_token(char **pbuf, size_t *plen, const char *val)
+{
+ char *buf;
+ size_t len;
+ const char *p;
+ bool rcb;
+
+ p = val;
+ while (*p == ' ') {
+ if (*p == 0) {
+ return false;
+ }
+ p++;
+ }
+ if (strncmp(p, "Negotiate ", sizeof("Negotiate ")-1) != 0)
+ return false;
+ p += sizeof("Negotiate ")-1;
+ while (*p == ' ') {
+ if (*p == 0) {
+ return false;
+ }
+ p++;
+ }
+
+ 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);
+ return 1;
+ }
+
+ rcb = nego_token(&buf, &gss_itok.length, val);
+ if (!rcb) {
+ do_resp(conn,NULL,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;
+}
+
+static int kv_iter_dump(void *cls, enum MHD_ValueKind kind,
+ const char *key, const char *value)
+{
+ /* ok to use printf when MHD_USE_THREAD_PER_CONNECTION maybe? */
+ printf("%s: %s\n", key, value);
+
+ return MHD_YES;
+}
+
static int
access_handler_0 (void *cctx, struct MHD_Connection *conn, const char *url,
const char *method, const char *version, const char *data,
@@ -2228,10 +2400,24 @@ access_handler_0 (void *cctx, struct MHD_Connection *conn, const char *url,
{
unsigned int i;
url_type utype;
- struct MHD_Response *resp;
my_state *ms = *rctx;
+ int rc;
+
+ if (verbose >= 2) {
+ printf(">> HTTP\n");
+ rc = MHD_get_connection_values(conn, MHD_HEADER_KIND,
+ kv_iter_dump, NULL);
+ printf(">> headers: %d\n", rc);
+ }
if (ms) {
+ if (kerberos) {
+ rc = authenticate(conn, ms);
+ if (rc < 0)
+ return MHD_NO;
+ if (rc > 0)
+ return MHD_YES;
+ }
return ms->handler(cctx,conn,url,method,version,
data,data_size,rctx);
}
@@ -2259,24 +2445,25 @@ access_handler_0 (void *cctx, struct MHD_Connection *conn, const char *url,
ms->post = NULL;
ms->conn = conn;
*rctx = ms;
+ if (kerberos) {
+ rc = authenticate(conn, ms);
+ if (rc < 0)
+ return MHD_NO;
+ if (rc > 0)
+ return MHD_YES;
+ }
return ms->handler(cctx,conn,url,method,version,
data,data_size,rctx);
}
+ /* This is *unauthenticated*. Remove altogether? TBD */
if (!strcmp(method,"QUIT")) {
(void)sem_post((sem_t *)cctx);
return MHD_NO;
}
fprintf(stderr,"bad request m=%s u=%s\n",method,url);
-
- resp = MHD_create_response_from_data(0,NULL,MHD_NO,MHD_NO);
- if (!resp) {
- return MHD_NO;
- }
- MHD_queue_response(conn,MHD_HTTP_NOT_FOUND,resp);
- MHD_destroy_response(resp);
- return MHD_YES;
+ return do_resp(conn,ms,MHD_HTTP_NOT_FOUND,NULL,NULL);
}
static int
@@ -2304,8 +2491,11 @@ static const struct option my_options[] = {
{ "autostart", no_argument, NULL, 'a' },
{ "config", required_argument, NULL, 'c' },
{ "db", required_argument, NULL, 'd' },
+ { "kerberos", no_argument, NULL, 'k' },
{ "master", required_argument, NULL, 'm' },
+ { "krbsvcnm", required_argument, NULL, 'n' },
{ "port", required_argument, NULL, 'p' },
+ { "krbkeytab", required_argument, NULL, 't' },
{ "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, GETOPT_VERSION_CHAR },
{ "help", no_argument, NULL, GETOPT_HELP_CHAR },
@@ -2331,8 +2521,11 @@ A configuration file must be specified.\n\
-a, --autostart start necessary back-end services\n\
-c, --config=FILE config file [required]\n\
-d, --db=HOST_PORT database server as ip[:port]\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\
-p, --port=PORT alternate listen port (default 9090)\n\
+ -t, --krbkeytab=FILE location of Kerberos keytab\n\
-v, --verbose verbose/debug output\n\
\n\
--help display this help and exit\n\
@@ -2347,6 +2540,78 @@ 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
+}
int
main (int argc, char **argv)
@@ -2356,7 +2621,7 @@ main (int argc, char **argv)
char *stctx = NULL;
char *port_tmp;
bool autostart = false;
- char *cfg_file = NULL;
+ char *cfg_file = NULL;
set_program_name (argv[0]);
setlocale (LC_ALL, "");
@@ -2367,7 +2632,9 @@ main (int argc, char **argv)
GC_INIT ();
- for (;;) switch (getopt_long(argc,argv,"ac:d:m:p:v",my_options,NULL)) {
+ for (;;)
+ switch (getopt_long(argc,argv,"ac:d:km:n:p:t:v",my_options,NULL))
+ {
case 'a':
autostart = true;
break;
@@ -2382,6 +2649,9 @@ main (int argc, char **argv)
db_port = (unsigned short)strtoul(port_tmp,NULL,10);
}
break;
+ case 'k':
+ kerberos = true;
+ break;
case 'm':
assert (optarg);
master_host = strtok_r(optarg,":",&stctx);
@@ -2390,9 +2660,15 @@ main (int argc, char **argv)
master_port = (unsigned short)strtoul(port_tmp,NULL,10);
}
break;
+ case 'n':
+ krb_svc_name = optarg;
+ break;
case 'p':
my_port = (unsigned short)strtoul(optarg,NULL,10);
break;
+ case 't':
+ krb_keytab = optarg;
+ break;
case 'v':
++verbose;
break;
@@ -2445,6 +2721,9 @@ args_done:
usage (EXIT_FAILURE);
}
+ if (kerberos)
+ gss_init();
+
sem_init(&the_sem,0,0);
if (verbose) {
diff --git a/state_defs.h b/state_defs.h
index 30bebb9..0f7904a 100644
--- a/state_defs.h
+++ b/state_defs.h
@@ -47,6 +47,8 @@ typedef struct _my_state {
/* for everyone */
MHD_AccessHandlerCallback handler;
ms_state state;
+ char *auth_hdr;
+ char *auth_user;
/* for proxy ops */
char *url;
char bucket[MAX_FIELD_LEN];
diff --git a/t/.gitignore b/t/.gitignore
new file mode 100644
index 0000000..a58f1a1
--- /dev/null
+++ b/t/.gitignore
@@ -0,0 +1 @@
+gssapi_test.so
diff --git a/t/Makefile.am b/t/Makefile.am
index 133de13..69dd36d 100644
--- a/t/Makefile.am
+++ b/t/Makefile.am
@@ -22,11 +22,13 @@ TESTS = \
provider \
replication \
auto \
- registration
+ registration \
+ kerberos
lock_dir = $(abs_builddir)/lock-dir
clean-local:
$(AM_V_GEN)rm -rf "$(lock_dir)"
+ $(AM_V_GEN)rm -f gssapi_test.so
.PHONY: prereq
prereq:
@@ -34,10 +36,16 @@ prereq:
$(TEST_LOGS): prereq
+gssapi_test.so: $(srcdir)/gssapi_test.c
+ gcc -Wall -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/auto b/t/auto
index dee4c4e..c9f18ab 100644
--- a/t/auto
+++ b/t/auto
@@ -3,9 +3,9 @@
. "${srcdir=.}/init.sh"; path_prepend_ ..
-mkdir iwhd || framework_failure_ mkdir failed
+mkdir FS mongod iwhd || framework_failure_ mkdir failed
-port=9094
+port=$(get_port 9095 $lock_dir/i-) || fail_ "failed to get iwhd port"
m_port=27018 # auto-start default
printf '[{"path": "FS", "type": "fs", "name": "primary"}]\n' \
diff --git a/t/gssapi_test.c b/t/gssapi_test.c
new file mode 100644
index 0000000..974fd97
--- /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 <http://www.gnu.org/licenses/>. */
+
+/* #include <config.h> */
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <gssapi/gssapi.h>
+/* #include <gssapi/gssapi_generic.h> */
+#include <gssapi/gssapi_krb5.h>
+
+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 100644
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
More information about the iwhd-devel
mailing list