1 This patch adds a kerberos authentication method to daemon mode.
3 NOTE: minimally munged to work with 3.1.1, but as yet untested!
5 To use this patch, run these commands for a successful build:
7 patch -p1 <patches/kerberos.diff
12 based-on: a1cc50ba96d212c0647a91bf56e3de25eb35bd56
13 diff --git a/Makefile.in b/Makefile.in
16 @@ -44,7 +44,7 @@ OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
17 util.o util2.o main.o checksum.o match.o syscall.o log.o backup.o delete.o
18 OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
19 fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
20 -OBJS3=progress.o pipe.o @ASM@
21 +OBJS3=progress.o pipe.o gss-auth.o @ASM@
22 DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
23 popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
24 popt/popthelp.o popt/poptparse.o
25 diff --git a/clientserver.c b/clientserver.c
28 @@ -137,7 +137,7 @@ int start_socket_client(char *host, int remote_argc, char *remote_argv[],
32 - ret = start_inband_exchange(fd, fd, user, remote_argc, remote_argv);
33 + ret = start_inband_exchange(fd, fd, user, host, remote_argc, remote_argv);
35 return ret ? ret : client_run(fd, fd, -1, argc, argv);
37 @@ -214,7 +214,7 @@ static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int
41 -int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char *argv[])
42 +int start_inband_exchange(int f_in, int f_out, const char *user, const char *host, int argc, char *argv[])
45 char line[BIGPATHBUFLEN];
46 @@ -329,6 +329,17 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
50 + if (strcmp(line, "@RSYNCD: GSS") == 0) {
52 + if (auth_gss_client(f_out, host) < 0)
56 + rprintf(FERROR, "GSSAPI is not supported\n");
61 if (strcmp(line,"@RSYNCD: OK") == 0)
64 @@ -685,7 +696,12 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
67 read_only = lp_read_only(i); /* may also be overridden by auth_server() */
68 - auth_user = auth_server(f_in, f_out, i, host, addr, "@RSYNCD: AUTHREQD ");
70 + if (lp_use_gssapi(i))
71 + auth_user = auth_gss_server(f_in, f_out, i, host, addr, "@RSYNCD: GSS");
74 + auth_user = auth_server(f_in, f_out, i, host, addr, "@RSYNCD: AUTHREQD ");
77 io_printf(f_out, "@ERROR: auth failed on module %s\n", name);
78 diff --git a/configure.ac b/configure.ac
81 @@ -917,6 +917,31 @@ if test x"$enable_iconv" != x"no"; then
82 AC_DEFINE(UTF8_CHARSET, "UTF-8", [String to pass to iconv() for the UTF-8 charset.])
85 +AC_ARG_WITH([gssapi],
86 + [AS_HELP_STRING([--with-gssapi],
87 + [support GSSAPI authentication @<:@default=check@:>@])],
89 + [with_gssapi=check])
91 +AH_TEMPLATE([GSSAPI_OPTION],
92 +[Define if you want GSSAPI authentication. Specifing a value will set the search path.])
94 +AS_IF([test "x$with_gssapi" != xno],
95 + [AC_SEARCH_LIBS([gss_import_name], gss gssapi_krb5 ,
96 + [AC_CHECK_HEADERS(gssapi/gssapi_generic.h gssapi/gssapi.h) ]
97 + [ AC_DEFINE([GSSAPI_OPTION], [1]) ]
99 + [if test "x$with_gssapi" = xcheck; then
101 + [--with-gssapi was given, but test for function failed])
106 +if test x"$enable_gssapi" != x"no"; then
107 + AC_DEFINE(GSSAPI_OPTION, 1)
110 AC_CACHE_CHECK([whether chown() modifies symlinks],rsync_cv_chown_modifies_symlink,[
111 AC_RUN_IFELSE([AC_LANG_SOURCE([[
113 diff --git a/daemon-parm.txt b/daemon-parm.txt
114 --- a/daemon-parm.txt
115 +++ b/daemon-parm.txt
116 @@ -62,4 +62,5 @@ BOOL reverse_lookup True
117 BOOL strict_modes True
118 BOOL transfer_logging False
120 +BOOL use_gssapi False
121 BOOL write_only False
122 diff --git a/gss-auth.c b/gss-auth.c
128 + * GSSAPI authentication.
130 + * Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
131 + * Copyright (C) 2001-2002 Martin Pool <mbp@samba.org>
132 + * Copyright (C) 2002-2008 Wayne Davison
134 + * This program is free software; you can redistribute it and/or modify
135 + * it under the terms of the GNU General Public License as published by
136 + * the Free Software Foundation; either version 3 of the License, or
137 + * (at your option) any later version.
139 + * This program is distributed in the hope that it will be useful,
140 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
141 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
142 + * GNU General Public License for more details.
144 + * You should have received a copy of the GNU General Public License along
145 + * with this program; if not, visit the http://fsf.org website.
150 +#ifdef GSSAPI_OPTION
152 +#define RSYNC_GSS_SERVICE "host"
154 +struct init_context_data {
155 + gss_cred_id_t initiator_cred_handle;
156 + gss_ctx_id_t *context_handle;
157 + gss_name_t target_name;
159 + OM_uint32 req_flags;
160 + OM_uint32 time_req;
161 + gss_channel_bindings_t input_chan_bindings;
162 + gss_OID *actual_mech_type;
163 + OM_uint32 *ret_flags;
164 + OM_uint32 *time_rec;
167 +struct accept_context_data {
168 + gss_ctx_id_t *context_handle;
169 + gss_cred_id_t acceptor_cred_handle;
170 + gss_channel_bindings_t input_chan_bindings;
171 + gss_name_t *src_name;
172 + gss_OID *mech_type;
173 + OM_uint32 *ret_flags;
174 + OM_uint32 *time_rec;
175 + gss_cred_id_t *delegated_cred_handle;
178 +int auth_gss_client(int fd, const char *host)
180 + gss_ctx_id_t ctxt = GSS_C_NO_CONTEXT;
181 + gss_name_t target_name = GSS_C_NO_NAME;
182 + struct init_context_data cb_data;
185 + OM_uint32 min_stat;
187 + buffer = new_array(char, (strlen(host) + 2 + strlen(RSYNC_GSS_SERVICE)));
189 + sprintf(buffer, "%s@%s", RSYNC_GSS_SERVICE, host);
191 + import_gss_name(&target_name, buffer, GSS_C_NT_HOSTBASED_SERVICE);
194 + cb_data.initiator_cred_handle = GSS_C_NO_CREDENTIAL;
195 + cb_data.context_handle = &ctxt;
196 + cb_data.target_name = target_name;
197 + cb_data.mech_type = GSS_C_NO_OID;
198 + cb_data.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
199 + cb_data.time_req = 0;
200 + cb_data.input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
201 + cb_data.actual_mech_type = NULL;
202 + cb_data.ret_flags = NULL;
203 + cb_data.time_rec = NULL;
205 + status = do_gss_dialog(fd, fd, 0, &cb_init_sec_context, (void *)&cb_data);
206 + if (ctxt != GSS_C_NO_CONTEXT)
207 + gss_delete_sec_context(&min_stat, &ctxt, GSS_C_NO_BUFFER);
208 + free_gss_name(&target_name);
214 + * The call back function for a gss_init_sec_context dialog
216 +OM_uint32 cb_init_sec_context(OM_uint32 *min_statp, gss_buffer_t in_token, gss_buffer_t out_token, void *cb_data)
218 + struct init_context_data *context_data;
220 + context_data = (struct init_context_data *) cb_data;
221 + return gss_init_sec_context(min_statp, context_data->initiator_cred_handle, context_data->context_handle, context_data->target_name, context_data->mech_type, context_data->req_flags, context_data->time_req, context_data->input_chan_bindings, in_token, context_data->actual_mech_type, out_token, context_data->ret_flags, context_data->time_rec);
224 +/* Possibly negotiate authentication with the client. Use "leader" to
225 + * start off the auth if necessary.
227 + * Return NULL if authentication failed. Return "" if anonymous access.
228 + * Otherwise return username.
230 +char *auth_gss_server(int fd_in, int fd_out, int module, const char *host, const char *addr, const char *leader)
232 + struct accept_context_data cb_data;
233 + gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
234 + gss_ctx_id_t context = GSS_C_NO_CONTEXT;
235 + OM_uint32 ret_flags;
236 + char *users = lp_auth_users(module);
237 + OM_uint32 maj_stat, min_stat;
238 + gss_name_t server_name = GSS_C_NO_NAME;
239 + gss_name_t client_name = GSS_C_NO_NAME;
240 + gss_OID doid = GSS_C_NO_OID;
243 + /* if no auth list then allow anyone in! */
244 + if (!users || !*users)
247 + import_gss_name(&server_name, "host", GSS_C_NT_HOSTBASED_SERVICE);
249 + maj_stat = gss_acquire_cred(&min_stat, server_name, GSS_C_INDEFINITE, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL);
250 + if (maj_stat != GSS_S_COMPLETE) {
251 + error_gss(maj_stat, min_stat, "error acquiring credentials on module %s from %s (%s)", lp_name(module), host, addr);
255 + io_printf(fd_out, "%s\n", leader);
257 + cb_data.context_handle = &context;
258 + cb_data.acceptor_cred_handle = server_creds;
259 + cb_data.input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
260 + cb_data.src_name = &client_name;
261 + cb_data.mech_type = &doid;
262 + cb_data.ret_flags = &ret_flags;
263 + cb_data.time_rec = NULL;
264 + cb_data.delegated_cred_handle = NULL;
266 + if (do_gss_dialog(fd_in, fd_out, -1, &cb_accept_sec_context, (void *)&cb_data) < 0)
269 + user = get_cn(client_name, doid);
271 + free_gss_name(&server_name);
272 + free_gss_name(&client_name);
278 + * The call back function for a gss_accept_sec_context dialog
280 +OM_uint32 cb_accept_sec_context(OM_uint32 *min_statp, gss_buffer_t in_token, gss_buffer_t out_token, void *cb_data)
282 + struct accept_context_data *context_data;
284 + context_data = (struct accept_context_data *) cb_data;
285 + return gss_accept_sec_context(min_statp, context_data->context_handle, context_data->acceptor_cred_handle, in_token, context_data->input_chan_bindings, context_data->src_name, context_data->mech_type, out_token, context_data->ret_flags, context_data->time_rec, context_data->delegated_cred_handle);
288 +void free_gss_buffer(gss_buffer_t gss_buffer)
290 + OM_uint32 maj_stat, min_stat;
292 + if (gss_buffer->length > 0) {
293 + maj_stat = gss_release_buffer(&min_stat, gss_buffer);
294 + if (maj_stat != GSS_S_COMPLETE) {
295 + error_gss(maj_stat, min_stat, "can't release a buffer");
300 +void free_gss_name(gss_name_t *gss_buffer)
302 + OM_uint32 maj_stat, min_stat;
304 + if (*gss_buffer != GSS_C_NO_NAME) {
305 + maj_stat = gss_release_name(&min_stat, gss_buffer);
306 + if (maj_stat != GSS_S_COMPLETE) {
307 + error_gss(maj_stat, min_stat, "can't release a name");
312 +void import_gss_name(gss_name_t *gss_name, const char *name, gss_OID type)
314 + gss_buffer_desc gssname;
315 + OM_uint32 maj_stat, min_stat;
317 + gssname.value = strdup(name);
318 + gssname.length = strlen(name) +1 ;
320 + maj_stat = gss_import_name(&min_stat, &gssname, type, gss_name);
322 + if (maj_stat != GSS_S_COMPLETE)
323 + error_gss(maj_stat, min_stat, "can't resolve %s", name);
325 + free_gss_buffer(&gssname);
328 +char *export_name(const gss_name_t input_name)
330 + OM_uint32 maj_stat, min_stat;
331 + gss_buffer_desc exported_name;
337 + maj_stat = gss_display_name(&min_stat, input_name, &exported_name, &name_oid);
338 + if (maj_stat != GSS_S_COMPLETE) {
339 + error_gss(maj_stat, min_stat, "can't get display name");
343 + if (exported_name.length > 0)
344 + exported = strdup(exported_name.value);
346 + free_gss_buffer(&exported_name);
351 +void error_gss(OM_uint32 major, OM_uint32 minor, const char *format, ...)
353 + OM_uint32 min_stat;
354 + gss_buffer_desc gss_msg = GSS_C_EMPTY_BUFFER;
357 + char message[BIGPATHBUFLEN];
359 + va_start(ap, format);
360 + vsnprintf(message, sizeof message, format, ap);
364 + if (major != GSS_S_FAILURE) /* Don't print unspecified failure, the message is useless */
366 + gss_display_status(&min_stat, major, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &gss_msg);
367 + rprintf(FERROR, "GSS-API error: %s: %s\n", message, (char *) gss_msg.value);
368 + free_gss_buffer(&gss_msg);
369 + } while (msg_ctx != 0);
373 + gss_display_status(&min_stat, minor, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &gss_msg);
374 + rprintf(FERROR, "GSS-API error: %s: %s\n",message, (char *) gss_msg.value);
375 + free_gss_buffer(&gss_msg);
376 + } while (msg_ctx != 0);
381 + * This function manage a gss dialog
382 + * gss tokens are eaten by a call-back function and then send by this function.
383 + * Argument to this function can be passed throught the cb_data argument
384 + * When told to act as a server, it just begin to wait for a first token before beginning operation
387 +int do_gss_dialog(int fd_in, int fd_out, int isServer, OM_uint32 (*eat_token)(OM_uint32 *,gss_buffer_t, gss_buffer_t, void *), void *cb_data)
389 + OM_uint32 maj_stat, min_stat;
390 + gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER;
391 + gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER;
394 + recv_gss_token(fd_in, &in_token);
397 + maj_stat = (*eat_token)(&min_stat, &in_token, &out_token, cb_data);
398 + free_gss_buffer(&in_token);
399 + if (maj_stat != GSS_S_COMPLETE
400 + && maj_stat != GSS_S_CONTINUE_NEEDED) {
401 + error_gss(maj_stat, min_stat, "error during dialog");
405 + if (out_token.length != 0) {
406 + send_gss_token(fd_out, &out_token);
408 + free_gss_buffer(&out_token);
410 + if (maj_stat == GSS_S_CONTINUE_NEEDED) {
411 + recv_gss_token(fd_in, &in_token);
413 + } while (maj_stat == GSS_S_CONTINUE_NEEDED);
418 +char *get_cn(const gss_name_t input_name, const gss_OID mech_type)
420 + OM_uint32 maj_stat, min_stat;
421 + gss_name_t output_name;
422 + gss_buffer_desc exported_name;
426 + maj_stat = gss_canonicalize_name(&min_stat, input_name, mech_type, &output_name);
427 + if (maj_stat != GSS_S_COMPLETE) {
428 + error_gss(maj_stat, min_stat, "canonizing name");
432 + maj_stat = gss_export_name(&min_stat, output_name, &exported_name);
433 + if (maj_stat != GSS_S_COMPLETE) {
434 + error_gss(maj_stat, min_stat, "canonizing name");
437 + if (exported_name.length > 0)
438 + cn = strdup(exported_name.value);
440 + free_gss_name(&output_name);
441 + free_gss_buffer(&exported_name);
446 +void send_gss_token(int fd, gss_buffer_t token)
448 + write_int(fd, token->length);
449 + write_buf(fd, token->value, token->length);
452 +void recv_gss_token(int fd, gss_buffer_t token)
454 + token->length = read_int(fd);
455 + if (token->length > 0) {
456 + token->value = new_array(char, token->length);
457 + read_buf(fd, token->value, token->length);
460 +#endif /* GSSAPI_OPTION */
461 diff --git a/main.c b/main.c
464 @@ -1522,7 +1522,7 @@ static int start_client(int argc, char *argv[])
465 * remote shell command, we need to do the RSYNCD protocol first */
466 if (daemon_over_rsh) {
468 - tmpret = start_inband_exchange(f_in, f_out, shell_user, remote_argc, remote_argv);
469 + tmpret = start_inband_exchange(f_in, f_out, shell_user, shell_machine, remote_argc, remote_argv);
473 diff --git a/rsync.h b/rsync.h
476 @@ -499,6 +499,15 @@ enum delret {
480 +#ifdef GSSAPI_OPTION
481 +#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
482 +#include <gssapi/gssapi_generic.h>
484 +#ifdef HAVE_GSSAPI_GSSAPI_H
485 +#include <gssapi/gssapi.h>
491 #include "lib/pool_alloc.h"