From 89eb160c93612c9dffa1ecdc83028a1aa69917dc Mon Sep 17 00:00:00 2001 From: kouril Date: Mon, 28 Mar 2011 20:21:23 +0000 Subject: [PATCH] Adding testing CLI client (based off the Heimdal testing sample) --- client/Makefile | 11 + client/base64.c | 144 +++++++++++++ client/base64.h | 55 +++++ client/client_locl.h | 98 +++++++++ client/gss_common.c | 179 ++++++++++++++++ client/gss_common.h | 49 +++++ client/http_client.c | 579 +++++++++++++++++++++++++++++++++++++++++++++++++++ client/net_read.c | 118 +++++++++++ client/net_write.c | 107 ++++++++++ 9 files changed, 1340 insertions(+) create mode 100644 client/Makefile create mode 100644 client/base64.c create mode 100644 client/base64.h create mode 100644 client/client_locl.h create mode 100644 client/gss_common.c create mode 100644 client/gss_common.h create mode 100644 client/http_client.c create mode 100644 client/net_read.c create mode 100644 client/net_write.c diff --git a/client/Makefile b/client/Makefile new file mode 100644 index 0000000..0fe6f2a --- /dev/null +++ b/client/Makefile @@ -0,0 +1,11 @@ +DEFS = -DHAVE_SYS_TYPES_H -DHAVE_UNISTD_H -DHAVE_SYS_SOCKET_H -DHAVE_NETINET_IN_H -DHAVE_NETDB_H -DHAVE_SYS_PARAM_H \ + -D_GNU_SOURCE +CPPFLAGS = $(DEFS) -Wall -O0 -g +LDFLAGS = -lgssapi_krb5 + +HTTP_OBJS = http_client.o gss_common.o base64.o net_write.o net_read.o + +http_client: $(HTTP_OBJS) + +clean: + rm -f $(HTTP_OBJS) http_client diff --git a/client/base64.c b/client/base64.c new file mode 100644 index 0000000..37c22b9 --- /dev/null +++ b/client/base64.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1995-2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * 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 Institute 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 BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include "base64.h" + +static const char base64_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int +pos(char c) +{ + const char *p; + for (p = base64_chars; *p; p++) + if (*p == c) + return p - base64_chars; + return -1; +} + +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +base64_encode(const void *data, int size, char **str) +{ + char *s, *p; + int i; + int c; + const unsigned char *q; + + if (size > INT_MAX/4 || size < 0) { + *str = NULL; + return -1; + } + + p = s = (char *) malloc(size * 4 / 3 + 4); + if (p == NULL) { + *str = NULL; + return -1; + } + q = (const unsigned char *) data; + + for (i = 0; i < size;) { + c = q[i++]; + c *= 256; + if (i < size) + c += q[i]; + i++; + c *= 256; + if (i < size) + c += q[i]; + i++; + p[0] = base64_chars[(c & 0x00fc0000) >> 18]; + p[1] = base64_chars[(c & 0x0003f000) >> 12]; + p[2] = base64_chars[(c & 0x00000fc0) >> 6]; + p[3] = base64_chars[(c & 0x0000003f) >> 0]; + if (i > size) + p[3] = '='; + if (i > size + 1) + p[2] = '='; + p += 4; + } + *p = 0; + *str = s; + return (int) strlen(s); +} + +#define DECODE_ERROR 0xffffffff + +static unsigned int +token_decode(const char *token) +{ + int i; + unsigned int val = 0; + int marker = 0; + if (strlen(token) < 4) + return DECODE_ERROR; + for (i = 0; i < 4; i++) { + val *= 64; + if (token[i] == '=') + marker++; + else if (marker > 0) + return DECODE_ERROR; + else + val += pos(token[i]); + } + if (marker > 2) + return DECODE_ERROR; + return (marker << 24) | val; +} + +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +base64_decode(const char *str, void *data) +{ + const char *p; + unsigned char *q; + + q = data; + for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) { + unsigned int val = token_decode(p); + unsigned int marker = (val >> 24) & 0xff; + if (val == DECODE_ERROR) + return -1; + *q++ = (val >> 16) & 0xff; + if (marker < 2) + *q++ = (val >> 8) & 0xff; + if (marker < 1) + *q++ = val & 0xff; + } + return q - (unsigned char *) data; +} diff --git a/client/base64.h b/client/base64.h new file mode 100644 index 0000000..dfae4c1 --- /dev/null +++ b/client/base64.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * 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 Institute 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 BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE 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. + */ + +/* $Id$ */ + +#ifndef _BASE64_H_ +#define _BASE64_H_ + +#ifndef ROKEN_LIB_FUNCTION +#ifdef _WIN32 +#define ROKEN_LIB_FUNCTION +#define ROKEN_LIB_CALL __cdecl +#else +#define ROKEN_LIB_FUNCTION +#define ROKEN_LIB_CALL +#endif +#endif + +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +base64_encode(const void *, int, char **); + +ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL +base64_decode(const char *, void *); + +#endif diff --git a/client/client_locl.h b/client/client_locl.h new file mode 100644 index 0000000..2c4024e --- /dev/null +++ b/client/client_locl.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * 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 Institute 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 BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE 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. + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif +#ifdef HAVE_NETINET6_IN6_H +#include +#endif + +#ifdef HAVE_PWD_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#include +//#include +//#include +#include +#include + +#include +#include + +#define SERVICE "test" + +#define PORT "test" + +#if 0 +extern char *service; +extern char *mech; +extern krb5_keytab keytab; +extern int fork_flag; +int server_setup(krb5_context*, int, char**); +int client_setup(krb5_context*, int*, char**); +int client_doit (const char *hostname, int port, const char *service, + int (*func)(int, const char *hostname, const char *service)); +#endif +ssize_t +net_write (int, const void *, size_t); + +ssize_t +net_read (int, void *, size_t); diff --git a/client/gss_common.c b/client/gss_common.c new file mode 100644 index 0000000..23517a7 --- /dev/null +++ b/client/gss_common.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * 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 Institute 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 BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE 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 "client_locl.h" +#include +#include "gss_common.h" +//RCSID("$Id$"); + +void +write_token (int sock, gss_buffer_t buf) +{ + uint32_t len, net_len; + OM_uint32 min_stat; + + len = buf->length; + + net_len = htonl(len); + + if (net_write (sock, &net_len, 4) != 4) + err (1, "write"); + if (net_write (sock, buf->value, len) != len) + err (1, "write"); + + gss_release_buffer (&min_stat, buf); +} + +static void +enet_read(int fd, void *buf, size_t len) +{ + ssize_t ret; + + ret = net_read (fd, buf, len); + if (ret == 0) + errx (1, "EOF in read"); + else if (ret < 0) + errx (1, "read"); +} + +void +read_token (int sock, gss_buffer_t buf) +{ + uint32_t len, net_len; + + enet_read (sock, &net_len, 4); + len = ntohl(net_len); + buf->length = len; + buf->value = malloc(len); + enet_read (sock, buf->value, len); +} + +void +gss_print_errors (OM_uint32 maj_stat, OM_uint32 min_stat) +{ + OM_uint32 new_stat; + OM_uint32 msg_ctx = 0; + gss_buffer_desc status_string; + OM_uint32 ret; + + do { + ret = gss_display_status(&new_stat, + maj_stat, + GSS_C_GSS_CODE, + GSS_C_NO_OID, + &msg_ctx, + &status_string); + fprintf (stderr, "%.*s\n", (int)status_string.length, + (char *)status_string.value); + gss_release_buffer(&new_stat, &status_string); + } while (!GSS_ERROR(ret) && msg_ctx != 0); + + msg_ctx = 0; + do { + ret = gss_display_status (&new_stat, + min_stat, + GSS_C_MECH_CODE, + GSS_C_NO_OID, + &msg_ctx, + &status_string); + fprintf (stderr, "%.*s\n", (int)status_string.length, + (char *)status_string.value); + gss_release_buffer (&new_stat, &status_string); + } while (!GSS_ERROR(ret) && msg_ctx != 0); +} + +void +gss_verr(int exitval, OM_uint32 maj_stat, OM_uint32 min_stat, const char *fmt, va_list ap) +{ + vwarnx (fmt, ap); + gss_print_errors (maj_stat, min_stat); + exit (exitval); +} + +void +gss_err(int exitval, OM_uint32 maj_stat, OM_uint32 min_stat, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + gss_verr (exitval, maj_stat, min_stat, fmt, args); + va_end(args); +} + +gss_OID +select_mech(const char *mech) +{ + OM_uint32 maj_stat, min_stat; + gss_buffer_desc tok; + gss_OID oid; + +#if 0 + if (strcasecmp(mech, "krb5") == 0) + return GSS_KRB5_MECHANISM; + if (strcasecmp(mech, "spnego") == 0) + return GSS_SPNEGO_MECHANISM; +#endif + if (strcasecmp(mech, "no-oid") == 0) + return GSS_C_NO_OID; + + tok.value = (char *) mech; + tok.length = strlen(tok.value); + maj_stat = gss_str_to_oid(&min_stat, &tok, &oid); + if (GSS_ERROR(maj_stat)) { + gss_err(1, maj_stat, min_stat, "Unknown gssapi mechanims required"); + exit(1); + } + + return oid; +} + +void +print_gss_name(const char *prefix, gss_name_t name) +{ + OM_uint32 maj_stat, min_stat; + gss_buffer_desc name_token; + + maj_stat = gss_display_name (&min_stat, + name, + &name_token, + NULL); + if (GSS_ERROR(maj_stat)) + gss_err (1, maj_stat, min_stat, "gss_display_name"); + + fprintf (stderr, "%s `%.*s'\n", prefix, + (int)name_token.length, + (char *)name_token.value); + + gss_release_buffer (&min_stat, &name_token); + +} diff --git a/client/gss_common.h b/client/gss_common.h new file mode 100644 index 0000000..fc467ce --- /dev/null +++ b/client/gss_common.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * 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 Institute 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 BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE 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. + */ + +/* $Id$ */ + +void write_token (int sock, gss_buffer_t buf); +void read_token (int sock, gss_buffer_t buf); + +void gss_print_errors (OM_uint32 maj_stat, OM_uint32 min_stat); + +void gss_verr(int exitval, OM_uint32 maj_stat, OM_uint32 min_stat, const char *fmt, va_list ap) + __attribute__ ((format (printf, 4, 0))); + +void gss_err(int exitval, OM_uint32 maj_stat, OM_uint32 min_stat, const char *fmt, ...) + __attribute__ ((format (printf, 4, 5))); + +gss_OID select_mech(const char *); + +void print_gss_name(const char *, gss_name_t); diff --git a/client/http_client.c b/client/http_client.c new file mode 100644 index 0000000..0ace960 --- /dev/null +++ b/client/http_client.c @@ -0,0 +1,579 @@ +/* + * Copyright (c) 2003 - 2005 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * 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 Institute 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 BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE 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 "client_locl.h" +#include +#include +#include "gss_common.h" +#include "base64.h" + +/* + * A simplistic client implementing draft-brezak-spnego-http-04.txt + */ + +static int +do_connect (const char *hostname, const char *port) +{ + struct addrinfo *ai, *a; + struct addrinfo hints; + int error; + int s = -1; + + memset (&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + + error = getaddrinfo (hostname, port, &hints, &ai); + if (error) + errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error)); + + for (a = ai; a != NULL; a = a->ai_next) { + s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); + if (s < 0) + continue; + if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { + warn ("connect(%s)", hostname); + close (s); + continue; + } + break; + } + freeaddrinfo (ai); + if (a == NULL) + errx (1, "failed to contact %s", hostname); + + return s; +} + +static void +fdprintf(int s, const char *fmt, ...) +{ + size_t len; + ssize_t ret; + va_list ap; + char *str, *buf; + + va_start(ap, fmt); + vasprintf(&str, fmt, ap); + va_end(ap); + + if (str == NULL) + errx(1, "vasprintf"); + + buf = str; + len = strlen(buf); + while (len) { + ret = write(s, buf, len); + if (ret == 0) + err(1, "connection closed"); + else if (ret < 0) + err(1, "error"); + len -= ret; + buf += ret; + } + free(str); +} + +//static int version_flag; +static int verbose_flag; +static int mutual_flag = 1; +static int delegate_flag; +static char *mech = NULL; +static char *port_str = "http"; +static char *gss_service = "HTTP"; +static char *user = NULL; +static char *pwd = NULL; + +static struct option const long_opts[] = { + { "help", no_argument, 0, 'h' }, + { "mech", required_argument, 0, 'm' }, + { "password", required_argument, 0, 'p' }, + { "gss-service", required_argument, 0, 's' }, + { "user", required_argument, 0, 'u' }, + { NULL, 0, NULL, 0 } +}; + +static const char *short_opts = "hm:p:s:u:"; + +static void +usage(int ret) +{ + fprintf(stderr, "Usage: http_client [OPTION] URL\n" + "-m mech, --mech=mech gssapi mech to use\n" + "-p pass, --password=pass password to acquire credentials\n" + "-s service, --gss-service=service gssapi service to use\n" + "-u user, --user=user client's username\n"); + exit(ret); +} + +/* + * + */ + +struct http_req { + char *response; + char **headers; + int num_headers; + void *body; + size_t body_size; +}; + + +static void +http_req_zero(struct http_req *req) +{ + req->response = NULL; + req->headers = NULL; + req->num_headers = 0; + req->body = NULL; + req->body_size = 0; +} + +static void +http_req_free(struct http_req *req) +{ + int i; + + free(req->response); + for (i = 0; i < req->num_headers; i++) + free(req->headers[i]); + free(req->headers); + free(req->body); + http_req_zero(req); +} + +static const char * +http_find_header(struct http_req *req, const char *header) +{ + int i, len = strlen(header); + + for (i = 0; i < req->num_headers; i++) { + if (strncasecmp(header, req->headers[i], len) == 0) { + return req->headers[i] + len + 1; + } + } + return NULL; +} + + +static int +http_query(int s, const char *host, const char *page, + char **headers, int num_headers, struct http_req *req) +{ + enum { RESPONSE, HEADER, BODY } state; + ssize_t ret; +// char in_buf[4096], *in_ptr = in_buf; + char in_buf[8000], *in_ptr = in_buf; + size_t in_len = 0; + int i; + size_t content_length = 0; + + http_req_zero(req); + + fdprintf(s, "GET %s HTTP/1.0\r\n", page); + for (i = 0; i < num_headers; i++) + fdprintf(s, "%s\r\n", headers[i]); + fdprintf(s, "Keep-Alive: 115\r\n"); + fdprintf(s, "Connection: keep-alive\r\n"); + fdprintf(s, "Host: %s\r\n\r\n", host); + + state = RESPONSE; + + while (1) { + ret = read (s, in_ptr, sizeof(in_buf) - in_len - 1); + if (ret == 0) + break; + else if (ret < 0) + err (1, "read: %lu", (unsigned long)ret); + + in_buf[ret + in_len] = '\0'; + + if (state == HEADER || state == RESPONSE) { + char *p; + + in_len += ret; + in_ptr += ret; + + while (1) { + p = strstr(in_buf, "\r\n"); + + if (p == NULL) { + break; + } else if (p == in_buf) { + memmove(in_buf, in_buf + 2, sizeof(in_buf) - 2); + state = BODY; + in_len -= 2; + in_ptr -= 2; + break; + } else if (state == RESPONSE) { + req->response = strndup(in_buf, p - in_buf); + state = HEADER; + } else { + req->headers = realloc(req->headers, + (req->num_headers + 1) * sizeof(req->headers[0])); + req->headers[req->num_headers] = strndup(in_buf, p - in_buf); + if (req->headers[req->num_headers] == NULL) + errx(1, "strdup"); + if (strncmp(req->headers[req->num_headers], "Content-Length:", 15) == 0) + content_length = atoi(req->headers[req->num_headers] + 16); + req->num_headers++; + } + memmove(in_buf, p + 2, sizeof(in_buf) - (p - in_buf) - 2); + in_len -= (p - in_buf) + 2; + in_ptr -= (p - in_buf) + 2; + } + } + + if (state == BODY) { + + req->body = realloc(req->body, req->body_size + in_len + 1); + + memcpy((char *)req->body + req->body_size, in_buf, in_len); + req->body_size += in_len; + ((char *)req->body)[req->body_size] = '\0'; + + if (content_length && req->body_size == content_length) + break; + + in_ptr = in_buf; + in_len = 0; + } +// else +// abort(); + } + +#if 0 + if (verbose_flag) { + int i; + printf("response: %s\n", req->response); + for (i = 0; i < req->num_headers; i++) + printf("header[%d] %s\n", i, req->headers[i]); + printf("body: %.*s\n", (int)req->body_size, (char *)req->body); + } +#endif + + return 0; +} + +static int +do_http(const char *host, const char *page, gss_OID mech_oid, gss_cred_id_t cred) +{ + struct http_req req; + int i, done, print_body, gssapi_done, gssapi_started; + char *headers[10]; /* XXX */ + int num_headers; + gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT; + gss_name_t server = GSS_C_NO_NAME; + OM_uint32 flags = 0; + int s; + + flags = 0; + if (delegate_flag) + flags |= GSS_C_DELEG_FLAG; + if (mutual_flag) + flags |= GSS_C_MUTUAL_FLAG; + + done = 0; + num_headers = 0; + gssapi_done = 1; + gssapi_started = 0; + + s = do_connect(host, port_str); + if (s < 0) + errx(1, "connection failed"); + + do { + print_body = 0; + + http_query(s, host, page, headers, num_headers, &req); + for (i = 0 ; i < num_headers; i++) + free(headers[i]); + num_headers = 0; + + if (strstr(req.response, " 200 ") != NULL) { + print_body = 1; + done = 1; + } else if (strstr(req.response, " 401 ") != NULL) { + if (http_find_header(&req, "WWW-Authenticate:") == NULL) + errx(1, "Got %s but missed `WWW-Authenticate'", req.response); + gssapi_done = 0; + } + + if (!gssapi_done) { + const char *h = http_find_header(&req, "WWW-Authenticate:"); + if (h == NULL) + errx(1, "Got %s but missed `WWW-Authenticate'", req.response); + + if (strncasecmp(h, "GSSAPI", 6) == 0) { + OM_uint32 maj_stat, min_stat; + gss_buffer_desc input_token, output_token; + + if (verbose_flag) + printf("Negotiate found\n"); + +#if 1 + if (server == GSS_C_NO_NAME) { + char *name; + asprintf(&name, "%s@%s", gss_service, host); + input_token.length = strlen(name); + input_token.value = name; + + maj_stat = gss_import_name(&min_stat, + &input_token, + GSS_C_NT_HOSTBASED_SERVICE, + &server); + if (GSS_ERROR(maj_stat)) + gss_err (1, maj_stat, min_stat, "gss_inport_name"); + free(name); + input_token.length = 0; + input_token.value = NULL; + } +#endif + +// i = 9; + i = 6; + while(h[i] && isspace((unsigned char)h[i])) + i++; + if (h[i] != '\0') { + int len = strlen(&h[i]); + if (len == 0) + errx(1, "invalid Negotiate token"); + input_token.value = malloc(len); + len = base64_decode(&h[i], input_token.value); + if (len < 0) + errx(1, "invalid base64 Negotiate token %s", &h[i]); + input_token.length = len; + } else { + if (gssapi_started) + errx(1, "Negotiate already started"); + gssapi_started = 1; + + input_token.length = 0; + input_token.value = NULL; + } + + maj_stat = + gss_init_sec_context(&min_stat, + cred, + &context_hdl, + server, + mech_oid, + flags, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, + NULL, + &output_token, + NULL, + NULL); + if (GSS_ERROR(maj_stat)) + gss_err (1, maj_stat, min_stat, "gss_init_sec_context"); + else if (maj_stat & GSS_S_CONTINUE_NEEDED) + gssapi_done = 0; + else { + gss_name_t targ_name, src_name; + gss_buffer_desc name_buffer; + gss_OID mech_type; + + gssapi_done = 1; + + printf("\nNegotiate done: %s\n", mech); + + maj_stat = gss_inquire_context(&min_stat, + context_hdl, + &src_name, + &targ_name, + NULL, + &mech_type, + NULL, + NULL, + NULL); + if (GSS_ERROR(maj_stat)) + gss_err (1, maj_stat, min_stat, "gss_inquire_context"); + + maj_stat = gss_display_name(&min_stat, + src_name, + &name_buffer, + NULL); + if (GSS_ERROR(maj_stat)) + gss_err (1, maj_stat, min_stat, "gss_display_name"); + + printf("Source: %.*s\n", + (int)name_buffer.length, + (char *)name_buffer.value); + + gss_release_buffer(&min_stat, &name_buffer); + + maj_stat = gss_display_name(&min_stat, + targ_name, + &name_buffer, + NULL); + if (GSS_ERROR(maj_stat)) + gss_err (1, maj_stat, min_stat, "gss_display_name"); + + printf("Target: %.*s\n", + (int)name_buffer.length, + (char *)name_buffer.value); + + gss_release_name(&min_stat, &targ_name); + gss_release_buffer(&min_stat, &name_buffer); + } + + if (output_token.length) { + char *neg_token; + + base64_encode(output_token.value, + output_token.length, + &neg_token); + + asprintf(&headers[0], "Authorization: GSSAPI %s", + neg_token); + num_headers = 1; + free(neg_token); + gss_release_buffer(&min_stat, &output_token); + } + if (input_token.length) + free(input_token.value); + + } else + done = 1; + } else + done = 1; + + if (verbose_flag) { + printf("%s\n\n", req.response); + + for (i = 0; i < req.num_headers; i++) + printf("%s\n", req.headers[i]); + printf("\n"); + } + if (print_body || verbose_flag) + printf("%.*s\n", (int)req.body_size, (char *)req.body); + + http_req_free(&req); + } while (!done); + + close(s); + + if (gssapi_done == 0) + errx(1, "gssapi not done but http dance done"); + + return 0; +} + +int +main(int argc, char *argv[]) +{ + int c, ret; + gss_buffer_desc token; + gss_OID mech_oid = GSS_C_NO_OID; + OM_uint32 maj_stat, min_stat; + gss_name_t gss_username = GSS_C_NO_NAME; + gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; + char *p, *host, *page; + + while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) != EOF) { + switch (c) { + case 'h': + usage(0); + case 'm': + mech = optarg; + mech_oid = select_mech(mech); + break; + case 'p': + pwd = optarg; + break; + case 's': + gss_service = optarg; + break; + case 'u': + user = optarg; + break; + default: + usage(1); + } + } + + if (optind >= argc) + usage(1); + + p = argv[optind]; + if (strncmp(p, "http://", 7) == 0) + p += 7; + host = p; + p = strchr(host, '/'); + if (p) { + page = strdup(p); + *p = '\0'; + } else + page = strdup("/"); + + if (user) { + token.value = user; + token.length = strlen(token.value); + maj_stat = gss_import_name(&min_stat, &token, + GSS_C_NT_USER_NAME, + &gss_username); + if (GSS_ERROR(maj_stat)) + gss_err(1, maj_stat, min_stat, "Invalid user name %s", user); + } + + if (pwd) { + gss_OID_set_desc mechs, *mechsp = GSS_C_NO_OID_SET; + + token.value = pwd; + token.length = strlen(token.value); + mechs.elements = mech_oid; + mechs.count = 1; + mechsp = &mechs; + maj_stat = gss_acquire_cred_with_password(&min_stat, + gss_username, &token, 0, + mechsp, GSS_C_INITIATE, + &cred, NULL, NULL); + if (GSS_ERROR(maj_stat)) + gss_err(1, maj_stat, min_stat, "Failed to load initial credentials"); + } + + ret = do_http(host, page, mech_oid, cred); + + if (gss_username != GSS_C_NO_NAME) + gss_release_name(&min_stat, &gss_username); + + if (cred != GSS_C_NO_CREDENTIAL) + gss_release_cred(&min_stat, &cred); + + free(page); + + return (ret); +} diff --git a/client/net_read.c b/client/net_read.c new file mode 100644 index 0000000..63bd1b1 --- /dev/null +++ b/client/net_read.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1995, 1996, 1997, 1998 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * 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 Institute 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 BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE 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 "client_locl.h" + +/* + * Like read but never return partial data. + */ + +#ifndef _WIN32 + +//ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL +ssize_t +net_read (int fd, void *buf, size_t nbytes) +{ + char *cbuf = (char *)buf; + ssize_t count; + size_t rem = nbytes; + + while (rem > 0) { + count = read (fd, cbuf, rem); + if (count < 0) { + if (errno == EINTR) + continue; + else + return count; + } else if (count == 0) { + return count; + } + cbuf += count; + rem -= count; + } + return nbytes; +} + +#else + +ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL +net_read(rk_socket_t sock, void *buf, size_t nbytes) +{ + char *cbuf = (char *)buf; + ssize_t count; + size_t rem = nbytes; + +#ifdef SOCKET_IS_NOT_AN_FD + int use_read = 0; +#endif + + while (rem > 0) { +#ifdef SOCKET_IS_NOT_AN_FD + if (use_read) + count = _read (sock, cbuf, rem); + else + count = recv (sock, cbuf, rem, 0); + + if (use_read == 0 && + rk_IS_SOCKET_ERROR(count) && + rk_SOCK_ERRNO == WSAENOTSOCK) { + use_read = 1; + + count = _read (sock, cbuf, rem); + } +#else + count = recv (sock, cbuf, rem, 0); +#endif + if (count < 0) { + + /* With WinSock, the error EINTR (WSAEINTR), is used to + indicate that a blocking call was cancelled using + WSACancelBlockingCall(). */ + +#ifndef HAVE_WINSOCK + if (rk_SOCK_ERRNO == EINTR) + continue; +#endif + return count; + } else if (count == 0) { + return count; + } + cbuf += count; + rem -= count; + } + return nbytes; +} + +#endif diff --git a/client/net_write.c b/client/net_write.c new file mode 100644 index 0000000..6fca8c6 --- /dev/null +++ b/client/net_write.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 1995, 1996, 1997, 1998 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * 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 Institute 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 BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE 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 "client_locl.h" + +/* + * Like write but never return partial data. + */ + +#ifndef _WIN32 + +//ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL +ssize_t +net_write (int fd, const void *buf, size_t nbytes) +{ + const char *cbuf = (const char *)buf; + ssize_t count; + size_t rem = nbytes; + + while (rem > 0) { + count = write (fd, cbuf, rem); + if (count < 0) { + if (errno == EINTR) + continue; + else + return count; + } + cbuf += count; + rem -= count; + } + return nbytes; +} + +#else + +ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL +net_write(rk_socket_t sock, const void *buf, size_t nbytes) +{ + const char *cbuf = (const char *)buf; + ssize_t count; + size_t rem = nbytes; +#ifdef SOCKET_IS_NOT_AN_FD + int use_write = 0; +#endif + + while (rem > 0) { +#ifdef SOCKET_IS_NOT_AN_FD + if (use_write) + count = _write (sock, cbuf, rem); + else + count = send (sock, cbuf, rem, 0); + + if (use_write == 0 && + rk_IS_SOCKET_ERROR(count) && + rk_SOCK_ERRNO == WSAENOTSOCK) { + use_write = 1; + + count = _write (sock, cbuf, rem); + } +#else + count = send (sock, cbuf, rem, 0); +#endif + if (count < 0) { + if (errno == EINTR) + continue; + else + return count; + } + cbuf += count; + rem -= count; + } + return nbytes; +} + +#endif -- 2.1.4