155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 2000, 2002 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
555682Smarkm *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
955682Smarkm *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
2055682Smarkm *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#include "kf_locl.h"
35233294SstasRCSID("$Id$");
3655682Smarkm
3755682Smarkmkrb5_context context;
3855682Smarkmstatic int help_flag;
3955682Smarkmstatic int version_flag;
4055682Smarkmstatic char *port_str;
41103423Snectarconst char *service     = KF_SERVICE;
4255682Smarkmconst char *remote_name = NULL;
4355682Smarkmint forwardable   = 0;
4455682Smarkmconst char *ccache_name = NULL;
4555682Smarkm
4655682Smarkmstatic struct getargs args[] = {
4755682Smarkm    { "port", 'p', arg_string, &port_str, "port to connect to", "port" },
4855682Smarkm    { "login", 'l',arg_string, &remote_name,"remote login name","login"},
4955682Smarkm    { "ccache", 'c',arg_string, &ccache_name, "remote cred cache","ccache"},
5055682Smarkm    { "forwardable",'F',arg_flag,&forwardable,
5155682Smarkm       "Forward forwardable credentials", NULL },
5255682Smarkm    { "forwardable",'G',arg_negative_flag,&forwardable,
5355682Smarkm       "Don't forward forwardable credentials", NULL },
5455682Smarkm    { "help", 'h', arg_flag, &help_flag },
5555682Smarkm    { "version", 0, arg_flag, &version_flag }
5655682Smarkm};
5755682Smarkm
5855682Smarkmstatic int num_args = sizeof(args) / sizeof(args[0]);
5955682Smarkm
6055682Smarkmstatic void
6155682Smarkmusage(int code, struct getargs *args, int num_args)
6255682Smarkm{
6355682Smarkm    arg_printusage(args, num_args, NULL, "hosts");
6455682Smarkm    exit(code);
6555682Smarkm}
6655682Smarkm
6755682Smarkmstatic int
6855682Smarkmclient_setup(krb5_context *context, int *argc, char **argv)
6955682Smarkm{
7055682Smarkm    int optind = 0;
7155682Smarkm    int port = 0;
7255682Smarkm    int status;
7355682Smarkm
7478527Sassar    setprogname (argv[0]);
75233294Sstas
7655682Smarkm    status = krb5_init_context (context);
7755682Smarkm    if (status)
7872445Sassar	errx(1, "krb5_init_context failed: %d", status);
79233294Sstas
8055682Smarkm    forwardable = krb5_config_get_bool (*context, NULL,
8155682Smarkm					"libdefaults",
8255682Smarkm					"forwardable",
83233294Sstas					NULL);
84233294Sstas
8555682Smarkm    if (getarg (args, num_args, *argc, argv, &optind))
8655682Smarkm	usage(1, args, num_args);
8755682Smarkm
8855682Smarkm    if(help_flag)
8955682Smarkm	usage (0, args, num_args);
9055682Smarkm    if(version_flag) {
9155682Smarkm	print_version(NULL);
9255682Smarkm	exit(0);
9355682Smarkm    }
94233294Sstas
9555682Smarkm    if(port_str) {
9655682Smarkm	struct servent *s = roken_getservbyname(port_str, "tcp");
9755682Smarkm	if(s)
9855682Smarkm	    port = s->s_port;
9955682Smarkm	else {
10055682Smarkm	    char *ptr;
10155682Smarkm
10255682Smarkm	    port = strtol (port_str, &ptr, 10);
10355682Smarkm	    if (port == 0 && ptr == port_str)
10455682Smarkm		errx (1, "Bad port `%s'", port_str);
10555682Smarkm	    port = htons(port);
10655682Smarkm	}
10755682Smarkm    }
10855682Smarkm
10955682Smarkm    if (port == 0)
110103423Snectar	port = krb5_getportbyname (*context, KF_PORT_NAME, "tcp", KF_PORT_NUM);
111233294Sstas
11255682Smarkm    if(*argc - optind < 1)
11355682Smarkm        usage(1, args, num_args);
11455682Smarkm    *argc = optind;
11555682Smarkm
11655682Smarkm    return port;
11755682Smarkm}
11855682Smarkm
11955682Smarkm/*
12055682Smarkm * forward creds to `hostname'/`service' over `sock'
12155682Smarkm * return 0 iff OK
12255682Smarkm */
12355682Smarkm
12455682Smarkmstatic int
125103423Snectarproto (int sock, const char *hostname, const char *service,
126103423Snectar       char *message, size_t len)
12755682Smarkm{
12855682Smarkm    krb5_auth_context auth_context;
12955682Smarkm    krb5_error_code status;
13055682Smarkm    krb5_principal server;
13155682Smarkm    krb5_data data;
13255682Smarkm    krb5_data data_send;
13355682Smarkm
13455682Smarkm    krb5_ccache     ccache;
13555682Smarkm    krb5_creds      creds;
13655682Smarkm    krb5_kdc_flags  flags;
13755682Smarkm    krb5_principal  principal;
13855682Smarkm
13955682Smarkm    status = krb5_auth_con_init (context, &auth_context);
14055682Smarkm    if (status) {
14155682Smarkm	krb5_warn (context, status, "krb5_auth_con_init");
14255682Smarkm	return 1;
14355682Smarkm    }
14455682Smarkm
14555682Smarkm    status = krb5_auth_con_setaddrs_from_fd (context,
14655682Smarkm					     auth_context,
14755682Smarkm					     &sock);
14855682Smarkm    if (status) {
149233294Sstas	krb5_auth_con_free(context, auth_context);
15055682Smarkm	krb5_warn (context, status, "krb5_auth_con_setaddr");
15155682Smarkm	return 1;
15255682Smarkm    }
15355682Smarkm
15455682Smarkm    status = krb5_sname_to_principal (context,
15555682Smarkm				      hostname,
15655682Smarkm				      service,
15755682Smarkm				      KRB5_NT_SRV_HST,
15855682Smarkm				      &server);
15955682Smarkm    if (status) {
160233294Sstas	krb5_auth_con_free(context, auth_context);
16155682Smarkm	krb5_warn (context, status, "krb5_sname_to_principal");
16255682Smarkm	return 1;
16355682Smarkm    }
16455682Smarkm
16555682Smarkm    status = krb5_sendauth (context,
16655682Smarkm			    &auth_context,
16755682Smarkm			    &sock,
168103423Snectar			    KF_VERSION_1,
16955682Smarkm			    NULL,
17055682Smarkm			    server,
171103423Snectar			    AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
17255682Smarkm			    NULL,
17355682Smarkm			    NULL,
17455682Smarkm			    NULL,
17555682Smarkm			    NULL,
17655682Smarkm			    NULL,
17755682Smarkm			    NULL);
17855682Smarkm    if (status) {
179233294Sstas	krb5_auth_con_free(context, auth_context);
18055682Smarkm	krb5_warn(context, status, "krb5_sendauth");
18155682Smarkm	return 1;
18255682Smarkm    }
18355682Smarkm
184103423Snectar    if (ccache_name == NULL)
185103423Snectar	ccache_name = "";
18655682Smarkm
18755682Smarkm    data_send.data   = (void *)remote_name;
18855682Smarkm    data_send.length = strlen(remote_name) + 1;
189103423Snectar    status = krb5_write_priv_message(context, auth_context, &sock, &data_send);
19055682Smarkm    if (status) {
191233294Sstas	krb5_auth_con_free(context, auth_context);
19255682Smarkm	krb5_warn (context, status, "krb5_write_message");
19355682Smarkm	return 1;
19455682Smarkm    }
19555682Smarkm    data_send.data   = (void *)ccache_name;
19655682Smarkm    data_send.length = strlen(ccache_name)+1;
197103423Snectar    status = krb5_write_priv_message(context, auth_context, &sock, &data_send);
19855682Smarkm    if (status) {
199233294Sstas	krb5_auth_con_free(context, auth_context);
20055682Smarkm	krb5_warn (context, status, "krb5_write_message");
20155682Smarkm	return 1;
20255682Smarkm    }
20355682Smarkm
20455682Smarkm    memset (&creds, 0, sizeof(creds));
20555682Smarkm
20655682Smarkm    status = krb5_cc_default (context, &ccache);
20755682Smarkm    if (status) {
208233294Sstas	krb5_auth_con_free(context, auth_context);
20955682Smarkm	krb5_warn (context, status, "krb5_cc_default");
21055682Smarkm	return 1;
21155682Smarkm    }
21255682Smarkm
21355682Smarkm    status = krb5_cc_get_principal (context, ccache, &principal);
21455682Smarkm    if (status) {
215233294Sstas	krb5_auth_con_free(context, auth_context);
21655682Smarkm	krb5_warn (context, status, "krb5_cc_get_principal");
21755682Smarkm	return 1;
21855682Smarkm    }
21955682Smarkm
22055682Smarkm    creds.client = principal;
221233294Sstas
222103423Snectar    status = krb5_make_principal (context,
223103423Snectar				  &creds.server,
224103423Snectar				  principal->realm,
225103423Snectar				  KRB5_TGS_NAME,
226103423Snectar				  principal->realm,
227103423Snectar				  NULL);
22855682Smarkm
22955682Smarkm    if (status) {
230233294Sstas	krb5_auth_con_free(context, auth_context);
231103423Snectar	krb5_warn (context, status, "krb5_make_principal");
23255682Smarkm	return 1;
23355682Smarkm    }
23455682Smarkm
23555682Smarkm    creds.times.endtime = 0;
23655682Smarkm
23755682Smarkm    flags.i = 0;
23855682Smarkm    flags.b.forwarded   = 1;
23955682Smarkm    flags.b.forwardable = forwardable;
24055682Smarkm
24155682Smarkm    status = krb5_get_forwarded_creds (context,
24255682Smarkm				       auth_context,
24355682Smarkm				       ccache,
24455682Smarkm				       flags.i,
24555682Smarkm				       hostname,
24655682Smarkm				       &creds,
24755682Smarkm				       &data);
24855682Smarkm    if (status) {
249233294Sstas	krb5_auth_con_free(context, auth_context);
25055682Smarkm	krb5_warn (context, status, "krb5_get_forwarded_creds");
25155682Smarkm	return 1;
25255682Smarkm    }
25355682Smarkm
254103423Snectar    status = krb5_write_priv_message(context, auth_context, &sock, &data);
255103423Snectar
25655682Smarkm    if (status) {
257233294Sstas	krb5_auth_con_free(context, auth_context);
25855682Smarkm	krb5_warn (context, status, "krb5_mk_priv");
25955682Smarkm	return 1;
26055682Smarkm    }
261233294Sstas
262103423Snectar    krb5_data_free (&data);
26355682Smarkm
264103423Snectar    status = krb5_read_priv_message(context, auth_context, &sock, &data);
265233294Sstas    krb5_auth_con_free(context, auth_context);
266103423Snectar    if (status) {
267103423Snectar	krb5_warn (context, status, "krb5_mk_priv");
26855682Smarkm	return 1;
26955682Smarkm    }
270103423Snectar    if(data.length >= len) {
271103423Snectar	krb5_warnx (context, "returned string is too long, truncating");
272103423Snectar	memcpy(message, data.data, len);
273103423Snectar	message[len - 1] = '\0';
274103423Snectar    } else {
275103423Snectar	memcpy(message, data.data, data.length);
276103423Snectar	message[data.length] = '\0';
27755682Smarkm    }
27855682Smarkm    krb5_data_free (&data);
27955682Smarkm
280103423Snectar    return(strcmp(message, "ok"));
28155682Smarkm}
28255682Smarkm
28355682Smarkmstatic int
284233294Sstasdoit (const char *hostname, int port, const char *service,
285103423Snectar      char *message, size_t len)
28655682Smarkm{
28755682Smarkm    struct addrinfo *ai, *a;
28855682Smarkm    struct addrinfo hints;
28955682Smarkm    int error;
29055682Smarkm    char portstr[NI_MAXSERV];
29155682Smarkm
29255682Smarkm    memset (&hints, 0, sizeof(hints));
29355682Smarkm    hints.ai_socktype = SOCK_STREAM;
29455682Smarkm    hints.ai_protocol = IPPROTO_TCP;
29555682Smarkm
29655682Smarkm    snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
29755682Smarkm
29855682Smarkm    error = getaddrinfo (hostname, portstr, &hints, &ai);
29955682Smarkm    if (error) {
30055682Smarkm	errx (1, "getaddrinfo(%s): %s", hostname, gai_strerror(error));
30155682Smarkm    }
30255682Smarkm
30355682Smarkm    for (a = ai; a != NULL; a = a->ai_next) {
30455682Smarkm	int s;
30555682Smarkm
30655682Smarkm	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
30755682Smarkm	if (s < 0)
30855682Smarkm	    continue;
30955682Smarkm	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
31055682Smarkm	    warn ("connect(%s)", hostname);
31155682Smarkm	    close (s);
31255682Smarkm	    continue;
31355682Smarkm	}
31455682Smarkm	freeaddrinfo (ai);
315103423Snectar	return proto (s, hostname, service, message, len);
31655682Smarkm    }
31755682Smarkm    warnx ("failed to contact %s", hostname);
31855682Smarkm    freeaddrinfo (ai);
31955682Smarkm    return 1;
32055682Smarkm}
32155682Smarkm
32255682Smarkmint
32355682Smarkmmain(int argc, char **argv)
32455682Smarkm{
32555682Smarkm    int argcc,port,i;
32655682Smarkm    int ret=0;
327233294Sstas
32855682Smarkm    argcc = argc;
32955682Smarkm    port = client_setup(&context, &argcc, argv);
33055682Smarkm
331103423Snectar    if (remote_name == NULL) {
332103423Snectar	remote_name = get_default_username ();
333103423Snectar	if (remote_name == NULL)
334103423Snectar	    errx (1, "who are you?");
335103423Snectar    }
336103423Snectar
33755682Smarkm    for (i = argcc;i < argc; i++) {
338103423Snectar	char message[128];
339103423Snectar	ret = doit (argv[i], port, service, message, sizeof(message));
340103423Snectar	if(ret == 0)
341103423Snectar	    warnx ("%s: ok", argv[i]);
342103423Snectar	else
343103423Snectar	    warnx ("%s: failed: %s", argv[i], message);
34455682Smarkm    }
34555682Smarkm    return(ret);
34655682Smarkm}
347