xref: /freebsd/crypto/heimdal/appl/kf/kfd.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
1 /*
2  * Copyright (c) 1997 - 2001 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: kfd.c,v 1.9 2001/02/20 01:44:44 assar Exp $");
36 
37 krb5_context context;
38 char krb5_tkfile[MAXPATHLEN];
39 
40 static int help_flag;
41 static int version_flag;
42 static char *port_str;
43 char *service = SERVICE;
44 int do_inetd = 0;
45 static char *regpag_str=NULL;
46 
47 static struct getargs args[] = {
48     { "port", 'p', arg_string, &port_str, "port to listen to", "port" },
49     { "inetd",'i',arg_flag, &do_inetd,
50        "Not started from inetd", NULL },
51     { "regpag",'R',arg_string,&regpag_str,"path to regpag binary","regpag"},
52     { "help", 'h', arg_flag, &help_flag },
53     { "version", 0, arg_flag, &version_flag }
54 };
55 
56 static int num_args = sizeof(args) / sizeof(args[0]);
57 
58 static void
59 usage(int code, struct getargs *args, int num_args)
60 {
61     arg_printusage(args, num_args, NULL, "");
62     exit(code);
63 }
64 
65 static int
66 server_setup(krb5_context *context, int argc, char **argv)
67 {
68     int port = 0;
69     int local_argc;
70 
71     local_argc = krb5_program_setup(context, argc, argv, args, num_args, usage);
72 
73     if(help_flag)
74 	(*usage)(0, args, num_args);
75     if(version_flag) {
76 	print_version(NULL);
77 	exit(0);
78     }
79 
80     if(port_str){
81 	struct servent *s = roken_getservbyname(port_str, "tcp");
82 	if(s)
83 	    port = s->s_port;
84 	else {
85 	    char *ptr;
86 
87 	    port = strtol (port_str, &ptr, 10);
88 	    if (port == 0 && ptr == port_str)
89 		errx (1, "Bad port `%s'", port_str);
90 	    port = htons(port);
91 	}
92     }
93 
94     if (port == 0)
95 	port = krb5_getportbyname (*context, PORT, "tcp", PORT_NUM);
96 
97     if(argv[local_argc] != NULL)
98         usage(1, args, num_args);
99 
100     return port;
101 }
102 
103 static void
104 syslog_and_die (const char *m, ...)
105 {
106     va_list args;
107 
108     va_start(args, m);
109     vsyslog (LOG_ERR, m, args);
110     va_end(args);
111     exit (1);
112 }
113 
114 static void
115 syslog_and_cont (const char *m, ...)
116 {
117     va_list args;
118 
119     va_start(args, m);
120     vsyslog (LOG_ERR, m, args);
121     va_end(args);
122     return;
123 }
124 
125 static int
126 proto (int sock, const char *service)
127 {
128     krb5_auth_context auth_context;
129     krb5_error_code status;
130     krb5_principal server;
131     krb5_ticket *ticket;
132     char *name;
133     char ret_string[10];
134     char hostname[MAXHOSTNAMELEN];
135     krb5_data packet;
136     krb5_data data;
137     krb5_data remotename;
138     krb5_data tk_file;
139 
140     u_int32_t len, net_len;
141     krb5_ccache ccache;
142     char ccname[MAXPATHLEN];
143     struct passwd *pwd;
144     ssize_t n;
145 
146     status = krb5_auth_con_init (context, &auth_context);
147     if (status)
148 	syslog_and_die("krb5_auth_con_init: %s",
149 	      krb5_get_err_text(context, status));
150 
151     status = krb5_auth_con_setaddrs_from_fd (context,
152 					     auth_context,
153 					     &sock);
154     if (status)
155 	syslog_and_die("krb5_auth_con_setaddr: %s",
156 	      krb5_get_err_text(context, status));
157 
158     if(gethostname (hostname, sizeof(hostname)) < 0)
159 	syslog_and_die("gethostname: %s",strerror(errno));
160 
161     status = krb5_sname_to_principal (context,
162 				      hostname,
163 				      service,
164 				      KRB5_NT_SRV_HST,
165 				      &server);
166     if (status)
167 	syslog_and_die("krb5_sname_to_principal: %s",
168 	      krb5_get_err_text(context, status));
169 
170     status = krb5_recvauth (context,
171 			    &auth_context,
172 			    &sock,
173 			    VERSION,
174 			    server,
175 			    0,
176 			    NULL,
177 			    &ticket);
178     if (status)
179 	syslog_and_die("krb5_recvauth: %s",
180 	      krb5_get_err_text(context, status));
181 
182     status = krb5_unparse_name (context,
183 				ticket->client,
184 				&name);
185     if (status)
186 	syslog_and_die("krb5_unparse_name: %s",
187 	      krb5_get_err_text(context, status));
188 
189     status=krb5_read_message (context, &sock, &remotename);
190     if (status) {
191 	syslog_and_die("krb5_read_message: %s",
192 		       krb5_get_err_text(context, status));
193     }
194     status=krb5_read_message (context, &sock, &tk_file);
195     if (status) {
196 	syslog_and_die("krb5_read_message: %s",
197 		       krb5_get_err_text(context, status));
198     }
199 
200     krb5_data_zero (&data);
201     krb5_data_zero (&packet);
202 
203     n = krb5_net_read (context, &sock, &net_len, 4);
204     if (n < 0)
205         syslog_and_die("krb5_net_read: %s", strerror(errno));
206     if (n == 0)
207         syslog_and_die("EOF in krb5_net_read");
208 
209     len = ntohl(net_len);
210     krb5_data_alloc (&packet, len);
211     n = krb5_net_read (context, &sock, packet.data, len);
212     if (n < 0)
213         syslog_and_die("krb5_net_read: %s", strerror(errno));
214     if (n == 0)
215         syslog_and_die("EOF in krb5_net_read");
216 
217     status = krb5_rd_priv (context,
218                            auth_context,
219                            &packet,
220                            &data,
221                            NULL);
222     if (status) {
223 	syslog_and_cont("krb5_rd_priv: %s",
224 			krb5_get_err_text(context, status));
225 	goto out;
226     }
227 
228     pwd = getpwnam ((char *)(remotename.data));
229     if (pwd == NULL) {
230 	status=1;
231 	syslog_and_cont("getpwnam: %s failed",(char *)(remotename.data));
232 	goto out;
233     }
234 
235     if(!krb5_kuserok (context,
236                      ticket->client,
237                      (char *)(remotename.data))) {
238 	status=1;
239 	syslog_and_cont("krb5_kuserok: permission denied");
240 	goto out;
241     }
242 
243     if (setgid(pwd->pw_gid) < 0) {
244 	syslog_and_cont ("setgid: %s", strerror(errno));
245 	goto out;
246     }
247     if (setuid(pwd->pw_uid) < 0) {
248 	syslog_and_cont ("setuid: %s", strerror(errno));
249 	goto out;
250     }
251 
252     if (tk_file.length != 1)
253 	snprintf (ccname, sizeof(ccname), "%s", (char *)(tk_file.data));
254     else
255 	snprintf (ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%u",pwd->pw_uid);
256 
257     status = krb5_cc_resolve (context, ccname, &ccache);
258     if (status) {
259 	syslog_and_cont("krb5_cc_resolve: %s",
260 			krb5_get_err_text(context, status));
261         goto out;
262     }
263     status = krb5_cc_initialize (context, ccache, ticket->client);
264     if (status) {
265 	syslog_and_cont("krb5_cc_initialize: %s",
266 			krb5_get_err_text(context, status));
267         goto out;
268     }
269     status = krb5_rd_cred2 (context, auth_context, ccache, &data);
270     krb5_cc_close (context, ccache);
271     if (status) {
272 	syslog_and_cont("krb5_rd_cred: %s",
273 			krb5_get_err_text(context, status));
274         goto out;
275 
276     }
277     strlcpy(krb5_tkfile,ccname,sizeof(krb5_tkfile));
278     syslog_and_cont("%s forwarded ticket to %s,%s",
279 		    name,
280 		    (char *)(remotename.data),ccname);
281 out:
282     if (status) {
283 	strcpy(ret_string, "no");
284 	syslog_and_cont("failed");
285     } else  {
286 	strcpy(ret_string, "ok");
287     }
288 
289     krb5_data_free (&tk_file);
290     krb5_data_free (&remotename);
291     krb5_data_free (&packet);
292     krb5_data_free (&data);
293     free(name);
294 
295     len = strlen(ret_string) + 1;
296     net_len = htonl(len);
297     if (krb5_net_write (context, &sock, &net_len, 4) != 4)
298          return 1;
299     if (krb5_net_write (context, &sock, ret_string, len) != len)
300          return 1;
301     return status;
302 }
303 
304 static int
305 doit (int port, const char *service)
306 {
307     if (do_inetd)
308 	mini_inetd(port);
309     return proto (STDIN_FILENO, service);
310 }
311 
312 int
313 main(int argc, char **argv)
314 {
315     int port;
316     int ret;
317 
318     setprogname (argv[0]);
319     roken_openlog (argv[0], LOG_ODELAY | LOG_PID,LOG_AUTH);
320     port = server_setup(&context, argc, argv);
321     ret = doit (port, service);
322     closelog();
323     if (ret == 0 && regpag_str != NULL)
324         ret = execl(regpag_str, "regpag", "-t", krb5_tkfile, "-r", NULL);
325     return ret;
326 }
327