Updating patches.
[rsync-patches.git] / kerberos.diff
1 This patch adds a kerberos authentication method to daemon mode.
2
3 NOTE: minimally munged to work with 3.1.1, but as yet untested!
4
5 To use this patch, run these commands for a successful build:
6
7     patch -p1 <patches/kerberos.diff
8     ./prepare-source
9     ./configure
10     make
11
12 based-on: 2ac35b45071c7bfd8be6be41bfd45326f1f57bce
13 diff --git a/Makefile.in b/Makefile.in
14 --- a/Makefile.in
15 +++ b/Makefile.in
16 @@ -40,7 +40,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
21 +OBJS3=progress.o pipe.o gss-auth.o
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
26 --- a/clientserver.c
27 +++ b/clientserver.c
28 @@ -131,7 +131,7 @@ int start_socket_client(char *host, int remote_argc, char *remote_argv[],
29         setup_iconv();
30  #endif
31  
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);
34  
35         return ret ? ret : client_run(fd, fd, -1, argc, argv);
36  }
37 @@ -210,7 +210,7 @@ static int exchange_protocols(int f_in, int f_out, char *buf, size_t bufsiz, int
38         return 0;
39  }
40  
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[])
43  {
44         int i, modlen;
45         char line[BIGPATHBUFLEN];
46 @@ -296,6 +296,17 @@ int start_inband_exchange(int f_in, int f_out, const char *user, int argc, char
47                         continue;
48                 }
49  
50 +               if (strcmp(line, "@RSYNCD: GSS") == 0) {
51 +#ifdef GSSAPI_OPTION
52 +                       if (auth_gss_client(f_out, host) < 0)
53 +                               return -1;
54 +                       continue;
55 +#else
56 +                       rprintf(FERROR, "GSSAPI is not supported\n");
57 +                       return -1;
58 +#endif
59 +               }
60 +
61                 if (strcmp(line,"@RSYNCD: OK") == 0)
62                         break;
63  
64 @@ -568,7 +579,12 @@ static int rsync_module(int f_in, int f_out, int i, const char *addr, const char
65         }
66  
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 ");
69 +#ifdef GSSAPI_OPTION
70 +       if (lp_use_gssapi(i))
71 +               auth_user = auth_gss_server(f_in, f_out, i, host, addr, "@RSYNCD: GSS");
72 +       else
73 +#endif
74 +               auth_user = auth_server(f_in, f_out, i, host, addr, "@RSYNCD: AUTHREQD ");
75  
76         if (!auth_user) {
77                 io_printf(f_out, "@ERROR: auth failed on module %s\n", name);
78 diff --git a/configure.ac b/configure.ac
79 --- a/configure.ac
80 +++ b/configure.ac
81 @@ -678,6 +678,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.])
83  fi
84  
85 +AC_ARG_WITH([gssapi],
86 +  [AS_HELP_STRING([--with-gssapi],
87 +    [support GSSAPI authentication @<:@default=check@:>@])],
88 +  [],
89 +  [with_gssapi=check])
90 +
91 +AH_TEMPLATE([GSSAPI_OPTION],
92 +[Define if you want GSSAPI authentication. Specifing a value will set the search path.])
93 +
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]) ]
98 +      ,
99 +      [if test "x$with_gssapi" = xcheck; then
100 +        AC_MSG_FAILURE(
101 +          [--with-gssapi was given, but test for function failed])
102 +       fi
103 +       ])
104 +     ])
105 +
106 +if test x"$enable_gssapi" != x"no"; then
107 +   AC_DEFINE(GSSAPI_OPTION, 1)
108 +fi
109 +
110  AC_CACHE_CHECK([whether chown() modifies symlinks],rsync_cv_chown_modifies_symlink,[
111    AC_TRY_RUN([
112  #if HAVE_UNISTD_H
113 diff --git a/gss-auth.c b/gss-auth.c
114 new file mode 100644
115 --- /dev/null
116 +++ b/gss-auth.c
117 @@ -0,0 +1,342 @@
118 +/*
119 + * GSSAPI authentication.
120 + *
121 + * Copyright (C) 1998-2001 Andrew Tridgell <tridge@samba.org>
122 + * Copyright (C) 2001-2002 Martin Pool <mbp@samba.org>
123 + * Copyright (C) 2002-2008 Wayne Davison
124 + *
125 + * This program is free software; you can redistribute it and/or modify
126 + * it under the terms of the GNU General Public License as published by
127 + * the Free Software Foundation; either version 3 of the License, or
128 + * (at your option) any later version.
129 + *
130 + * This program is distributed in the hope that it will be useful,
131 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
132 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
133 + * GNU General Public License for more details.
134 + *
135 + * You should have received a copy of the GNU General Public License along
136 + * with this program; if not, visit the http://fsf.org website.
137 + */
138 +
139 +#include "rsync.h"
140 +
141 +#ifdef GSSAPI_OPTION
142 +
143 +#define RSYNC_GSS_SERVICE "host"
144 +
145 +struct init_context_data {
146 +       gss_cred_id_t          initiator_cred_handle;
147 +       gss_ctx_id_t           *context_handle;
148 +       gss_name_t             target_name;
149 +       gss_OID                mech_type;
150 +       OM_uint32              req_flags;
151 +       OM_uint32              time_req;
152 +       gss_channel_bindings_t input_chan_bindings;
153 +       gss_OID                *actual_mech_type;
154 +       OM_uint32              *ret_flags;
155 +       OM_uint32              *time_rec;
156 +};
157 +
158 +struct accept_context_data {
159 +       gss_ctx_id_t           *context_handle;
160 +       gss_cred_id_t          acceptor_cred_handle;
161 +       gss_channel_bindings_t input_chan_bindings;
162 +       gss_name_t             *src_name;
163 +       gss_OID                *mech_type;
164 +       OM_uint32              *ret_flags;
165 +       OM_uint32              *time_rec;
166 +       gss_cred_id_t          *delegated_cred_handle;
167 +};
168 +
169 +int auth_gss_client(int fd, const char *host)
170 +{
171 +       gss_ctx_id_t ctxt = GSS_C_NO_CONTEXT;
172 +       gss_name_t target_name = GSS_C_NO_NAME;
173 +       struct init_context_data cb_data;
174 +       char *buffer;
175 +       int status;
176 +       OM_uint32 min_stat;
177 +
178 +       buffer = new_array(char, (strlen(host) + 2 + strlen(RSYNC_GSS_SERVICE)));
179 +       if (!(buffer))
180 +               out_of_memory("auth_gss_client");
181 +
182 +       sprintf(buffer, "%s@%s", RSYNC_GSS_SERVICE, host);
183 +
184 +       import_gss_name(&target_name, buffer, GSS_C_NT_HOSTBASED_SERVICE);
185 +       free(buffer);
186 +
187 +       cb_data.initiator_cred_handle = GSS_C_NO_CREDENTIAL;
188 +       cb_data.context_handle = &ctxt;
189 +       cb_data.target_name = target_name;
190 +       cb_data.mech_type = GSS_C_NO_OID;
191 +       cb_data.req_flags = GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
192 +       cb_data.time_req = 0;
193 +       cb_data.input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
194 +       cb_data.actual_mech_type = NULL;
195 +       cb_data.ret_flags = NULL;
196 +       cb_data.time_rec = NULL;
197 +
198 +       status = do_gss_dialog(fd, fd, 0, &cb_init_sec_context, (void *)&cb_data);
199 +       if (ctxt != GSS_C_NO_CONTEXT)
200 +               gss_delete_sec_context(&min_stat, &ctxt, GSS_C_NO_BUFFER);
201 +       free_gss_name(&target_name);
202 +
203 +       return status;
204 +}
205 +
206 +/*
207 + * The call back function for a gss_init_sec_context dialog
208 + */
209 +OM_uint32 cb_init_sec_context(OM_uint32 *min_statp, gss_buffer_t in_token, gss_buffer_t out_token, void *cb_data)
210 +{
211 +       struct init_context_data *context_data;
212 +
213 +       context_data = (struct init_context_data *) cb_data;
214 +       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);
215 +}
216 +
217 +/* Possibly negotiate authentication with the client.  Use "leader" to
218 + * start off the auth if necessary.
219 + *
220 + * Return NULL if authentication failed.  Return "" if anonymous access.
221 + * Otherwise return username.
222 + */
223 +char *auth_gss_server(int fd_in, int fd_out, int module, const char *host, const char *addr, const char *leader)
224 +{
225 +       struct accept_context_data cb_data;
226 +       gss_cred_id_t server_creds = GSS_C_NO_CREDENTIAL;
227 +       gss_ctx_id_t context = GSS_C_NO_CONTEXT;
228 +       OM_uint32 ret_flags;
229 +       char *users = lp_auth_users(module);
230 +       OM_uint32 maj_stat, min_stat;
231 +       gss_name_t server_name = GSS_C_NO_NAME;
232 +       gss_name_t client_name = GSS_C_NO_NAME;
233 +       gss_OID doid = GSS_C_NO_OID;
234 +       char *user = NULL;
235 +
236 +       /* if no auth list then allow anyone in! */
237 +       if (!users || !*users)
238 +               return "";
239 +
240 +       import_gss_name(&server_name, "host", GSS_C_NT_HOSTBASED_SERVICE);
241 +
242 +       maj_stat = gss_acquire_cred(&min_stat, server_name, GSS_C_INDEFINITE, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL);
243 +       if (maj_stat != GSS_S_COMPLETE) {
244 +               error_gss(maj_stat, min_stat, "error acquiring credentials on module %s from %s (%s)", lp_name(module), host, addr);
245 +               return NULL;
246 +       }
247 +
248 +       io_printf(fd_out, "%s\n", leader);
249 +
250 +       cb_data.context_handle = &context;
251 +       cb_data.acceptor_cred_handle = server_creds;
252 +       cb_data.input_chan_bindings = GSS_C_NO_CHANNEL_BINDINGS;
253 +       cb_data.src_name = &client_name;
254 +       cb_data.mech_type = &doid;
255 +       cb_data.ret_flags = &ret_flags;
256 +       cb_data.time_rec = NULL;
257 +       cb_data.delegated_cred_handle = NULL;
258 +
259 +       if (do_gss_dialog(fd_in, fd_out, -1, &cb_accept_sec_context, (void *)&cb_data) < 0)
260 +               return NULL;
261 +
262 +       user = get_cn(client_name, doid);
263 +
264 +       free_gss_name(&server_name);
265 +       free_gss_name(&client_name);
266 +
267 +       return user;
268 +}
269 +
270 +/*
271 + * The call back function for a gss_accept_sec_context dialog
272 + */
273 +OM_uint32 cb_accept_sec_context(OM_uint32 *min_statp, gss_buffer_t in_token, gss_buffer_t out_token, void *cb_data)
274 +{
275 +       struct accept_context_data *context_data;
276 +
277 +       context_data = (struct accept_context_data *) cb_data;
278 +       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);
279 +}
280 +
281 +void free_gss_buffer(gss_buffer_t gss_buffer)
282 +{
283 +       OM_uint32 maj_stat, min_stat;
284 +
285 +       if (gss_buffer->length > 0) {
286 +               maj_stat = gss_release_buffer(&min_stat, gss_buffer);
287 +               if (maj_stat != GSS_S_COMPLETE) {
288 +                       error_gss(maj_stat, min_stat, "can't release a buffer");
289 +               }
290 +       }
291 +}
292 +
293 +void free_gss_name(gss_name_t *gss_buffer)
294 +{
295 +       OM_uint32 maj_stat, min_stat;
296 +
297 +       if (*gss_buffer != GSS_C_NO_NAME) {
298 +               maj_stat = gss_release_name(&min_stat, gss_buffer);
299 +               if (maj_stat != GSS_S_COMPLETE) {
300 +                       error_gss(maj_stat, min_stat, "can't release a name");
301 +               }
302 +       }
303 +}
304 +
305 +void import_gss_name(gss_name_t *gss_name, const char *name, gss_OID type)
306 +{
307 +       gss_buffer_desc gssname;
308 +       OM_uint32 maj_stat, min_stat;
309 +
310 +       if (!(gssname.value = strdup(name)))
311 +               out_of_memory("import_gss_name");
312 +       gssname.length = strlen(name) +1 ;
313 +
314 +       maj_stat = gss_import_name(&min_stat, &gssname, type, gss_name);
315 +
316 +       if (maj_stat != GSS_S_COMPLETE)
317 +               error_gss(maj_stat, min_stat, "can't resolve %s", name);
318 +
319 +       free_gss_buffer(&gssname);
320 +}
321 +
322 +char *export_name(const gss_name_t input_name)
323 +{
324 +       OM_uint32 maj_stat, min_stat;
325 +       gss_buffer_desc exported_name;
326 +       char *exported;
327 +       gss_OID name_oid;
328 +
329 +       exported = NULL;
330 +
331 +       maj_stat = gss_display_name(&min_stat, input_name, &exported_name, &name_oid);
332 +       if (maj_stat != GSS_S_COMPLETE) {
333 +               error_gss(maj_stat, min_stat, "can't get display name");
334 +               return NULL;
335 +       }
336 +
337 +       if (exported_name.length > 0) {
338 +               if (!(exported = strdup(exported_name.value)))
339 +                       out_of_memory("export_name");
340 +       }
341 +
342 +       free_gss_buffer(&exported_name);
343 +
344 +       return exported;
345 +}
346 +
347 +void error_gss(OM_uint32 major, OM_uint32 minor, const char *format, ...)
348 +{
349 +       OM_uint32 min_stat;
350 +       gss_buffer_desc gss_msg = GSS_C_EMPTY_BUFFER;
351 +       OM_uint32 msg_ctx;
352 +       va_list ap;
353 +       char message[BIGPATHBUFLEN];
354 +
355 +       va_start(ap, format);
356 +       vsnprintf(message, sizeof message, format, ap);
357 +       va_end(ap);
358 +
359 +       msg_ctx = 0;
360 +       if (major != GSS_S_FAILURE) /* Don't print unspecified failure, the message is useless */
361 +               do {
362 +                       gss_display_status(&min_stat, major, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &gss_msg);
363 +                       rprintf(FERROR, "GSS-API error: %s: %s\n", message, (char *) gss_msg.value);
364 +                       free_gss_buffer(&gss_msg);
365 +               } while (msg_ctx != 0);
366 +
367 +       if (minor != 0) {
368 +               do {
369 +                       gss_display_status(&min_stat, minor, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &gss_msg);
370 +                       rprintf(FERROR, "GSS-API error: %s: %s\n",message, (char *) gss_msg.value);
371 +                       free_gss_buffer(&gss_msg);
372 +               } while (msg_ctx != 0);
373 +       }
374 +}
375 +
376 +/*
377 + * This function manage a gss dialog
378 + * gss tokens are eaten by a call-back function and then send by this function.
379 + * Argument to this function can be passed throught the cb_data argument
380 + * When told to act as a server, it just begin to wait for a first token before beginning operation
381 + * on it
382 + */
383 +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)
384 +{
385 +       OM_uint32 maj_stat, min_stat;
386 +       gss_buffer_desc in_token = GSS_C_EMPTY_BUFFER;
387 +       gss_buffer_desc out_token = GSS_C_EMPTY_BUFFER;
388 +
389 +       if (isServer)
390 +               recv_gss_token(fd_in, &in_token);
391 +
392 +       do {
393 +               maj_stat = (*eat_token)(&min_stat, &in_token, &out_token, cb_data);
394 +               free_gss_buffer(&in_token);
395 +               if (maj_stat != GSS_S_COMPLETE
396 +                && maj_stat != GSS_S_CONTINUE_NEEDED) {
397 +                       error_gss(maj_stat, min_stat, "error during dialog");
398 +                       return -1;
399 +               }
400 +
401 +               if (out_token.length != 0) {
402 +                       send_gss_token(fd_out, &out_token);
403 +               }
404 +               free_gss_buffer(&out_token);
405 +
406 +               if (maj_stat == GSS_S_CONTINUE_NEEDED) {
407 +                       recv_gss_token(fd_in, &in_token);
408 +               }
409 +       } while (maj_stat == GSS_S_CONTINUE_NEEDED);
410 +
411 +       return 0;
412 +}
413 +
414 +char *get_cn(const gss_name_t input_name, const gss_OID mech_type)
415 +{
416 +       OM_uint32 maj_stat, min_stat;
417 +       gss_name_t output_name;
418 +       gss_buffer_desc exported_name;
419 +       char *cn;
420 +
421 +       cn = NULL;
422 +       maj_stat = gss_canonicalize_name(&min_stat, input_name, mech_type, &output_name);
423 +       if (maj_stat != GSS_S_COMPLETE) {
424 +               error_gss(maj_stat, min_stat, "canonizing name");
425 +               return NULL;
426 +       }
427 +
428 +       maj_stat = gss_export_name(&min_stat, output_name, &exported_name);
429 +       if (maj_stat != GSS_S_COMPLETE) {
430 +               error_gss(maj_stat, min_stat, "canonizing name");
431 +               return NULL;
432 +       }
433 +       if (exported_name.length > 0) {
434 +               if (!(cn = strdup(exported_name.value)))
435 +                       out_of_memory("auth_server");
436 +       }
437 +
438 +       free_gss_name(&output_name);
439 +       free_gss_buffer(&exported_name);
440 +
441 +       return cn;
442 +}
443 +
444 +void send_gss_token(int fd, gss_buffer_t token)
445 +{
446 +       write_int(fd, token->length);
447 +       write_buf(fd, token->value, token->length);
448 +}
449 +
450 +void recv_gss_token(int fd, gss_buffer_t token)
451 +{
452 +       token->length = read_int(fd);
453 +       if (token->length > 0) {
454 +               if (!(token->value = new_array(char, token->length)))
455 +                       out_of_memory("recv_gss_token");
456 +               read_buf(fd, token->value, token->length);
457 +       }
458 +}
459 +#endif /* GSSAPI_OPTION */
460 diff --git a/loadparm.c b/loadparm.c
461 --- a/loadparm.c
462 +++ b/loadparm.c
463 @@ -151,6 +151,7 @@ typedef struct {
464         BOOL strict_modes;
465         BOOL transfer_logging;
466         BOOL use_chroot;
467 +       BOOL use_gssapi;
468         BOOL write_only;
469  } local_vars;
470  
471 @@ -225,6 +226,7 @@ static const all_vars Defaults = {
472   /* strict_modes; */           True,
473   /* transfer_logging; */       False,
474   /* use_chroot; */             True,
475 + /* use_gssapi; */             False,
476   /* write_only; */             False,
477   }
478  };
479 @@ -362,6 +364,7 @@ static struct parm_struct parm_table[] =
480   {"transfer logging",  P_BOOL,   P_LOCAL, &Vars.l.transfer_logging,    NULL,0},
481   {"uid",               P_STRING, P_LOCAL, &Vars.l.uid,                 NULL,0},
482   {"use chroot",        P_BOOL,   P_LOCAL, &Vars.l.use_chroot,          NULL,0},
483 + {"use gssapi",        P_BOOL,   P_LOCAL, &Vars.l.use_gssapi,          NULL,0},
484   {"write only",        P_BOOL,   P_LOCAL, &Vars.l.write_only,          NULL,0},
485   {NULL,                P_BOOL,   P_NONE,  NULL,                        NULL,0}
486  };
487 @@ -494,6 +497,7 @@ FN_LOCAL_BOOL(lp_reverse_lookup, reverse_lookup)
488  FN_LOCAL_BOOL(lp_strict_modes, strict_modes)
489  FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging)
490  FN_LOCAL_BOOL(lp_use_chroot, use_chroot)
491 +FN_LOCAL_BOOL(lp_use_gssapi, use_gssapi)
492  FN_LOCAL_BOOL(lp_write_only, write_only)
493  
494  /* Assign a copy of v to *s.  Handles NULL strings.  We don't worry
495 diff --git a/main.c b/main.c
496 --- a/main.c
497 +++ b/main.c
498 @@ -1397,7 +1397,7 @@ static int start_client(int argc, char *argv[])
499          * remote shell command, we need to do the RSYNCD protocol first */
500         if (daemon_over_rsh) {
501                 int tmpret;
502 -               tmpret = start_inband_exchange(f_in, f_out, shell_user, remote_argc, remote_argv);
503 +               tmpret = start_inband_exchange(f_in, f_out, shell_user, shell_machine, remote_argc, remote_argv);
504                 if (tmpret < 0)
505                         return tmpret;
506         }
507 diff --git a/rsync.h b/rsync.h
508 --- a/rsync.h
509 +++ b/rsync.h
510 @@ -472,6 +472,15 @@ enum delret {
511  #define iconv_t int
512  #endif
513  
514 +#ifdef GSSAPI_OPTION
515 +#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
516 +#include <gssapi/gssapi_generic.h>
517 +#endif
518 +#ifdef HAVE_GSSAPI_GSSAPI_H
519 +#include <gssapi/gssapi.h>
520 +#endif
521 +#endif
522 +
523  #include <assert.h>
524  
525  #include "lib/pool_alloc.h"