summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfergus.henderson <fergushenderson@users.noreply.github.com>2009-07-24 16:59:25 +0000
committerfergus.henderson <fergushenderson@users.noreply.github.com>2009-07-24 16:59:25 +0000
commit6036a4211ca10e55545eac0df37f78c5a7e63a39 (patch)
treecd70163e266b7dcc0aaab0e74c3fe9f7a84ecc1f
parent8f268d30d81e54814d8036fbee59d25020682f60 (diff)
downloaddistcc-git-6036a4211ca10e55545eac0df37f78c5a7e63a39.tar.gz
Apply patch from Ian.Baker@cern.ch:
Optional Black/Whitelist Functionality. This patch is used to implement optional server-side access control through a specified black or whitelist file. This option is specified through a command line option. Revised by me (Fergus Henderson): fixed spelling error, added a comment.
-rw-r--r--man/distccd.114
-rw-r--r--src/auth.h2
-rw-r--r--src/auth_common.c2
-rw-r--r--src/auth_distcc.c5
-rw-r--r--src/auth_distccd.c242
-rw-r--r--src/daemon.c11
-rw-r--r--src/dopt.c41
-rw-r--r--src/dopt.h7
-rw-r--r--src/dparent.c4
-rw-r--r--src/dsignal.c4
10 files changed, 322 insertions, 10 deletions
diff --git a/man/distccd.1 b/man/distccd.1
index ec1b007..e6b0342 100644
--- a/man/distccd.1
+++ b/man/distccd.1
@@ -228,6 +228,20 @@ 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.
+.TP
+.B --blacklist=FILE
+Instruct distccd to reject connections from users whose principal names
+are listed in FILE.
+.B This option is only available if distccd was compiled with
+.B the --with-auth configure option and if distccd is run with the
+.B --auth option.
+.TP
+.B --whitelist=FILE
+Instruct distccd to accept connections from users whose principal names
+are listed in FILE.
+.B This option is only available if distccd was compiled with
+.B the --with-auth configure option and if distccd is run with the
+.B --auth option.
.SH "SEARCH PATHS"
.PP
distcc can pass either a relative or an absolute name for the compiler
diff --git a/src/auth.h b/src/auth.h
index a0d6b05..f661201 100644
--- a/src/auth.h
+++ b/src/auth.h
@@ -29,6 +29,8 @@
int dcc_gssapi_acquire_credentials(void);
void dcc_gssapi_release_credentials(void);
+int dcc_gssapi_obtain_list(int mode);
+void dcc_gssapi_free_list(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);
diff --git a/src/auth_common.c b/src/auth_common.c
index 9d9d6ef..471691b 100644
--- a/src/auth_common.c
+++ b/src/auth_common.c
@@ -44,7 +44,7 @@
*
* @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.
+ * 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;
diff --git a/src/auth_distcc.c b/src/auth_distcc.c
index ebd0d0e..4e3947c 100644
--- a/src/auth_distcc.c
+++ b/src/auth_distcc.c
@@ -353,8 +353,9 @@ static int dcc_gssapi_recv_notification(int sd) {
}
if (notification != ACCESS) {
- rs_log_crit("Access denied by server.");
- return EXIT_ACCESS_DENIED;
+ rs_log_crit("Access denied by server.");
+ rs_log_info("Your principal may be blacklisted or may not be whitelisted.");
+ return EXIT_ACCESS_DENIED;
}
rs_log_info("Access granted by server.");
diff --git a/src/auth_distccd.c b/src/auth_distccd.c
index 5c1639f..a26d168 100644
--- a/src/auth_distccd.c
+++ b/src/auth_distccd.c
@@ -24,6 +24,7 @@
#include <arpa/inet.h>
#endif
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -35,12 +36,21 @@
#include "netutil.h"
#include "trace.h"
+/*Maximum length of principal name in black/white list.*/
+#define MAX_NAME_LENGTH 50
+/*Key not found during binary search*/
+#define KEY_NOT_FOUND -1
+
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_check_list(char *principal, int sd);
+static int dcc_gssapi_bin_search(char *key);
static int dcc_gssapi_notify_client(int sd, char status);
+static int dcc_gssapi_compare_strings(const void *string_one,
+ const void *string_two);
/*Global credentials so they're only required and released once*/
/*in the most suitable place.*/
@@ -48,6 +58,11 @@ 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;
+/*Global sorted list of principal names from either a specified*/
+/*blacklist or a whitelist available to all children*/
+char **list = NULL;
+/*Global count of the number of principal names in the sorted list.*/
+int list_count = 0;
/*
* Perform any requested security.
@@ -80,11 +95,25 @@ int dcc_gssapi_check_client(int to_net_sd, int from_net_sd) {
return ret;
}
- rs_log_info("Notifying client.");
+ if (opt_blacklist_enabled || opt_whitelist_enabled) {
+ rs_log_info("Checking %s against %slist %s.",
+ principal,
+ (opt_blacklist_enabled) ? "black" : "white",
+ arg_list_file);
+ if ((ret = dcc_gssapi_check_list(principal, to_net_sd)) != 0) {
+ dcc_gssapi_delete_ctx(&distccd_ctx_handle);
+ free(principal);
+ return ret;
+ }
- if ((ret = dcc_gssapi_notify_client(to_net_sd, ACCESS)) != 0) {
- dcc_gssapi_delete_ctx(&distccd_ctx_handle);
- return ret;
+ free(principal);
+ } else {
+ 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;
@@ -246,6 +275,92 @@ static int dcc_gssapi_recv_handshake(int from_net_sd, int to_net_sd) {
}
/*
+ * Check the name of the connecting client principal against the sorted
+ * list of principal names using a binary search to determine access
+ * rights depending upon the type of list used. The client is then
+ * notified of the outcome.
+ *
+ * @param principal. The name of the connecting client principal.
+ *
+ * @param sd. Socket to write notification to.
+ *
+ * Returns 0 on success, otherwise access deinied.
+ */
+static int dcc_gssapi_check_list(char *principal, int sd) {
+ char *pos = NULL;
+ int location, ret;
+
+ if ((pos = strchr(principal, '@')) != NULL) {
+ *pos = '\0';
+ }
+
+ location = dcc_gssapi_bin_search(principal);
+
+ if (opt_blacklist_enabled) { /*blacklist*/
+ if (location >= 0) {
+ rs_log_info("Access denied - %s blacklisted.", principal);
+ rs_log_info("Notifying client.");
+ dcc_gssapi_notify_client(sd, NO_ACCESS);
+ return EXIT_GSSAPI_FAILED;
+ } else {
+ rs_log_info("Access granted - %s not blacklisted.", principal);
+ rs_log_info("Notifying client.");
+
+ if ((ret = dcc_gssapi_notify_client(sd, ACCESS)) != 0) {
+ return ret;
+ }
+
+ return 0;
+ }
+ } else { /*whitelist*/
+ if (location >= 0) {
+ rs_log_info("Access granted - %s whitelisted.", principal);
+ rs_log_info("Notifying client.");
+
+ if ((ret = dcc_gssapi_notify_client(sd, ACCESS)) != 0) {
+ return ret;
+ }
+
+ return 0;
+ } else {
+ rs_log_info("Access denied - %s not whitelisted.", principal);
+ rs_log_info("Notifying client.");
+ dcc_gssapi_notify_client(sd, NO_ACCESS);
+ return EXIT_GSSAPI_FAILED;
+ }
+ }
+}
+
+/*
+ * Perform a binary search on a sorted list for a key.
+ *
+ * @param key. The search key.
+ *
+ * Returns index if key in list, otherwise
+ * KEY_NOT_FOUND condition.
+ */
+static int dcc_gssapi_bin_search(char *key) {
+ int bottom = 0;
+ int middle, res;
+ int top = list_count - 1;
+
+ while (bottom <= top) {
+ middle = (bottom + top) / 2;
+ res = strcmp(key, list[middle]);
+
+ if (res < 0) {
+ top = middle - 1;
+ } else if (res > 0) {
+ bottom = middle + 1;
+ } else {
+ return middle;
+ }
+ }
+
+ return KEY_NOT_FOUND;
+}
+
+/*
* Send notification of access/no access to client.
*
* @param sd. Socket to write notification to.
@@ -362,3 +477,122 @@ void dcc_gssapi_release_credentials(void) {
rs_log_info("Credentials released successfully.");
}
+
+/*
+ * Read the set of principal names from the specified file
+ * to the list global variable and apply a qsort. If the
+ * list file can not be opened we exit with error as a
+ * requested security feature can not be implemented.
+ *
+ * @param mode. Indicates the type of the list, either
+ * black or white. Used for the log file.
+ *
+ * Returns 0 on success, otherwise error.
+ */
+int dcc_gssapi_obtain_list(int mode) {
+ char **head = NULL;
+ char *line = NULL;
+ char *pos = NULL;
+ FILE *file;
+ int ret;
+ size_t length = 0;
+ ssize_t read;
+
+ if (!(file = fopen(arg_list_file, "r"))) {
+ rs_log_error("Failed to open list file: %s: %s.", arg_list_file,
+ strerror(errno));
+ return EXIT_GSSAPI_FAILED;
+ }
+
+ rs_log_info("Using file %s as a %slist.", arg_list_file,
+ (mode) ? "black" : "white");
+
+ while ((read = getline(&line, &length, file)) != -1) {
+ list_count++;
+ }
+
+ if ((ret = fseek(file, 0, SEEK_SET)) != 0) {
+ rs_log_error("fseek failed: %s.", strerror(errno));
+
+ /* If seeking to the start of the file fails,
+ * try achieving the same effect by closing and reopening the file. */
+
+ if ((ret = fclose(file)) != 0) {
+ rs_log_error("fclose failed: %s.", strerror(errno));
+ }
+
+ if (!(file = fopen(arg_list_file, "r"))) {
+ rs_log_error("Failed to open list file: %s: %s.", arg_list_file,
+ strerror(errno));
+ return EXIT_GSSAPI_FAILED;
+ }
+ }
+
+ if ((list = malloc(list_count * sizeof(char *))) == NULL) {
+ rs_log_error("malloc failed : %ld bytes: out of memory.",
+ (long) (list_count * sizeof(char *)));
+ return EXIT_OUT_OF_MEMORY;
+ }
+
+ head = list;
+
+ while ((getline(&line, &length, file)) != -1) {
+ if ((pos = strchr(line, '\n')) != NULL) {
+ *pos = '\0';
+ }
+
+ if ((*list = malloc(strlen(line) + 1)) == NULL) {
+ rs_log_error("malloc failed : %ld bytes: out of memory.",
+ (long) (strlen(line) + 1));
+ return EXIT_OUT_OF_MEMORY;
+ }
+
+ strcpy(*list, line);
+ list++;
+ }
+
+ list = head;
+ qsort(list, list_count, sizeof(char *), dcc_gssapi_compare_strings);
+
+ if ((ret = fclose(file)) != 0) {
+ rs_log_error("fclose failed: %s.", strerror(errno));
+ }
+
+ free(line);
+
+ return 0;
+}
+
+/*
+ * Comparison function used by qsort, simply compares
+ * the two specified array elements containing two
+ * principal names.
+ *
+ * @param string_one. First element to be compared.
+ *
+ * @param string_two. Second element to be compared.
+ *
+ * Returns the result of the comparison.
+ */
+static int dcc_gssapi_compare_strings(const void *string_one,
+ const void *string_two) {
+ const char **s1 = (const char **) string_one;
+ const char **s2 = (const char **) string_two;
+
+ return strcmp(*s1, *s2);
+}
+
+/*
+ * Free the dynamically allocated memory used by the
+ * list global variable. First free the individual
+ * strings then the array itself.
+ */
+void dcc_gssapi_free_list(void) {
+ int i;
+
+ for (i = 0; i < list_count; i++) {
+ free(list[i]);
+ }
+
+ free(list);
+}
diff --git a/src/daemon.c b/src/daemon.c
index df17c82..395d953 100644
--- a/src/daemon.c
+++ b/src/daemon.c
@@ -212,6 +212,13 @@ int main(int argc, char *argv[])
if ((ret = dcc_gssapi_acquire_credentials()) != 0) {
goto out;
}
+
+ /* Read contents of list file into an array and apply qsort. */
+ if (opt_blacklist_enabled || opt_whitelist_enabled) {
+ if ((ret = dcc_gssapi_obtain_list((opt_blacklist_enabled) ? 1 : 0)) != 0) {
+ goto out;
+ }
+ }
}
#endif
@@ -314,6 +321,10 @@ static int dcc_inetd_server(void)
#ifdef HAVE_GSSAPI
if (dcc_auth_enabled) {
dcc_gssapi_release_credentials();
+
+ if (opt_blacklist_enabled || opt_whitelist_enabled) {
+ dcc_gssapi_free_list();
+ }
}
#endif
diff --git a/src/dopt.c b/src/dopt.c
index caaed07..9c36daa 100644
--- a/src/dopt.c
+++ b/src/dopt.c
@@ -58,6 +58,10 @@ int arg_max_jobs = 0;
#ifdef HAVE_GSSAPI
/* If true perform GSS-API based authentication. */
int opt_auth_enabled = 0;
+/* Control access through a specified list file. */
+int opt_blacklist_enabled = 0;
+int opt_whitelist_enabled = 0;
+const char *arg_list_file = NULL;
#endif
int arg_port = DISTCC_DEFAULT_PORT;
@@ -115,6 +119,7 @@ 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 },
+ { "blacklist", 0, POPT_ARG_STRING, &arg_list_file, 'b', 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 },
@@ -138,6 +143,9 @@ const struct poptOption options[] = {
{ "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 },
+#ifdef HAVE_GSSAPI
+ { "whitelist", 0, POPT_ARG_STRING, &arg_list_file, 'w', 0, 0 },
+#endif
{ "wizard", 'W', POPT_ARG_NONE, 0, 'W', 0, 0 },
{ "stats", 0, POPT_ARG_NONE, &arg_stats, 0, 0, 0 },
{ "stats-port", 0, POPT_ARG_INT, &arg_stats_port, 0, 0, 0 },
@@ -147,7 +155,6 @@ const struct poptOption options[] = {
{ 0, 0, 0, 0, 0, 0, 0 }
};
-
static void distccd_show_usage(void)
{
dcc_show_version("distccd");
@@ -172,6 +179,8 @@ static void distccd_show_usage(void)
" -a, --allow IP[/BITS] client address access control\n"
#ifdef HAVE_GSSAPI
" --auth enable GSS-API based mutual authenticaton\n"
+" --blacklist=FILE control client access through a blacklist\n"
+" --whitelist=FILE control client access through a whitelist\n"
#endif
" --stats enable statistics reporting via HTTP server\n"
" --stats-port PORT TCP port to listen on for statistics requests\n"
@@ -205,7 +214,7 @@ 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);
+ printf("Principal is\t: %s\n", princ_env_val);
} else {
printf("Principal\t: Not Set\n");
}
@@ -244,7 +253,7 @@ int distccd_parse_options(int argc, const char **argv)
break;
#ifdef HAVE_GSSAPI
- /* Set the flag to indicate that authentication is requested. */
+ /* Set the flag to indicate that authentication is requested. */
case 'A': {
if (opt_auth_enabled < 0) {
opt_auth_enabled = 0;
@@ -253,6 +262,18 @@ int distccd_parse_options(int argc, const char **argv)
dcc_auth_enabled = opt_auth_enabled;
break;
}
+
+ case 'b': {
+ if (opt_whitelist_enabled) {
+ rs_log_error("Can't specify both --whitelist and --blacklist.");
+ exitcode = EXIT_BAD_ARGUMENTS;
+ goto out_exit;
+ } else {
+ opt_blacklist_enabled = 1;
+ }
+
+ break;
+ }
#endif
case 'j':
@@ -312,6 +333,20 @@ int distccd_parse_options(int argc, const char **argv)
opt_log_level_num = RS_LOG_DEBUG;
break;
+#ifdef HAVE_GSSAPI
+ case 'w': {
+ if (opt_blacklist_enabled) {
+ rs_log_error("Can't specify both --blacklist and --whitelist.");
+ exitcode = EXIT_BAD_ARGUMENTS;
+ goto out_exit;
+ } else {
+ opt_whitelist_enabled = 1;
+ }
+
+ break;
+ }
+#endif
+
case 'W':
/* catchall for running under gdb */
opt_log_stderr = 1;
diff --git a/src/dopt.h b/src/dopt.h
index 9d0eae0..695bc1e 100644
--- a/src/dopt.h
+++ b/src/dopt.h
@@ -51,3 +51,10 @@ extern int opt_zeroconf;
#ifdef HAVE_GSSAPI
extern int dcc_auth_enabled;
#endif
+
+#ifdef HAVE_GSSAPI
+extern int dcc_auth_enabled;
+extern int opt_blacklist_enabled;
+extern int opt_whitelist_enabled;
+extern const char *arg_list_file;
+#endif
diff --git a/src/dparent.c b/src/dparent.c
index 9ced2a2..77a4ac5 100644
--- a/src/dparent.c
+++ b/src/dparent.c
@@ -265,6 +265,10 @@ static void dcc_nofork_parent(int listen_fd)
#ifdef HAVE_GSSAPI
if (dcc_auth_enabled) {
dcc_gssapi_release_credentials();
+
+ if (opt_blacklist_enabled || opt_whitelist_enabled) {
+ dcc_gssapi_free_list();
+ }
}
#endif
dcc_exit(EXIT_CONNECT_FAILED);
diff --git a/src/dsignal.c b/src/dsignal.c
index 35cc6a2..de033b7 100644
--- a/src/dsignal.c
+++ b/src/dsignal.c
@@ -142,6 +142,10 @@ static RETSIGTYPE dcc_daemon_terminate(int whichsig)
#ifdef HAVE_GSSAPI
if (dcc_auth_enabled) {
dcc_gssapi_release_credentials();
+
+ if (opt_blacklist_enabled || opt_whitelist_enabled) {
+ dcc_gssapi_free_list();
+ }
}
#endif
}