summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfergus.henderson <fergushenderson@users.noreply.github.com>2009-07-23 02:16:18 +0000
committerfergus.henderson <fergushenderson@users.noreply.github.com>2009-07-23 02:16:18 +0000
commit8a5f0e583a3593803c41ef0719d78e79c38cbcec (patch)
treef7f781c1b4a4e47775b6b692b7b4e148fa79ea8d
parent12f786698f9be16fc9d0b76373665c87ab5df5ac (diff)
downloaddistcc-git-8a5f0e583a3593803c41ef0719d78e79c38cbcec.tar.gz
Apply patch from Ian.Baker@cern.ch:
Optional GSS-API Functionality. This patch implements mutual authentication, out of sequence and replay detection using the GSS-API. The changes implemented are optional and are turned off by default. This option is specified to the client through an environment variable as is the name of the server principal to authenticate. Currently the server principal can be left unspecified and a default based on the host keytab will be used. This option is specified to the daemon through a command line option, with the name of the principal whose credentials the daemon should use specified as an environment variable. A simple handshake is exchanged between the client and server in order to prevent unecessary delays and protocol derailments when mixing authenticating and non-authenticating clients and servers. Revised based on review comments. GSS-API authentication is now implemented as a per host option. Revised further by me (Fergus Henderson) to fix a spelling error and to rename the per host option from ",gssapi" to ",auth".
-rw-r--r--Makefile.in11
-rw-r--r--configure.ac26
-rw-r--r--man/distcc.147
-rw-r--r--man/distccd.117
-rw-r--r--src/auth.h42
-rw-r--r--src/auth_common.c229
-rw-r--r--src/auth_distcc.c362
-rw-r--r--src/auth_distccd.c364
-rw-r--r--src/clirpc.c6
-rw-r--r--src/daemon.c18
-rw-r--r--src/distcc.c30
-rw-r--r--src/dopt.c51
-rw-r--r--src/dopt.h4
-rw-r--r--src/dparent.c9
-rw-r--r--src/dsignal.c9
-rw-r--r--src/exitcode.h5
-rw-r--r--src/help.c3
-rw-r--r--src/hosts.c9
-rw-r--r--src/hosts.h5
-rw-r--r--src/lock.c6
-rw-r--r--src/remote.c24
-rw-r--r--src/serve.c30
22 files changed, 1301 insertions, 6 deletions
diff --git a/Makefile.in b/Makefile.in
index d544966..29a66f7 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -239,7 +239,8 @@ common_obj = src/arg.o src/argutil.o \
src/hosts.o src/hostfile.o \
src/implicit.o src/loadfile.o \
lzo/minilzo.o \
- @ZEROCONF_COMMON_OBJS@
+ @ZEROCONF_COMMON_OBJS@ \
+ @AUTH_COMMON_OBJS@
distcc_obj = src/backoff.o \
src/climasq.o src/clinet.o src/clirpc.o \
@@ -251,6 +252,7 @@ distcc_obj = src/backoff.o \
src/include_server_if.o \
src/where.o \
@ZEROCONF_DISTCC_OBJS@ \
+ @AUTH_DISTCC_OBJS@ \
src/emaillog.o \
$(common_obj)
@@ -263,6 +265,7 @@ distccd_obj = src/access.o \
src/stats.o \
src/fix_debug_info.o \
@ZEROCONF_DISTCCD_OBJS@ \
+ @AUTH_DISTCCD_OBJS@ \
$(common_obj) @BUILD_POPT@
lsdistcc_obj = src/lsdistcc.o \
@@ -305,6 +308,7 @@ h_compile_obj = src/h_compile.o $(common_obj) src/compile.o src/timefile.o \
# All source files, for the purposes of building the distribution
SRC = src/stats.c \
src/access.c src/arg.c src/argutil.c \
+ src/auth_common.c src/auth_distcc.c src/auth_distccd.c \
src/backoff.c src/bulk.c \
src/cleanup.c \
src/climasq.c src/clinet.c src/clirpc.c src/compile.c \
@@ -339,6 +343,7 @@ SRC = src/stats.c \
HEADERS = src/stats.h \
src/access.h \
+ src/auth.h \
src/bulk.h \
src/clinet.h src/compile.h \
src/daemon.h \
@@ -356,8 +361,8 @@ HEADERS = src/stats.h \
src/exec.h src/lock.h src/where.h src/srvnet.h \
src/rslave.h \
src/dotd.h src/include_server_if.h \
- src/emaillog.h \
- src/va_copy.h \
+ src/emaillog.h \
+ src/va_copy.h \
src/zeroconf.h
conf_dir = packaging/RedHat/conf
diff --git a/configure.ac b/configure.ac
index 757dbe3..fab20b4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -491,6 +491,32 @@ if test x"$with_avahi" != xno; then
AC_SUBST(ZEROCONF_DISTCCD_OBJS)
fi
+AUTH_COMMON_OBJS=""
+AUTH_DISTCC_OBJS=""
+AUTH_DISTCCD_OBJS=""
+
+#check for GSS-API
+AC_ARG_WITH([auth],
+ [AS_HELP_STRING([--with-auth],
+ [provide mutual authentication services via the GSS-API])])
+
+if test x"$with_auth" = xyes; then
+ AC_SEARCH_LIBS([gss_init_sec_context],
+ [gssapi gssapi_krb5],
+ AC_DEFINE(HAVE_GSSAPI, 1, [Define if the GSS_API is available])
+ AUTH_COMMON_OBJS="src/auth_common.o"
+ AUTH_DISTCC_OBJS="src/auth_distcc.o"
+ AUTH_DISTCCD_OBJS="src/auth_distccd.o",
+ AC_MSG_FAILURE([--with-auth was given but no GSS-API library found])
+ AUTH_COMMON_OBJS=""
+ AUTH_DISTCC_OBJS=""
+ AUTH_DISTCCD_OBJS="")
+fi
+
+AC_SUBST(AUTH_COMMON_OBJS)
+AC_SUBST(AUTH_DISTCC_OBJS)
+AC_SUBST(AUTH_DISTCCD_OBJS)
+
ACX_PTHREAD
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
diff --git a/man/distcc.1 b/man/distcc.1
index a1bacd8..283f868 100644
--- a/man/distcc.1
+++ b/man/distcc.1
@@ -136,6 +136,24 @@ Wrap your build inside the pump command, here assuming 10 servers:
.RS
$ pump make -j20 CC=distcc
.RE
+
+.SH "QUICKSTART FOR DISTCC-GSSAPI MODE"
+Proceed as per the
+.B QUICKSTART
+but in Step 3, specify that the remote hosts are to mutually
+authenticate with the client:
+
+.RS
+$ export DISTCC_HOSTS='--randomize localhost red,auth green,auth blue,auth'
+.RE
+
+If distccd runs under a specific principal name then execute the
+following command prior to step 4:
+
+.RS
+export DISTCC_PRINICIPAL=<name>
+.RE
+
.SH "HOW PLAIN (NON-PUMP) DISTCC WORKS"
distcc only ever runs the compiler and assembler remotely. With plain distcc,
the preprocessor must always run locally because it needs to access various
@@ -227,6 +245,12 @@ directories of the compiler installation.
See the \fBinclude_server\fR(1) manual for more information on symptoms and
causes of violations of distcc-pump mode assumptions.
+.SH "HOW DISTCC-GSSAPI MODE WORKS"
+
+In this mode distcc will use the GSS-API framework to access the currently
+configured security mechanism and perform mutual authentication with the
+daemon.
+
.SH "OPTION SUMMARY"
Most options passed to distcc are interpreted as compiler options.
The following options are understood by distcc itself.
@@ -288,6 +312,14 @@ to all servers.
By default this will be four times the number of hosts in the host list,
unless the /LIMIT option was used in the host list.
See the Host Specifications section.
+
+.TP
+.B --show-principal
+Displays the name of the distccd security principal extracted from the
+environment.
+.B This option is only available if distcc was compiled with
+.B the --with-auth configure option.
+
.SH "INSTALLING DISTCC"
There are three different ways to call distcc, to suit different
circumstances:
@@ -482,7 +514,7 @@ The syntax is
OLDSTYLE_TCP_HOST = HOSTID[/LIMIT][:PORT][OPTIONS]
HOSTID = HOSTNAME | IPV4 | IPV6
OPTIONS = ,OPTION[OPTIONS]
- OPTION = lzo | cpp
+ OPTION = lzo | cpp | auth
GLOBAL_OPTION = --randomize
ZEROCONF = +zeroconf
.fi
@@ -540,6 +572,9 @@ Enables LZO compression for this TCP or SSH host.
Enables distcc-pump mode for this host. Note: the build command must be
wrapped in the pump script in order to start the include server.
.TP
+.B ,auth
+Enables GSSAPI-based mutual authentication for this host.
+.TP
.B --randomize
Randomize the order of the host list before execution.
.TP
@@ -691,6 +726,10 @@ No hosts defined and fallbacks disabled.
.TP
118
Timeout.
+.TP
+119
+GSS-API - Catchall error code for GSS-API related errors.
+
.SH "FILES"
If $DISTCC_HOSTS is not set, distcc reads a host list from either
.B $DISTCC_DIR/hosts
@@ -763,6 +802,12 @@ and the succeeding local compilation.
.TP
.B "DCC_EMAILLOG_WHOM_TO_BLAME"
The email address for discrepancy email; the default is "distcc-pump-errors".
+.TP
+.B "DISTCC_PRINCIPAL"
+If set, specifies the name of the principal that distccd runs under, and is used
+to authenticate the server to the client.
+.B This environment variable is only used if distcc was compiled with
+.B the --with-auth configure option and the ",auth" per host option is specified.
.SH "CROSS COMPILING"
Cross compilation means building programs to run on a
machine with a different processor, architecture, or
diff --git a/man/distccd.1 b/man/distccd.1
index c855a03..ec1b007 100644
--- a/man/distccd.1
+++ b/man/distccd.1
@@ -217,6 +217,17 @@ name or IP address in their distcc host list: the distcc clients can
just use "+zeroconf" in their distcc host lists.
.B This option is only available if distccd was compiled with
.B Avahi support enabled.
+.TP
+.B --auth
+Peform GSS-API based mutual authentication.
+.B This option is only available if distccd was compiled with
+.B the --with-auth configure option.
+.TP
+.B --show-principal
+Displays the name of the distccd security principal extracted from the
+environment.
+.B This option is only available if distccd was compiled with
+.B the --with-auth configure option.
.SH "SEARCH PATHS"
.PP
distcc can pass either a relative or an absolute name for the compiler
@@ -278,6 +289,12 @@ On Linux, turn on the TCP_DEFER_ACCEPT socket option. Defaults to on.
.B "TMPDIR"
Directory for temporary files such as preprocessor output. By default
/tmp/ is used.
+.TP
+.B "DISTCCD_PRINCIPAL"
+If set, specifies the name of the principal that distccd runs under, and is used
+to authenticate with the client.
+.B This environment variable is only used if distccd was compiled with
+.B the --with-auth configure option and if distccd is run with the --auth option.
.SH "SEE ALSO"
\fBdistcc\fR(1), \fBpump\fR(1), \fBinclude_server\fR(1), \fBgcc\fR(1),
\fBmake\fR(1), and \fBccache\fR(1)
diff --git a/src/auth.h b/src/auth.h
new file mode 100644
index 0000000..a0d6b05
--- /dev/null
+++ b/src/auth.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2008 CERN
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Author: Ian Baker */
+
+#include <gssapi/gssapi.h>
+
+/* Handshake exchange character. */
+#define HANDSHAKE '*'
+/* Notification of server access. */
+#define ACCESS 'y'
+/* Notification of server access denied. */
+#define NO_ACCESS 'n'
+
+int dcc_gssapi_acquire_credentials(void);
+void dcc_gssapi_release_credentials(void);
+int dcc_gssapi_check_client(int to_net_fd, int from_net_fd);
+int dcc_gssapi_perform_requested_security(int to_net_fd,
+ int from_net_fd);
+void dcc_gssapi_status_to_log(OM_uint32 status_code, int status_type);
+void dcc_gssapi_cleanup(gss_buffer_desc *input_tok,
+ gss_buffer_desc *output_tok,
+ gss_name_t *name);
+int dcc_gssapi_compare_flags(OM_uint32 req_flags, OM_uint32 ret_flags);
+void dcc_gssapi_delete_ctx(gss_ctx_id_t *ctx_handle);
+int send_token(int sd, gss_buffer_t token);
+int recv_token(int sd, gss_buffer_t token);
diff --git a/src/auth_common.c b/src/auth_common.c
new file mode 100644
index 0000000..9d9d6ef
--- /dev/null
+++ b/src/auth_common.c
@@ -0,0 +1,229 @@
+/* Copyright (C) 2008 CERN
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Author: Ian Baker */
+
+#include <config.h>
+
+#ifdef HAVE_GSSAPI
+#include <arpa/inet.h>
+#endif
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "auth.h"
+#include "distcc.h"
+#include "exitcode.h"
+#include "netutil.h"
+#include "rpc.h"
+#include "trace.h"
+
+/*
+ * Provide a textual representation of a GSS-API or mechanism-specific
+ * status code and write this to the log file.
+ *
+ * @param status_code. Status code to convert.
+ *
+ * @param status_type. The type of the status code, either GSS_C_GSS_CODE
+ * for a GSS-API status code or GSS_C_MECH_CODE
+ * for a mechanism specific status code.
+ */
+void dcc_gssapi_status_to_log(OM_uint32 status_code, int status_type) {
+ gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER;
+ OM_uint32 major_status, minor_status;
+ OM_uint32 message_ctx = 0;
+
+ do {
+ major_status = gss_display_status(&minor_status,
+ status_code,
+ status_type,
+ GSS_C_NULL_OID,
+ &message_ctx,
+ &status_string);
+
+ rs_log_info("%s: %s", (status_type == GSS_C_GSS_CODE) ? "GSSAPI" : "Mech" ,
+ (char *) status_string.value);
+ gss_release_buffer(&minor_status, &status_string);
+
+ } while (message_ctx != 0);
+}
+
+/*
+ * Perform a cleanup on specified GSS-API internal data formats.
+ *
+ * @param input_tok. The input token to release.
+ *
+ * @param output_tok. The output token to release.
+ *
+ * @param name. The name to release.
+ */
+void dcc_gssapi_cleanup(gss_buffer_desc *input_tok,
+ gss_buffer_desc *output_tok,
+ gss_name_t *name) {
+ OM_uint32 major_status, minor_status;
+
+ if (input_tok != NULL) {
+ if ((major_status = gss_release_buffer(&minor_status,
+ input_tok)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release buffer.");
+ }
+ }
+
+ if (output_tok != NULL) {
+ if ((major_status = gss_release_buffer(&minor_status,
+ output_tok)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release buffer.");
+ }
+ }
+
+ if (name != NULL) {
+ if ((major_status = gss_release_name(&minor_status,
+ name)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release name.");
+ }
+ }
+}
+
+/*
+ * Perform a comparison of two sets of flags representing security
+ * services. At the moment we only check for mutual authentication,
+ * replay and out of sequence detection, but this function could be
+ * extended to include others.
+ *
+ * @param req_flags. The first set of flags to be compared and
+ * represents what is required by the client
+ * or server.
+ *
+ * @param ret_flags. The second set of flags to be compared and
+ * what is provided/returned by the peer.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+int dcc_gssapi_compare_flags(OM_uint32 req_flags, OM_uint32 ret_flags) {
+ if (req_flags & GSS_C_MUTUAL_FLAG) {
+ if (ret_flags & GSS_C_MUTUAL_FLAG) {
+ rs_log_info("Mutual authentication requested and granted.");
+ } else {
+ rs_log_crit("Requested security services don't match those returned.");
+ return EXIT_GSSAPI_FAILED;
+ }
+ }
+
+ if (req_flags & GSS_C_REPLAY_FLAG) {
+ if (ret_flags & GSS_C_REPLAY_FLAG) {
+ rs_log_info("Replay detection requested and granted.");
+ } else {
+ rs_log_crit("Requested security services don't match those returned.");
+ return EXIT_GSSAPI_FAILED;
+ }
+ }
+
+ if (req_flags & GSS_C_SEQUENCE_FLAG) {
+ if (ret_flags & GSS_C_SEQUENCE_FLAG) {
+ rs_log_info("Out of sequence detection requested and granted.");
+ } else {
+ rs_log_crit("Requested security services don't match those returned.");
+ return EXIT_GSSAPI_FAILED;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Delete a specified secure context from the current process.
+ * The output buffer token is not used as we do not notify the
+ * peer.
+ *
+ * @param ctx_handle. The secure context to delete.
+ */
+void dcc_gssapi_delete_ctx(gss_ctx_id_t *ctx_handle) {
+ OM_uint32 major_status, minor_status;
+
+ if (ctx_handle != NULL) {
+ if (*ctx_handle != GSS_C_NO_CONTEXT) {
+ if ((major_status = gss_delete_sec_context(&minor_status,
+ ctx_handle,
+ GSS_C_NO_BUFFER)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to delete context.");
+ }
+ }
+ }
+}
+
+/*
+ * Send the specified token. First we transmit the token length,
+ * then the token itself.
+ *
+ * @param token. The token to be sent.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+int send_token(int sd, gss_buffer_t token) {
+ int ret;
+
+ if ((ret = dcc_x_token_int(sd, "TLEN", token->length)) != 0) {
+ return ret;
+ }
+
+ if ((ret = dcc_writex(sd, token->value, token->length)) != 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Receive a token. First we receive the token length,
+ * then the token itself.
+ *
+ * @param token. The token to be received.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+int recv_token(int sd, gss_buffer_t token) {
+ int ret;
+ unsigned length;
+
+ if ((ret = dcc_r_token_int(sd, "TLEN", &length)) != 0) {
+ return ret;
+ }
+
+ if (length > INT_MAX || length == 0) {
+ rs_log_error("Malformed token length.");
+ return EXIT_IO_ERROR;
+ }
+
+ token->length = length;
+ token->value = malloc(length);
+
+ if (token->value == NULL && length != 0) {
+ rs_log_error("malloc failed : %lu bytes: out of memory.",
+ (unsigned long) length);
+ return EXIT_OUT_OF_MEMORY;
+ }
+
+ if ((ret = dcc_readx(sd, token->value, token->length)) != 0) {
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/src/auth_distcc.c b/src/auth_distcc.c
new file mode 100644
index 0000000..ebd0d0e
--- /dev/null
+++ b/src/auth_distcc.c
@@ -0,0 +1,362 @@
+/* Copyright (C) 2008 CERN
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Author: Ian Baker */
+
+#include <config.h>
+
+#ifdef HAVE_GSSAPI
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "auth.h"
+#include "distcc.h"
+#include "exitcode.h"
+#include "netutil.h"
+#include "trace.h"
+
+static int dcc_gssapi_establish_secure_context(int to_net_sd,
+ int from_net_sd,
+ OM_uint32 req_flags,
+ OM_uint32 *ret_flags);
+static int dcc_gssapi_send_handshake(int to_net_sd, int from_net_sd);
+static int dcc_gssapi_recv_notification(int sd);
+
+/**
+ * Global security context in case other services are implemented in the
+ * future.
+ */
+gss_ctx_id_t distcc_ctx_handle = GSS_C_NO_CONTEXT;
+
+/*
+ * Perform any requested security. Message replay and out of sequence
+ * detection are given in addition to mutual authentication.
+ *
+ * @param to_net_sd. Socket to write to.
+ *
+ * @param from_net_sd. Socket to read from.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+int dcc_gssapi_perform_requested_security(int to_net_sd,
+ int from_net_sd) {
+ int ret;
+ OM_uint32 req_flags, ret_flags;
+
+ req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+
+ if ((ret = dcc_gssapi_establish_secure_context(to_net_sd,
+ from_net_sd,
+ req_flags,
+ &ret_flags)) != 0) {
+ return ret;
+ }
+
+ if ((ret = dcc_gssapi_compare_flags(req_flags, ret_flags)) != 0) {
+ dcc_gssapi_delete_ctx(&distcc_ctx_handle);
+ return ret;
+ }
+
+ if ((ret = dcc_gssapi_recv_notification(from_net_sd)) != 0) {
+ dcc_gssapi_delete_ctx(&distcc_ctx_handle);
+ return ret;
+ }
+
+ rs_log_info("Authentication complete - happy compiling!");
+
+ return 0;
+}
+
+/*
+ * Establish a secure context using the GSS-API. A handshake is attempted in
+ * order to detect a non-authenticating server. The server IP address is obtained
+ * from the socket and is used to perform an fqdn lookup in case a DNS alias is
+ * used as a host spec, this ensures we authenticate against the correct server.
+ * We attempt to extract the server principal name, a service, from the
+ * environment, if it is not specified we use "host/" as a default.
+ *
+ * @pram to_net_sd. Socket to write to.
+ *
+ * @pram from_net_sd. Socket to read from.
+ *
+ * @param req_flags. A representation of the security services to
+ * be requested.
+ *
+ * @param ret_flags. A representation of the security services
+ * provided/supported by the underlying mechanism
+ * to be returned to the invoking function.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+static int dcc_gssapi_establish_secure_context(int to_net_sd,
+ int from_net_sd,
+ OM_uint32 req_flags,
+ OM_uint32 *ret_flags) {
+ char *ext_princ_name = NULL;
+ char *full_name = NULL;
+ char *princ_env_val = NULL;
+ gss_buffer_desc input_tok = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_tok = GSS_C_EMPTY_BUFFER;
+ gss_name_t int_serv_name;
+ gss_OID name_type;
+ int ret;
+ OM_uint32 major_status, minor_status, return_status;
+ socklen_t addr_len;
+ struct hostent *hp;
+ struct sockaddr_in addr;
+
+ addr_len = sizeof(addr);
+
+ if ((ret = getpeername(to_net_sd, &addr, &addr_len)) != 0) {
+ rs_log_error("Failed to look up peer address using socket \"%d\": %s.",
+ to_net_sd,
+ hstrerror(h_errno));
+ return EXIT_CONNECT_FAILED;
+ }
+
+ rs_log_info("Successfully looked up IP address %s using socket %d.",
+ inet_ntoa(addr.sin_addr),
+ to_net_sd);
+
+ if ((hp = gethostbyaddr((char *) &addr.sin_addr,
+ sizeof(addr.sin_addr),
+ AF_INET)) == NULL) {
+ rs_log_error("Failed to look up host by address \"%s\": %s.",
+ inet_ntoa(addr.sin_addr),
+ hstrerror(h_errno));
+ return EXIT_CONNECT_FAILED;
+ }
+
+ rs_log_info("Successfully looked up host %s using IP address %s.",
+ hp->h_name,
+ inet_ntoa(addr.sin_addr));
+
+ if ((full_name = malloc(strlen(hp->h_name) + 1)) == NULL) {
+ rs_log_error("malloc failed : %ld bytes: out of memory.",
+ (long) (strlen(hp->h_name) + 1));
+ return EXIT_OUT_OF_MEMORY;
+ }
+
+ strcpy(full_name, hp->h_name);
+
+ if ((princ_env_val = getenv("DISTCC_PRINCIPAL"))) {
+ if (asprintf(&ext_princ_name, "%s@%s", princ_env_val, full_name) < 0) {
+ rs_log_error("Failed to allocate memory for asprintf.");
+ return EXIT_OUT_OF_MEMORY;
+ }
+
+ name_type = GSS_C_NT_HOSTBASED_SERVICE;
+ } else {
+ if (asprintf(&ext_princ_name, "host/%s", full_name) < 0) {
+ rs_log_error("Failed to allocate memory for asprintf.");
+ return EXIT_OUT_OF_MEMORY;
+ }
+
+ name_type = GSS_C_NT_USER_NAME;
+ }
+
+ free(full_name);
+ name_buffer.value = ext_princ_name;
+ name_buffer.length = strlen(ext_princ_name);
+
+ if ((major_status = gss_import_name(&minor_status,
+ &name_buffer,
+ name_type,
+ &int_serv_name)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to import service name to internal GSS-API format.");
+ return EXIT_GSSAPI_FAILED;
+ }
+
+ input_tok.value = NULL;
+ input_tok.length = 0;
+ output_tok.value = NULL;
+ output_tok.length = 0;
+
+ if ((ret = dcc_gssapi_send_handshake(to_net_sd, from_net_sd)) != 0) {
+ return ret;
+ }
+
+ do
+ {
+ major_status = gss_init_sec_context(&minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &distcc_ctx_handle,
+ int_serv_name,
+ GSS_C_NO_OID,
+ req_flags,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &input_tok,
+ NULL,
+ &output_tok,
+ ret_flags,
+ NULL);
+
+ if (GSS_ERROR(major_status)) {
+ rs_log_crit("Failed to initiate a secure context.");
+ dcc_gssapi_status_to_log(major_status, GSS_C_GSS_CODE);
+ dcc_gssapi_status_to_log(minor_status, GSS_C_MECH_CODE);
+ return EXIT_GSSAPI_FAILED;
+ }
+
+ if (output_tok.length > 0) {
+ if ((ret = send_token(to_net_sd, &output_tok)) != 0) {
+ dcc_gssapi_cleanup(&input_tok, &output_tok, &int_serv_name);
+ return ret;
+ } else {
+ if ((return_status = gss_release_buffer(&minor_status,
+ &output_tok)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release buffer.");
+ }
+ }
+ }
+
+ if (input_tok.length > 0) {
+ if ((return_status = gss_release_buffer(&minor_status,
+ &input_tok)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release buffer.");
+ }
+ }
+
+ if (major_status == GSS_S_CONTINUE_NEEDED) {
+ if ((ret = recv_token(from_net_sd, &input_tok)) != 0) {
+ dcc_gssapi_cleanup(&input_tok, &output_tok, &int_serv_name);
+ return ret;
+ }
+ }
+
+ } while (major_status != GSS_S_COMPLETE);
+
+ rs_log_info("Successfully authenticated %s.", ext_princ_name);
+ dcc_gssapi_cleanup(&input_tok, &output_tok, &int_serv_name);
+
+ if ((major_status = gss_release_buffer(&minor_status,
+ &name_buffer)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release buffer.");
+ }
+
+ return 0;
+}
+
+/*
+ * Attempt handshake exchange with the server to indicate client's
+ * desire to authentciate.
+ *
+ * @param to_net_sd. Socket to write to.
+ *
+ * @param from_net_sd. Socket to read from.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+static int dcc_gssapi_send_handshake(int to_net_sd, int from_net_sd) {
+ char auth = HANDSHAKE;
+ fd_set sockets;
+ int ret;
+ struct timeval timeout;
+
+ rs_log_info("Sending handshake.");
+
+ if ((ret = dcc_writex(to_net_sd, &auth, sizeof(auth))) != 0) {
+ return ret;
+ }
+
+ rs_log_info("Sent %c.", auth);
+
+ FD_ZERO(&sockets);
+ FD_SET(from_net_sd, &sockets);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ ret = select(from_net_sd + 1, &sockets, NULL, NULL, &timeout);
+
+ if (ret < 0) {
+ rs_log_error("select failed: %s.", strerror(errno));
+ return EXIT_IO_ERROR;
+ }
+
+ if (ret == 0) {
+ rs_log_error("Timeout - does this server require authentication?");
+ return EXIT_TIMEOUT;
+ }
+
+ rs_log_info("Receiving handshake.");
+
+ if ((ret = dcc_readx(from_net_sd, &auth, sizeof(auth))) != 0) {
+ return ret;
+ }
+
+ rs_log_info("Received %c.", auth);
+
+ if (auth != HANDSHAKE) {
+ rs_log_crit("No server handshake.");
+ return EXIT_GSSAPI_FAILED;
+ }
+
+ return 0;
+}
+
+/*
+ * Receive notification from an authenticating server as to
+ * whether the client has successfully gained access or not.
+ *
+ * @param sd. Socket to read from.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+static int dcc_gssapi_recv_notification(int sd) {
+ char notification;
+ fd_set sockets;
+ int ret;
+ struct timeval timeout;
+
+ FD_ZERO(&sockets);
+ FD_SET(sd, &sockets);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ ret = select(sd + 1, &sockets, NULL, NULL, &timeout);
+
+ if (ret < 0) {
+ rs_log_error("select failed: %s.", strerror(errno));
+ return EXIT_IO_ERROR;
+ }
+
+ if (ret == 0) {
+ rs_log_error("Timeout - error receiving notification.");
+ return EXIT_TIMEOUT;
+ }
+
+ if ((ret = dcc_readx(sd, &notification, sizeof(notification))) != 0) {
+ rs_log_crit("Failed to receive notification.");
+ return ret;
+ }
+
+ if (notification != ACCESS) {
+ rs_log_crit("Access denied by server.");
+ return EXIT_ACCESS_DENIED;
+ }
+
+ rs_log_info("Access granted by server.");
+ return 0;
+}
diff --git a/src/auth_distccd.c b/src/auth_distccd.c
new file mode 100644
index 0000000..5c1639f
--- /dev/null
+++ b/src/auth_distccd.c
@@ -0,0 +1,364 @@
+/* Copyright (C) 2008 CERN
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Author: Ian Baker */
+
+#include <config.h>
+
+#ifdef HAVE_GSSAPI
+#include <arpa/inet.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "auth.h"
+#include "distcc.h"
+#include "dopt.h"
+#include "exitcode.h"
+#include "netutil.h"
+#include "trace.h"
+
+static int dcc_gssapi_accept_secure_context(int to_net_sd,
+ int from_net_sd,
+ OM_uint32 *ret_flags,
+ char **principal);
+static int dcc_gssapi_recv_handshake(int from_net_sd, int to_net_sd);
+static int dcc_gssapi_notify_client(int sd, char status);
+
+/*Global credentials so they're only required and released once*/
+/*in the most suitable place.*/
+gss_cred_id_t creds;
+/*Global security context in case other services*/
+/*are implemented in the future.*/
+gss_ctx_id_t distccd_ctx_handle = GSS_C_NO_CONTEXT;
+
+/*
+ * Perform any requested security.
+ *
+ * @param to_net_sd. Socket to write to.
+ *
+ * @param from_net_sd. Socket to read from.
+ *
+ * @param ret_flags. A representation of the services requested
+ * by the client.
+ *
+ * @param principal. The name of the client principal.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+int dcc_gssapi_check_client(int to_net_sd, int from_net_sd) {
+ char *principal = NULL;
+ int ret;
+ OM_uint32 ret_flags;
+
+ if ((ret = dcc_gssapi_accept_secure_context(to_net_sd,
+ from_net_sd,
+ &ret_flags,
+ &principal)) != 0) {
+ return ret;
+ }
+
+ if ((ret = dcc_gssapi_compare_flags(GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG, ret_flags)) != 0) {
+ dcc_gssapi_delete_ctx(&distccd_ctx_handle);
+ return ret;
+ }
+
+ rs_log_info("Notifying client.");
+
+ if ((ret = dcc_gssapi_notify_client(to_net_sd, ACCESS)) != 0) {
+ dcc_gssapi_delete_ctx(&distccd_ctx_handle);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Accept a secure context using the GSS-API. A handshake is attempted
+ * in order to detect a non-authenticating client.
+ *
+ * @param to_net_sd. Socket to write to.
+ *
+ * @param from_net_sd. Socket to read from.
+ *
+ * @param ret_flags. A representation of the security services
+ * requested by the client to be returned to
+ * the invoking function.
+ *
+ * @param principal. The name of the client principal to be returned
+ * to the invoking function.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+static int dcc_gssapi_accept_secure_context(int to_net_sd,
+ int from_net_sd,
+ OM_uint32 *ret_flags,
+ char **principal) {
+ gss_buffer_desc input_tok = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output_tok = GSS_C_EMPTY_BUFFER;
+ gss_name_t int_client_name;
+ gss_OID name_type;
+ int ret;
+ OM_uint32 major_status, minor_status, return_status;
+
+ input_tok.value = NULL;
+ input_tok.length = 0;
+ output_tok.value = NULL;
+ output_tok.length = 0;
+
+ if ((ret = dcc_gssapi_recv_handshake(from_net_sd, to_net_sd)) != 0) {
+ return ret;
+ }
+
+ do {
+ if ((ret = recv_token(from_net_sd, &input_tok)) != 0) {
+ rs_log_error("Error receiving token.");
+ rs_log_info("(eof may indicate a client error during ctx init).");
+
+ return ret;
+ }
+
+ major_status = gss_accept_sec_context(&minor_status,
+ &distccd_ctx_handle,
+ creds,
+ &input_tok,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &int_client_name,
+ NULL,
+ &output_tok,
+ ret_flags,
+ NULL,
+ NULL);
+
+ if (GSS_ERROR(major_status)) {
+ rs_log_crit("Failed to accept secure context.");
+ dcc_gssapi_status_to_log(major_status, GSS_C_GSS_CODE);
+ dcc_gssapi_status_to_log(minor_status, GSS_C_MECH_CODE);
+
+ if ((return_status = gss_release_buffer(&minor_status,
+ &input_tok)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release buffer.");
+ }
+
+ return EXIT_GSSAPI_FAILED;
+ }
+
+ if (output_tok.length > 0) {
+ if ((ret = send_token(to_net_sd,
+ &output_tok)) != 0) {
+ dcc_gssapi_cleanup(&input_tok,
+ &output_tok,
+ &int_client_name);
+ return ret;
+ }
+
+ if ((return_status = gss_release_buffer(&minor_status,
+ &output_tok)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release buffer.");
+ }
+ }
+
+ if (input_tok.length > 0) {
+ if ((return_status = gss_release_buffer(&minor_status,
+ &input_tok)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release buffer.");
+ }
+ }
+
+
+ } while (major_status != GSS_S_COMPLETE);
+
+ if ((major_status = gss_display_name(&minor_status,
+ int_client_name,
+ &name_buffer,
+ &name_type)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to convert name.");
+ }
+
+ rs_log_info("Successfully authenticated %s.", (char *) name_buffer.value);
+
+ if ((*principal = malloc(strlen((char *) name_buffer.value) + 1 )) == NULL) {
+ rs_log_error("malloc failed : %ld bytes: out of memory.",
+ (long) (strlen((char *) name_buffer.value) + 1));
+ return EXIT_OUT_OF_MEMORY;
+ }
+
+ strcpy(*principal, (char *) name_buffer.value);
+ dcc_gssapi_cleanup(&input_tok, &output_tok, &int_client_name);
+
+ return 0;
+}
+
+/*
+ * Attempt handshake exchange with the client to indicate server's
+ * desire to authentciate.
+ *
+ * @param from_net_sd. Socket to read from.
+ *
+ * @param to_net_sd. Socket to write to.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+static int dcc_gssapi_recv_handshake(int from_net_sd, int to_net_sd) {
+ char auth;
+ int ret;
+
+ rs_log_info("Receiving handshake.");
+
+ if ((ret = dcc_readx(from_net_sd, &auth, sizeof(auth))) != 0) {
+ return ret;
+ }
+
+ rs_log_info("Received %c.", auth);
+
+ if (auth != HANDSHAKE) {
+ rs_log_crit("No client handshake - did the client require authentication?");
+ return EXIT_GSSAPI_FAILED;
+ }
+
+ rs_log_info("Sending handshake.");
+
+ if ((ret = dcc_writex(to_net_sd, &auth, sizeof(auth))) != 0) {
+ return ret;
+ }
+
+ rs_log_info("Sent %c.", auth);
+
+ return 0;
+}
+
+/*
+ * Send notification of access/no access to client.
+ *
+ * @param sd. Socket to write notification to.
+ *
+ * @param status. Status of access request.
+ * Either 'y' or 'n'
+ *
+ * Returns 0 on success, otherwise error.
+ */
+static int dcc_gssapi_notify_client(int sd, char status) {
+ int ret;
+
+ if ((ret = dcc_writex(sd, &status, sizeof(status))) != 0) {
+ rs_log_crit("Failed to notify client.");
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Acquire credentials for the distccd daemon. We attempt to extract
+ * the server principal name from the environment and ascertain the
+ * name type.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+int dcc_gssapi_acquire_credentials(void) {
+ char *princ_env_val = NULL;
+ gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER;
+ gss_name_t int_princ_name;
+ gss_OID name_type;
+ OM_uint32 major_status, minor_status;
+
+ princ_env_val = getenv("DISTCCD_PRINCIPAL");
+
+ if (princ_env_val == NULL) {
+ rs_log_error("No principal name specified.");
+ return EXIT_GSSAPI_FAILED;
+ }
+
+ if (strchr(princ_env_val, '@') != NULL) {
+ name_type = GSS_C_NT_HOSTBASED_SERVICE;
+ } else {
+ name_type = GSS_C_NT_USER_NAME;
+ }
+
+ name_buffer.value = princ_env_val;
+ name_buffer.length = strlen(princ_env_val);
+
+ rs_log_info("Acquiring credentials.");
+
+ name_buffer.length = strlen(name_buffer.value);
+
+ if ((major_status = gss_import_name(&minor_status,
+ &name_buffer,
+ name_type,
+ &int_princ_name)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to import princ name (%s) to internal GSS-API format.",
+ (char *) name_buffer.value);
+ return EXIT_GSSAPI_FAILED;
+ }
+
+ major_status = gss_acquire_cred(&minor_status,
+ int_princ_name,
+ 0,
+ GSS_C_NO_OID_SET,
+ GSS_C_ACCEPT,
+ &creds,
+ NULL,
+ NULL);
+
+ if (major_status != GSS_S_COMPLETE) {
+ rs_log_crit("Failed to acquire credentials.");
+ dcc_gssapi_status_to_log(major_status, GSS_C_GSS_CODE);
+ dcc_gssapi_status_to_log(minor_status, GSS_C_MECH_CODE);
+
+ if ((major_status = gss_release_name(&minor_status,
+ &int_princ_name)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release GSS-API buffer.");
+ }
+
+ return EXIT_GSSAPI_FAILED;
+ }
+
+ if ((major_status = gss_release_name(&minor_status,
+ &int_princ_name)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release GSS-API buffer.");
+ }
+
+ rs_log_info("Credentials successfully acquired for %s.",
+ (char *) name_buffer.value);
+
+ name_buffer.value = NULL;
+
+ if ((major_status = gss_release_buffer(&minor_status,
+ &name_buffer)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release GSS-API buffer.");
+ }
+
+ return 0;
+}
+
+/*
+ * Release acquired credentials.
+ */
+void dcc_gssapi_release_credentials(void) {
+ OM_uint32 major_status, minor_status;
+
+ if ((major_status = gss_release_cred(&minor_status,
+ &creds)) != GSS_S_COMPLETE) {
+ rs_log_error("Failed to release credentials.");
+ }
+
+ rs_log_info("Credentials released successfully.");
+}
diff --git a/src/clirpc.c b/src/clirpc.c
index bacbfad..30ce99f 100644
--- a/src/clirpc.c
+++ b/src/clirpc.c
@@ -114,8 +114,10 @@ int dcc_r_result_header(int ifd,
if ((ret = dcc_r_token_int(ifd, "DONE", &vers)))
rs_log_error("server provided no answer. "
"Is the server configured to allow access from your IP"
- " address? Does the server have the compiler installed?"
- " Is the server configured to access the compiler?");
+ " address? Is the server performing authentication and"
+ " your client isn't? Does the server have the compiler"
+ " installed? Is the server configured to access the"
+ " compiler?");
return ret;
if (vers != expect_ver) {
diff --git a/src/daemon.c b/src/daemon.c
index 5b06971..df17c82 100644
--- a/src/daemon.c
+++ b/src/daemon.c
@@ -76,6 +76,9 @@
#include "srvnet.h"
#include "daemon.h"
#include "types.h"
+#ifdef HAVE_GSSAPI
+#include "auth.h"
+#endif
/* for trace.c */
@@ -203,6 +206,15 @@ int main(int argc, char *argv[])
if ((ret = dcc_setup_daemon_path()))
goto out;
+#ifdef HAVE_GSSAPI
+ /* Obtain credentials if authentication is requested. */
+ if (dcc_auth_enabled) {
+ if ((ret = dcc_gssapi_acquire_credentials()) != 0) {
+ goto out;
+ }
+ }
+#endif
+
if (dcc_should_be_inetd())
ret = dcc_inetd_server();
else
@@ -299,6 +311,12 @@ static int dcc_inetd_server(void)
close_ret = dcc_close(STDIN_FILENO);
+#ifdef HAVE_GSSAPI
+ if (dcc_auth_enabled) {
+ dcc_gssapi_release_credentials();
+ }
+#endif
+
if (ret)
return ret;
else
diff --git a/src/distcc.c b/src/distcc.c
index 4c22fba..ee05617 100644
--- a/src/distcc.c
+++ b/src/distcc.c
@@ -89,6 +89,9 @@ static void dcc_show_usage(void)
" the host list, and exit.\n"
" --scan-includes Show the files that distcc would send to the\n"
" remote machine, and exit. (Pump mode only.)\n"
+#ifdef HAVE_GSSAPI
+" --show-principal Show current distccd GSS-API principal and exit.\n"
+#endif
"\n"
"Environment variables:\n"
" See the manual page for a complete list.\n"
@@ -96,6 +99,9 @@ static void dcc_show_usage(void)
" DISTCC_LOG Send messages to file, not stderr.\n"
" DISTCC_SSH Command to run to open SSH connections.\n"
" DISTCC_DIR Directory for host list and locks.\n"
+#ifdef HAVE_GSSAPI
+" DISTCC_PRINCIPAL The name of the server principal to connect to.\n"
+#endif
"\n"
"Server specification:\n"
"A list of servers is taken from the environment variable $DISTCC_HOSTS, or\n"
@@ -109,6 +115,7 @@ static void dcc_show_usage(void)
" USER@HOST SSH connection to specified username at host.\n"
" HOSTSPEC,lzo Enable compression.\n"
" HOSTSPEC,cpp,lzo Use pump mode (remote preprocessing).\n"
+" HOSTSPEC,auth Enable GSS-API based mutual authenticaton.\n"
" --randomize Randomize the server list before execution.\n"
"\n"
"distcc distributes compilation jobs across volunteer machines running\n"
@@ -185,6 +192,21 @@ static void dcc_concurrency_level(void) {
printf("%i\n", nslots);
}
+#ifdef HAVE_GSSAPI
+/*
+ * Print out the name of the principal.
+ */
+static void dcc_gssapi_show_principal(void) {
+ char *princ_env_val = NULL;
+
+ if((princ_env_val = getenv("DISTCC_PRINCIPAL"))) {
+ printf("Principal is\t: %s\n", princ_env_val);
+ } else {
+ printf("Principal\t: Not Set.\n");
+ }
+}
+#endif
+
/**
* distcc client entry point.
*
@@ -267,6 +289,14 @@ int main(int argc, char **argv)
argv++;
}
+#ifdef HAVE_GSSAPI
+ if (!strcmp(argv[1], "--show-principal")) {
+ dcc_gssapi_show_principal();
+ ret = 0;
+ goto out;
+ }
+#endif
+
if ((ret = dcc_find_compiler(argv, &compiler_args)) != 0) {
goto out;
}
diff --git a/src/dopt.c b/src/dopt.c
index 267b63e..caaed07 100644
--- a/src/dopt.c
+++ b/src/dopt.c
@@ -55,6 +55,11 @@ int opt_niceness = 5; /* default */
**/
int arg_max_jobs = 0;
+#ifdef HAVE_GSSAPI
+/* If true perform GSS-API based authentication. */
+int opt_auth_enabled = 0;
+#endif
+
int arg_port = DISTCC_DEFAULT_PORT;
int arg_stats = DISTCC_DEFAULT_STATS_ENABLED;
int arg_stats_port = DISTCC_DEFAULT_STATS_PORT;
@@ -108,6 +113,9 @@ int opt_zeroconf = 0;
const struct poptOption options[] = {
{ "allow", 'a', POPT_ARG_STRING, 0, 'a', 0, 0 },
+#ifdef HAVE_GSSAPI
+ { "auth", 0, POPT_ARG_NONE, &opt_auth_enabled, 'A', 0, 0 },
+#endif
{ "jobs", 'j', POPT_ARG_INT, &arg_max_jobs, 'j', 0, 0 },
{ "daemon", 0, POPT_ARG_NONE, &opt_daemon_mode, 0, 0, 0 },
{ "help", 0, POPT_ARG_NONE, 0, '?', 0, 0 },
@@ -124,6 +132,9 @@ const struct poptOption options[] = {
{ "no-fork", 0, POPT_ARG_NONE, &opt_no_fork, 0, 0, 0 },
{ "pid-file", 'P', POPT_ARG_STRING, &arg_pid_file, 0, 0, 0 },
{ "port", 'p', POPT_ARG_INT, &arg_port, 0, 0, 0 },
+#ifdef HAVE_GSSAPI
+ { "show-principal", 0, POPT_ARG_NONE, 0, 'P', 0, 0 },
+#endif
{ "user", 0, POPT_ARG_STRING, &opt_user, 'u', 0, 0 },
{ "verbose", 0, POPT_ARG_NONE, 0, 'v', 0, 0 },
{ "version", 0, POPT_ARG_NONE, 0, 'V', 0, 0 },
@@ -147,6 +158,9 @@ static void distccd_show_usage(void)
"Options:\n"
" --help explain usage and exit\n"
" --version show version and exit\n"
+#ifdef HAVE_GSSAPI
+" --show-principal show current GSS-API principal and exit\n"
+#endif
" -P, --pid-file FILE save daemon process id to file\n"
" -N, --nice LEVEL lower priority, 20=most nice\n"
" --user USER if run by root, change to this persona\n"
@@ -156,6 +170,9 @@ static void distccd_show_usage(void)
" -p, --port PORT TCP port to listen on\n"
" --listen ADDRESS IP address to listen on\n"
" -a, --allow IP[/BITS] client address access control\n"
+#ifdef HAVE_GSSAPI
+" --auth enable GSS-API based mutual authenticaton\n"
+#endif
" --stats enable statistics reporting via HTTP server\n"
" --stats-port PORT TCP port to listen on for statistics requests\n"
#ifdef HAVE_AVAHI
@@ -180,6 +197,20 @@ static void distccd_show_usage(void)
);
}
+#ifdef HAVE_GSSAPI
+/*
+ * Print out the name of the principal.
+ */
+static void dcc_gssapi_show_principal(void) {
+ char *princ_env_val = NULL;
+
+ if ((princ_env_val = getenv("DISTCCD_PRINCIPAL"))) {
+ printf("Principal is\t: %s\n", princ_env_val);
+ } else {
+ printf("Principal\t: Not Set\n");
+ }
+}
+#endif
int distccd_parse_options(int argc, const char **argv)
{
@@ -212,6 +243,18 @@ int distccd_parse_options(int argc, const char **argv)
}
break;
+#ifdef HAVE_GSSAPI
+ /* Set the flag to indicate that authentication is requested. */
+ case 'A': {
+ if (opt_auth_enabled < 0) {
+ opt_auth_enabled = 0;
+ }
+
+ dcc_auth_enabled = opt_auth_enabled;
+ break;
+ }
+#endif
+
case 'j':
if (arg_max_jobs < 1 || arg_max_jobs > 200) {
rs_log_error("--jobs argument must be between 1 and 200");
@@ -227,6 +270,14 @@ int distccd_parse_options(int argc, const char **argv)
dcc_job_lifetime = opt_job_lifetime;
break;
+#ifdef HAVE_GSSAPI
+ case 'P': {
+ dcc_gssapi_show_principal();
+ exitcode = 0;
+ goto out_exit;
+ }
+#endif
+
case 'u':
if (getuid() != 0 && geteuid() != 0) {
rs_log_warning("--user is ignored when distccd is not run by root");
diff --git a/src/dopt.h b/src/dopt.h
index 7d75e09..9d0eae0 100644
--- a/src/dopt.h
+++ b/src/dopt.h
@@ -47,3 +47,7 @@ extern int opt_niceness;
#ifdef HAVE_AVAHI
extern int opt_zeroconf;
#endif
+
+#ifdef HAVE_GSSAPI
+extern int dcc_auth_enabled;
+#endif
diff --git a/src/dparent.c b/src/dparent.c
index 7c8af71..9ced2a2 100644
--- a/src/dparent.c
+++ b/src/dparent.c
@@ -71,6 +71,9 @@
#include "daemon.h"
#include "netutil.h"
#include "zeroconf.h"
+#ifdef HAVE_GSSAPI
+#include "auth.h"
+#endif
static void dcc_nofork_parent(int listen_fd) NORETURN;
static void dcc_detach(void);
@@ -258,6 +261,12 @@ static void dcc_nofork_parent(int listen_fd)
;
} else if (acc_fd == -1) {
rs_log_error("accept failed: %s", strerror(errno));
+
+#ifdef HAVE_GSSAPI
+ if (dcc_auth_enabled) {
+ dcc_gssapi_release_credentials();
+ }
+#endif
dcc_exit(EXIT_CONNECT_FAILED);
} else {
dcc_service_job(acc_fd, acc_fd, (struct sockaddr *) &cli_addr, cli_len);
diff --git a/src/dsignal.c b/src/dsignal.c
index ee8cd8f..35cc6a2 100644
--- a/src/dsignal.c
+++ b/src/dsignal.c
@@ -58,6 +58,9 @@
#include "dopt.h"
#include "exec.h"
#include "daemon.h"
+#ifdef HAVE_GSSAPI
+#include "auth.h"
+#endif
/* This stores the pid of the parent daemon. It's used to make sure
@@ -135,4 +138,10 @@ static RETSIGTYPE dcc_daemon_terminate(int whichsig)
}
raise(whichsig);
+
+#ifdef HAVE_GSSAPI
+ if (dcc_auth_enabled) {
+ dcc_gssapi_release_credentials();
+ }
+#endif
}
diff --git a/src/exitcode.h b/src/exitcode.h
index 3a4c1d0..dde0810 100644
--- a/src/exitcode.h
+++ b/src/exitcode.h
@@ -59,7 +59,12 @@ enum dcc_exitcode {
EXIT_NO_SUCH_FILE = 115,
EXIT_NO_HOSTS = 116,
EXIT_GONE = 117, /**< No longer relevant */
+#ifdef HAVE_GSSAPI
+ EXIT_TIMEOUT = 118,
+ EXIT_GSSAPI_FAILED = 119 /**< GSS-API - Catchall error code for GSS-API related errors. */
+#else
EXIT_TIMEOUT = 118
+#endif
};
diff --git a/src/help.c b/src/help.c
index a9d9489..8e84273 100644
--- a/src/help.c
+++ b/src/help.c
@@ -68,6 +68,9 @@ int dcc_show_version(const char *prog)
#ifdef HAVE_AVAHI
"\nBuilt with Zeroconf support.\n"
#endif
+#ifdef HAVE_GSSAPI
+"\nBuilt with GSS-API support for mutual authentication.\n"
+#endif
"\n"
"Please report bugs to %s\n"
"\n"
diff --git a/src/hosts.c b/src/hosts.c
index 71bce88..2ca00d0 100644
--- a/src/hosts.c
+++ b/src/hosts.c
@@ -241,6 +241,9 @@ static int dcc_parse_options(const char **psrc,
host->compr = DCC_COMPRESS_NONE;
host->cpp_where = DCC_CPP_ON_CLIENT;
+#ifdef HAVE_GSSAPI
+ host->authenticate = 0;
+#endif
while (p[0] == ',') {
p++;
@@ -256,6 +259,12 @@ static int dcc_parse_options(const char **psrc,
rs_trace("got CPP option");
host->cpp_where = DCC_CPP_ON_SERVER;
p += 3;
+#ifdef HAVE_GSSAPI
+ } else if (str_startswith("auth", p)) {
+ rs_trace("got GSSAPI option");
+ host->authenticate = 1;
+ p += 4;
+#endif
} else {
rs_log_error("unrecognized option in host specification: %s",
started);
diff --git a/src/hosts.h b/src/hosts.h
index 9c5d6ef..c88cc23 100644
--- a/src/hosts.h
+++ b/src/hosts.h
@@ -59,6 +59,11 @@ struct dcc_hostdef {
/** Where are we doing preprocessing? */
enum dcc_cpp_where cpp_where;
+#ifdef HAVE_GSSAPI
+ /* Are we autenticating with this host? */
+ int authenticate;
+#endif
+
struct dcc_hostdef *next;
};
diff --git a/src/lock.c b/src/lock.c
index d22f3f8..74d086d 100644
--- a/src/lock.c
+++ b/src/lock.c
@@ -101,6 +101,9 @@ struct dcc_hostdef _dcc_local = {
DCC_VER_1, /* protocol (ignored) */
DCC_COMPRESS_NONE, /* compression (ignored) */
DCC_CPP_ON_CLIENT, /* where to cpp (ignored) */
+#ifdef HAVE_GSSAPI
+ 0, /* Authentication? */
+#endif
NULL
};
@@ -118,6 +121,9 @@ struct dcc_hostdef _dcc_local_cpp = {
DCC_VER_1, /* protocol (ignored) */
DCC_COMPRESS_NONE, /* compression (ignored) */
DCC_CPP_ON_CLIENT, /* where to cpp (ignored) */
+#ifdef HAVE_GSSAPI
+ 0, /* Authentication? */
+#endif
NULL
};
diff --git a/src/remote.c b/src/remote.c
index f06a365..c15211e 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -49,7 +49,13 @@
#include "lock.h"
#include "compile.h"
#include "bulk.h"
+#ifdef HAVE_GSSAPI
+#include "auth.h"
+/* Global security context in case confidentiality/integrity */
+/* type services are needed in the future. */
+extern gss_ctx_id_t distcc_ctx_handle;
+#endif
/*
* TODO: If cpp finishes early and fails then perhaps break out of
@@ -220,6 +226,24 @@ int dcc_compile_remote(char **argv,
if ((ret = dcc_remote_connect(host, &to_net_fd, &from_net_fd, &ssh_pid)))
goto out;
+#ifdef HAVE_GSSAPI
+ /* Perform requested security. */
+ if(host->authenticate) {
+ rs_log_info("Performing authentication.");
+
+ if ((ret = dcc_gssapi_perform_requested_security(to_net_fd, from_net_fd)) != 0) {
+ rs_log_crit("Failed to perform authentication.");
+ goto out;
+ }
+
+ /* Context deleted here as we no longer need it. However, we have it available */
+ /* in case we want to use confidentiality/integrity type services in the future. */
+ dcc_gssapi_delete_ctx(&distcc_ctx_handle);
+ } else {
+ rs_log_info("No authentication requested.");
+ }
+#endif
+
dcc_note_state(DCC_PHASE_SEND, NULL, NULL);
if (host->cpp_where == DCC_CPP_ON_SERVER) {
diff --git a/src/serve.c b/src/serve.c
index bcb7a34..c31f59f 100644
--- a/src/serve.c
+++ b/src/serve.c
@@ -90,6 +90,17 @@
#include "stringmap.h"
#include "dotd.h"
#include "fix_debug_info.h"
+#ifdef HAVE_GSSAPI
+#include "auth.h"
+
+/* Global security context in case confidentiality/integrity */
+/* type services are needed in the future. */
+extern gss_ctx_id_t distccd_ctx_handle;
+
+/* Simple boolean, with a non-zero value indicating that the */
+/* --auth option was specified. */
+int dcc_auth_enabled = 0;
+#endif
/**
* We copy all serious distccd messages to this file, as well as sending the
@@ -164,6 +175,25 @@ int dcc_service_job(int in_fd,
if ((ret = dcc_check_client(cli_addr, cli_len, opt_allowed)) != 0)
goto out;
+#ifdef HAVE_GSSAPI
+ /* If requested perform authentication. */
+ if (dcc_auth_enabled) {
+ rs_log_info("Performing authentication.");
+
+ if ((ret = dcc_gssapi_check_client(in_fd, out_fd)) != 0) {
+ goto out;
+ }
+ } else {
+ rs_log_info("No authentication requested.");
+ }
+
+ /* Context deleted here as we no longer need it. However, we have it available */
+ /* in case we want to use confidentiality/integrity type services in the future. */
+ if (dcc_auth_enabled) {
+ dcc_gssapi_delete_ctx(&distccd_ctx_handle);
+ }
+#endif
+
ret = dcc_run_job(in_fd, out_fd);
dcc_job_summary();