/ appl / kf / kf.c
kf.c
  1  /*
  2   * Copyright (c) 1997 - 2000, 2002 Kungliga Tekniska Högskolan
  3   * (Royal Institute of Technology, Stockholm, Sweden).
  4   * All rights reserved.
  5   *
  6   * Redistribution and use in source and binary forms, with or without
  7   * modification, are permitted provided that the following conditions
  8   * are met:
  9   *
 10   * 1. Redistributions of source code must retain the above copyright
 11   *    notice, this list of conditions and the following disclaimer.
 12   *
 13   * 2. Redistributions in binary form must reproduce the above copyright
 14   *    notice, this list of conditions and the following disclaimer in the
 15   *    documentation and/or other materials provided with the distribution.
 16   *
 17   * 3. Neither the name of the Institute nor the names of its contributors
 18   *    may be used to endorse or promote products derived from this software
 19   *    without specific prior written permission.
 20   *
 21   * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 24   * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 25   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 27   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 28   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 29   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 30   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 31   * SUCH DAMAGE.
 32   */
 33  
 34  #include "kf_locl.h"
 35  RCSID("$Id$");
 36  
 37  krb5_context context;
 38  static int help_flag;
 39  static int version_flag;
 40  static char *port_str;
 41  const char *service     = KF_SERVICE;
 42  const char *remote_name = NULL;
 43  int forwardable   = 0;
 44  const char *ccache_name = NULL;
 45  
 46  static struct getargs args[] = {
 47      { "port", 'p', arg_string, &port_str, "port to connect to", "port" },
 48      { "login", 'l',arg_string, &remote_name,"remote login name","login"},
 49      { "ccache", 'c',arg_string, &ccache_name, "remote cred cache","ccache"},
 50      { "forwardable",'F',arg_flag,&forwardable,
 51         "Forward forwardable credentials", NULL },
 52      { "forwardable",'G',arg_negative_flag,&forwardable,
 53         "Don't forward forwardable credentials", NULL },
 54      { "help", 'h', arg_flag, &help_flag },
 55      { "version", 0, arg_flag, &version_flag }
 56  };
 57  
 58  static int num_args = sizeof(args) / sizeof(args[0]);
 59  
 60  static void
 61  usage(int code, struct getargs *args, int num_args)
 62  {
 63      arg_printusage(args, num_args, NULL, "hosts");
 64      exit(code);
 65  }
 66  
 67  static int
 68  client_setup(krb5_context *context, int *argc, char **argv)
 69  {
 70      int optind = 0;
 71      int port = 0;
 72      int status;
 73  
 74      setprogname (argv[0]);
 75  
 76      status = krb5_init_context (context);
 77      if (status)
 78  	errx(1, "krb5_init_context failed: %d", status);
 79  
 80      forwardable = krb5_config_get_bool (*context, NULL,
 81  					"libdefaults",
 82  					"forwardable",
 83  					NULL);
 84  
 85      if (getarg (args, num_args, *argc, argv, &optind))
 86  	usage(1, args, num_args);
 87  
 88      if(help_flag)
 89  	usage (0, args, num_args);
 90      if(version_flag) {
 91  	print_version(NULL);
 92  	exit(0);
 93      }
 94  
 95      if(port_str) {
 96  	struct servent *s = roken_getservbyname(port_str, "tcp");
 97  	if(s)
 98  	    port = s->s_port;
 99  	else {
100  	    char *ptr;
101  
102  	    port = strtol (port_str, &ptr, 10);
103  	    if (port == 0 && ptr == port_str)
104  		errx (1, "Bad port `%s'", port_str);
105  	    port = htons(port);
106  	}
107      }
108  
109      if (port == 0)
110  	port = krb5_getportbyname (*context, KF_PORT_NAME, "tcp", KF_PORT_NUM);
111  
112      if(*argc - optind < 1)
113          usage(1, args, num_args);
114      *argc = optind;
115  
116      return port;
117  }
118  
119  /*
120   * forward creds to `hostname'/`service' over `sock'
121   * return 0 iff OK
122   */
123  
124  static int
125  proto (int sock, const char *hostname, const char *service,
126         char *message, size_t len)
127  {
128      krb5_auth_context auth_context;
129      krb5_error_code status;
130      krb5_principal server;
131      krb5_data data;
132      krb5_data data_send;
133  
134      krb5_ccache     ccache;
135      krb5_creds      creds;
136      krb5_kdc_flags  flags;
137      krb5_principal  principal;
138  
139      status = krb5_auth_con_init (context, &auth_context);
140      if (status) {
141  	krb5_warn (context, status, "krb5_auth_con_init");
142  	return 1;
143      }
144  
145      status = krb5_auth_con_setaddrs_from_fd (context,
146  					     auth_context,
147  					     &sock);
148      if (status) {
149  	krb5_auth_con_free(context, auth_context);
150  	krb5_warn (context, status, "krb5_auth_con_setaddr");
151  	return 1;
152      }
153  
154      status = krb5_sname_to_principal (context,
155  				      hostname,
156  				      service,
157  				      KRB5_NT_SRV_HST,
158  				      &server);
159      if (status) {
160  	krb5_auth_con_free(context, auth_context);
161  	krb5_warn (context, status, "krb5_sname_to_principal");
162  	return 1;
163      }
164  
165      status = krb5_sendauth (context,
166  			    &auth_context,
167  			    &sock,
168  			    KF_VERSION_1,
169  			    NULL,
170  			    server,
171  			    AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
172  			    NULL,
173  			    NULL,
174  			    NULL,
175  			    NULL,
176  			    NULL,
177  			    NULL);
178      if (status) {
179  	krb5_auth_con_free(context, auth_context);
180  	krb5_warn(context, status, "krb5_sendauth");
181  	return 1;
182      }
183  
184      if (ccache_name == NULL)
185  	ccache_name = "";
186  
187      data_send.data   = (void *)remote_name;
188      data_send.length = strlen(remote_name) + 1;
189      status = krb5_write_priv_message(context, auth_context, &sock, &data_send);
190      if (status) {
191  	krb5_auth_con_free(context, auth_context);
192  	krb5_warn (context, status, "krb5_write_message");
193  	return 1;
194      }
195      data_send.data   = (void *)ccache_name;
196      data_send.length = strlen(ccache_name)+1;
197      status = krb5_write_priv_message(context, auth_context, &sock, &data_send);
198      if (status) {
199  	krb5_auth_con_free(context, auth_context);
200  	krb5_warn (context, status, "krb5_write_message");
201  	return 1;
202      }
203  
204      memset (&creds, 0, sizeof(creds));
205  
206      status = krb5_cc_default (context, &ccache);
207      if (status) {
208  	krb5_auth_con_free(context, auth_context);
209  	krb5_warn (context, status, "krb5_cc_default");
210  	return 1;
211      }
212  
213      status = krb5_cc_get_principal (context, ccache, &principal);
214      if (status) {
215  	krb5_auth_con_free(context, auth_context);
216  	krb5_warn (context, status, "krb5_cc_get_principal");
217  	return 1;
218      }
219  
220      creds.client = principal;
221  
222      status = krb5_make_principal (context,
223  				  &creds.server,
224  				  principal->realm,
225  				  KRB5_TGS_NAME,
226  				  principal->realm,
227  				  NULL);
228  
229      if (status) {
230  	krb5_auth_con_free(context, auth_context);
231  	krb5_warn (context, status, "krb5_make_principal");
232  	return 1;
233      }
234  
235      creds.times.endtime = 0;
236  
237      flags.i = 0;
238      flags.b.forwarded   = 1;
239      flags.b.forwardable = forwardable;
240  
241      status = krb5_get_forwarded_creds (context,
242  				       auth_context,
243  				       ccache,
244  				       flags.i,
245  				       hostname,
246  				       &creds,
247  				       &data);
248      if (status) {
249  	krb5_auth_con_free(context, auth_context);
250  	krb5_warn (context, status, "krb5_get_forwarded_creds");
251  	return 1;
252      }
253  
254      status = krb5_write_priv_message(context, auth_context, &sock, &data);
255  
256      if (status) {
257  	krb5_auth_con_free(context, auth_context);
258  	krb5_warn (context, status, "krb5_mk_priv");
259  	return 1;
260      }
261  
262      krb5_data_free (&data);
263  
264      status = krb5_read_priv_message(context, auth_context, &sock, &data);
265      krb5_auth_con_free(context, auth_context);
266      if (status) {
267  	krb5_warn (context, status, "krb5_mk_priv");
268  	return 1;
269      }
270      if(data.length >= len) {
271  	krb5_warnx (context, "returned string is too long, truncating");
272  	memcpy(message, data.data, len);
273  	message[len - 1] = '\0';
274      } else {
275  	memcpy(message, data.data, data.length);
276  	message[data.length] = '\0';
277      }
278      krb5_data_free (&data);
279  
280      return(strcmp(message, "ok"));
281  }
282  
283  static int
284  doit (const char *hostname, int port, const char *service,
285        char *message, size_t len)
286  {
287      struct addrinfo *ai, *a;
288      struct addrinfo hints;
289      int error;
290      char portstr[NI_MAXSERV];
291  
292      memset (&hints, 0, sizeof(hints));
293      hints.ai_socktype = SOCK_STREAM;
294      hints.ai_protocol = IPPROTO_TCP;
295  
296      snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
297  
298      error = getaddrinfo (hostname, portstr, &hints, &ai);
299      if (error) {
300  	errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error));
301      }
302  
303      for (a = ai; a != NULL; a = a->ai_next) {
304  	int s;
305  
306  	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
307  	if (s < 0)
308  	    continue;
309  	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
310  	    warn ("connect(%s)", hostname);
311  	    close (s);
312  	    continue;
313  	}
314  	freeaddrinfo (ai);
315  	return proto (s, hostname, service, message, len);
316      }
317      warnx ("failed to contact %s", hostname);
318      freeaddrinfo (ai);
319      return 1;
320  }
321  
322  int
323  main(int argc, char **argv)
324  {
325      int argcc,port,i;
326      int ret=0;
327  
328      argcc = argc;
329      port = client_setup(&context, &argcc, argv);
330  
331      if (remote_name == NULL) {
332  	remote_name = get_default_username ();
333  	if (remote_name == NULL)
334  	    errx (1, "who are you?");
335      }
336  
337      for (i = argcc;i < argc; i++) {
338  	char message[128];
339  	ret = doit (argv[i], port, service, message, sizeof(message));
340  	if(ret == 0)
341  	    warnx ("%s: ok", argv[i]);
342  	else
343  	    warnx ("%s: failed: %s", argv[i], message);
344      }
345      return(ret);
346  }