155682Smarkm/*
2233294Sstas * Copyright (c) 1997 - 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;
3855682Smarkmchar krb5_tkfile[MAXPATHLEN];
3955682Smarkm
4055682Smarkmstatic int help_flag;
4155682Smarkmstatic int version_flag;
4255682Smarkmstatic char *port_str;
43103423Snectarchar *service = KF_SERVICE;
4455682Smarkmint do_inetd = 0;
4555682Smarkmstatic char *regpag_str=NULL;
4655682Smarkm
4755682Smarkmstatic struct getargs args[] = {
4855682Smarkm    { "port", 'p', arg_string, &port_str, "port to listen to", "port" },
4955682Smarkm    { "inetd",'i',arg_flag, &do_inetd,
5055682Smarkm       "Not started from inetd", NULL },
5155682Smarkm    { "regpag",'R',arg_string,&regpag_str,"path to regpag binary","regpag"},
5255682Smarkm    { "help", 'h', arg_flag, &help_flag },
5355682Smarkm    { "version", 0, arg_flag, &version_flag }
5455682Smarkm};
5555682Smarkm
5655682Smarkmstatic int num_args = sizeof(args) / sizeof(args[0]);
5755682Smarkm
5855682Smarkmstatic void
5955682Smarkmusage(int code, struct getargs *args, int num_args)
6055682Smarkm{
6155682Smarkm    arg_printusage(args, num_args, NULL, "");
6255682Smarkm    exit(code);
6355682Smarkm}
6455682Smarkm
6555682Smarkmstatic int
6655682Smarkmserver_setup(krb5_context *context, int argc, char **argv)
6755682Smarkm{
6855682Smarkm    int port = 0;
6955682Smarkm    int local_argc;
7055682Smarkm
7155682Smarkm    local_argc = krb5_program_setup(context, argc, argv, args, num_args, usage);
7255682Smarkm
7355682Smarkm    if(help_flag)
7455682Smarkm	(*usage)(0, args, num_args);
7555682Smarkm    if(version_flag) {
7655682Smarkm	print_version(NULL);
7755682Smarkm	exit(0);
7855682Smarkm    }
79233294Sstas
8055682Smarkm    if(port_str){
8155682Smarkm	struct servent *s = roken_getservbyname(port_str, "tcp");
8255682Smarkm	if(s)
8355682Smarkm	    port = s->s_port;
8455682Smarkm	else {
8555682Smarkm	    char *ptr;
8655682Smarkm
8755682Smarkm	    port = strtol (port_str, &ptr, 10);
8855682Smarkm	    if (port == 0 && ptr == port_str)
8955682Smarkm		errx (1, "Bad port `%s'", port_str);
9055682Smarkm	    port = htons(port);
9155682Smarkm	}
9255682Smarkm    }
9355682Smarkm
9455682Smarkm    if (port == 0)
95103423Snectar	port = krb5_getportbyname (*context, KF_PORT_NAME, "tcp", KF_PORT_NUM);
9655682Smarkm
9755682Smarkm    if(argv[local_argc] != NULL)
9855682Smarkm        usage(1, args, num_args);
99233294Sstas
10055682Smarkm    return port;
10155682Smarkm}
10255682Smarkm
103103423Snectarstatic int protocol_version;
10455682Smarkm
105103423Snectarstatic krb5_boolean
106103423Snectarkfd_match_version(const void *arg, const char *version)
10755682Smarkm{
108103423Snectar    if(strcmp(version, KF_VERSION_1) == 0) {
109103423Snectar	protocol_version = 1;
110103423Snectar	return TRUE;
111103423Snectar    } else if (strlen(version) == 4 &&
112103423Snectar	       version[0] == '0' &&
113103423Snectar	       version[1] == '.' &&
114103423Snectar	       (version[2] == '4' || version[2] == '3') &&
115178825Sdfr	       islower((unsigned char)version[3])) {
116103423Snectar	protocol_version = 0;
117103423Snectar	return TRUE;
118103423Snectar    }
119103423Snectar    return FALSE;
12055682Smarkm}
12155682Smarkm
12255682Smarkmstatic int
12355682Smarkmproto (int sock, const char *service)
12455682Smarkm{
12555682Smarkm    krb5_auth_context auth_context;
12655682Smarkm    krb5_error_code status;
12755682Smarkm    krb5_principal server;
12855682Smarkm    krb5_ticket *ticket;
12955682Smarkm    char *name;
13055682Smarkm    char ret_string[10];
13155682Smarkm    char hostname[MAXHOSTNAMELEN];
13255682Smarkm    krb5_data data;
13355682Smarkm    krb5_data remotename;
13455682Smarkm    krb5_data tk_file;
13555682Smarkm    krb5_ccache ccache;
13655682Smarkm    char ccname[MAXPATHLEN];
13755682Smarkm    struct passwd *pwd;
13855682Smarkm
13955682Smarkm    status = krb5_auth_con_init (context, &auth_context);
14055682Smarkm    if (status)
141103423Snectar	krb5_err(context, 1, status, "krb5_auth_con_init");
14255682Smarkm
14355682Smarkm    status = krb5_auth_con_setaddrs_from_fd (context,
14455682Smarkm					     auth_context,
14555682Smarkm					     &sock);
14655682Smarkm    if (status)
147103423Snectar	krb5_err(context, 1, status, "krb5_auth_con_setaddr");
14855682Smarkm
14955682Smarkm    if(gethostname (hostname, sizeof(hostname)) < 0)
150103423Snectar	krb5_err(context, 1, errno, "gethostname");
15155682Smarkm
15255682Smarkm    status = krb5_sname_to_principal (context,
15355682Smarkm				      hostname,
15455682Smarkm				      service,
15555682Smarkm				      KRB5_NT_SRV_HST,
15655682Smarkm				      &server);
15755682Smarkm    if (status)
158103423Snectar	krb5_err(context, 1, status, "krb5_sname_to_principal");
15955682Smarkm
160103423Snectar    status = krb5_recvauth_match_version (context,
161103423Snectar					  &auth_context,
162103423Snectar					  &sock,
163103423Snectar					  kfd_match_version,
164103423Snectar					  NULL,
165103423Snectar					  server,
166103423Snectar					  0,
167103423Snectar					  NULL,
168103423Snectar					  &ticket);
16955682Smarkm    if (status)
170103423Snectar	krb5_err(context, 1, status, "krb5_recvauth");
17155682Smarkm
17255682Smarkm    status = krb5_unparse_name (context,
17355682Smarkm				ticket->client,
17455682Smarkm				&name);
17555682Smarkm    if (status)
176103423Snectar	krb5_err(context, 1, status, "krb5_unparse_name");
17755682Smarkm
178103423Snectar    if(protocol_version == 0) {
179103423Snectar	data.data = "old clnt"; /* XXX old clients only had room for
180103423Snectar                                   10 bytes of message, and also
181103423Snectar                                   didn't show it to the user */
182103423Snectar	data.length = strlen(data.data) + 1;
183103423Snectar	krb5_write_message(context, &sock, &data);
184103423Snectar	sleep(2); /* XXX give client time to finish */
185103423Snectar	krb5_errx(context, 1, "old client; exiting");
18655682Smarkm    }
18755682Smarkm
188103423Snectar    status=krb5_read_priv_message (context, auth_context,
189103423Snectar				   &sock, &remotename);
190103423Snectar    if (status)
191103423Snectar	krb5_err(context, 1, status, "krb5_read_message");
192233294Sstas    status=krb5_read_priv_message (context, auth_context,
193103423Snectar				   &sock, &tk_file);
194103423Snectar    if (status)
195103423Snectar	krb5_err(context, 1, status, "krb5_read_message");
196103423Snectar
19755682Smarkm    krb5_data_zero (&data);
19855682Smarkm
199103423Snectar    if(((char*)remotename.data)[remotename.length-1] != '\0')
200103423Snectar	krb5_errx(context, 1, "unterminated received");
201103423Snectar    if(((char*)tk_file.data)[tk_file.length-1] != '\0')
202103423Snectar	krb5_errx(context, 1, "unterminated received");
20355682Smarkm
204103423Snectar    status = krb5_read_priv_message(context, auth_context, &sock, &data);
20555682Smarkm
20655682Smarkm    if (status) {
207103423Snectar	krb5_err(context, 1, errno, "krb5_read_priv_message");
20855682Smarkm	goto out;
20955682Smarkm    }
21055682Smarkm
21155682Smarkm    pwd = getpwnam ((char *)(remotename.data));
21255682Smarkm    if (pwd == NULL) {
21355682Smarkm	status=1;
214103423Snectar	krb5_warnx(context, "getpwnam: %s failed",(char *)(remotename.data));
21555682Smarkm	goto out;
21655682Smarkm    }
21755682Smarkm
21855682Smarkm    if(!krb5_kuserok (context,
219103423Snectar		      ticket->client,
220103423Snectar		      (char *)(remotename.data))) {
22155682Smarkm	status=1;
222103423Snectar	krb5_warnx(context, "krb5_kuserok: permission denied");
22355682Smarkm	goto out;
22455682Smarkm    }
22555682Smarkm
22655682Smarkm    if (setgid(pwd->pw_gid) < 0) {
227103423Snectar	krb5_warn(context, errno, "setgid");
22855682Smarkm	goto out;
22955682Smarkm    }
23055682Smarkm    if (setuid(pwd->pw_uid) < 0) {
231103423Snectar	krb5_warn(context, errno, "setuid");
23255682Smarkm	goto out;
23355682Smarkm    }
23455682Smarkm
23555682Smarkm    if (tk_file.length != 1)
23655682Smarkm	snprintf (ccname, sizeof(ccname), "%s", (char *)(tk_file.data));
23755682Smarkm    else
238178825Sdfr	snprintf (ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%lu",
239178825Sdfr		  (unsigned long)pwd->pw_uid);
24055682Smarkm
24155682Smarkm    status = krb5_cc_resolve (context, ccname, &ccache);
24255682Smarkm    if (status) {
243103423Snectar	krb5_warn(context, status, "krb5_cc_resolve");
24455682Smarkm        goto out;
24555682Smarkm    }
24655682Smarkm    status = krb5_cc_initialize (context, ccache, ticket->client);
24755682Smarkm    if (status) {
248103423Snectar	krb5_warn(context, status, "krb5_cc_initialize");
24955682Smarkm        goto out;
25055682Smarkm    }
25172445Sassar    status = krb5_rd_cred2 (context, auth_context, ccache, &data);
25255682Smarkm    krb5_cc_close (context, ccache);
25355682Smarkm    if (status) {
254103423Snectar	krb5_warn(context, status, "krb5_rd_cred");
25555682Smarkm        goto out;
25655682Smarkm
25755682Smarkm    }
25855682Smarkm    strlcpy(krb5_tkfile,ccname,sizeof(krb5_tkfile));
259103423Snectar    krb5_warnx(context, "%s forwarded ticket to %s,%s",
260103423Snectar	       name,
261103423Snectar	       (char *)(remotename.data),ccname);
262103423Snectar  out:
26355682Smarkm    if (status) {
264120945Snectar	strlcpy(ret_string, "no", sizeof(ret_string));
265103423Snectar	krb5_warnx(context, "failed");
26655682Smarkm    } else  {
267120945Snectar	strlcpy(ret_string, "ok", sizeof(ret_string));
26855682Smarkm    }
26955682Smarkm
27055682Smarkm    krb5_data_free (&tk_file);
27155682Smarkm    krb5_data_free (&remotename);
27255682Smarkm    krb5_data_free (&data);
27355682Smarkm    free(name);
27455682Smarkm
275103423Snectar    data.data = ret_string;
276103423Snectar    data.length = strlen(ret_string) + 1;
277233294Sstas    status = krb5_write_priv_message(context, auth_context, &sock, &data);
278233294Sstas    krb5_auth_con_free(context, auth_context);
279233294Sstas
280233294Sstas    return status;
28155682Smarkm}
28255682Smarkm
28355682Smarkmstatic int
28455682Smarkmdoit (int port, const char *service)
28555682Smarkm{
28655682Smarkm    if (do_inetd)
287233294Sstas	mini_inetd(port, NULL);
28855682Smarkm    return proto (STDIN_FILENO, service);
28955682Smarkm}
29055682Smarkm
29155682Smarkmint
29255682Smarkmmain(int argc, char **argv)
29355682Smarkm{
29455682Smarkm    int port;
29555682Smarkm    int ret;
296103423Snectar    krb5_log_facility *fac;
29755682Smarkm
29878527Sassar    setprogname (argv[0]);
29955682Smarkm    roken_openlog (argv[0], LOG_ODELAY | LOG_PID,LOG_AUTH);
30055682Smarkm    port = server_setup(&context, argc, argv);
301103423Snectar    ret = krb5_openlog(context, "kfd", &fac);
302103423Snectar    if(ret) krb5_err(context, 1, ret, "krb5_openlog");
303103423Snectar    ret = krb5_set_warn_dest(context, fac);
304103423Snectar    if(ret) krb5_err(context, 1, ret, "krb5_set_warn_dest");
305103423Snectar
30655682Smarkm    ret = doit (port, service);
30755682Smarkm    closelog();
30855682Smarkm    if (ret == 0 && regpag_str != NULL)
30955682Smarkm        ret = execl(regpag_str, "regpag", "-t", krb5_tkfile, "-r", NULL);
31055682Smarkm    return ret;
31155682Smarkm}
312