1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
4 *
5 */
6
7 /*
8 * Copyright (C) 1998 by the FundsXpress, INC.
9 *
10 * All rights reserved.
11 *
12 * Export of this software from the United States of America may require
13 * a specific license from the United States Government. It is the
14 * responsibility of any person or organization contemplating export to
15 * obtain such a license before exporting.
16 *
17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
18 * distribute this software and its documentation for any purpose and
19 * without fee is hereby granted, provided that the above copyright
20 * notice appear in all copies and that both that copyright notice and
21 * this permission notice appear in supporting documentation, and that
22 * the name of FundsXpress. not be used in advertising or publicity pertaining
23 * to distribution of the software without specific, written prior
24 * permission. FundsXpress makes no representations about the suitability of
25 * this software for any purpose. It is provided "as is" without express
26 * or implied warranty.
27 *
28 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31 */
32
33 #include <k5-platform.h>
34 #include <errno.h>
35 #include <locale.h>
36 #include <stdio.h>
37 #include <signal.h>
38 #include <syslog.h>
39 #include <sys/types.h>
40 #ifdef _AIX
41 #include <sys/select.h>
42 #endif
43 #include <sys/time.h>
44 #include <sys/socket.h>
45 #include <unistd.h>
46 #include <netinet/in.h>
47 #include <netdb.h>
48 #include <gssrpc/rpc.h>
49 #include <gssapi/gssapi.h>
50 #include "gssapiP_krb5.h" /* for kg_get_context */
51 #include <gssrpc/auth_gssapi.h>
52 #include <kadm5/admin.h>
53 #include <kadm5/kadm_rpc.h>
54 #include <adm_proto.h>
55 #include "kdb_kt.h" /* for krb5_ktkdb_set_context */
56 #include <string.h>
57 #include <kdb_log.h>
58
59 #include "misc.h"
60 #include "auth.h"
61
62 #if defined(NEED_DAEMON_PROTO)
63 int daemon(int, int);
64 #endif
65
66 #define TIMEOUT 15
67
68 gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL;
69 void *global_server_handle;
70 int nofork = 0;
71 char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL;
72 char *kprop = KPROPD_DEFAULT_KPROP;
73 char *dump_file = KPROP_DEFAULT_FILE;
74 char *kprop_port = NULL;
75
76 static krb5_context context;
77 static char *progname;
78
79 static void
usage(void)80 usage(void)
81 {
82 fprintf(stderr, _("Usage: kadmind [-x db_args]* [-r realm] [-m] [-nofork] "
83 "[-port port-number]\n"
84 "\t\t[-proponly] [-p path-to-kdb5_util] [-F dump-file]\n"
85 "\t\t[-K path-to-kprop] [-k kprop-port] [-P pid_file]\n"
86 "\nwhere,\n\t[-x db_args]* - any number of database "
87 "specific arguments.\n"
88 "\t\t\tLook at each database documentation for "
89 "supported arguments\n"));
90 exit(1);
91 }
92
93 /*
94 * Output a message to stderr and the admin server log, and exit with status 1.
95 * msg should not be punctuated. If code is given, msg should indicate what
96 * operation was taking place in the present progressive. Otherwise msg should
97 * be capitalized and should indicate what went wrong.
98 */
99 static void
fail_to_start(krb5_error_code code,const char * msg)100 fail_to_start(krb5_error_code code, const char *msg)
101 {
102 const char *errmsg;
103
104 if (code) {
105 errmsg = krb5_get_error_message(context, code);
106 fprintf(stderr, _("%s: %s while %s, aborting\n"), progname, errmsg,
107 msg);
108 krb5_klog_syslog(LOG_ERR, _("%s while %s, aborting\n"), errmsg, msg);
109 } else {
110 fprintf(stderr, _("%s: %s, aborting\n"), progname, msg);
111 krb5_klog_syslog(LOG_ERR, _("%s, aborting"), msg);
112 }
113 exit(1);
114 }
115
116 static int
write_pid_file(const char * pid_file)117 write_pid_file(const char *pid_file)
118 {
119 FILE *file;
120 unsigned long pid;
121 int st1, st2;
122
123 file = fopen(pid_file, "w");
124 if (file == NULL)
125 return errno;
126 pid = (unsigned long)getpid();
127 st1 = (fprintf(file, "%ld\n", pid) < 0) ? errno : 0;
128 st2 = (fclose(file) == EOF) ? errno : 0;
129 return st1 ? st1 : st2;
130 }
131
132 /* Set up the main loop. If proponly is set, don't set up ports for kpasswd or
133 * kadmin. May set *ctx_out even on error. */
134 static krb5_error_code
setup_loop(kadm5_config_params * params,int proponly,verto_ctx ** ctx_out)135 setup_loop(kadm5_config_params *params, int proponly, verto_ctx **ctx_out)
136 {
137 krb5_error_code ret;
138 verto_ctx *ctx;
139
140 *ctx_out = ctx = loop_init(VERTO_EV_TYPE_SIGNAL);
141 if (ctx == NULL)
142 return ENOMEM;
143 ret = loop_setup_signals(ctx, &global_server_handle, NULL);
144 if (ret)
145 return ret;
146 if (!proponly) {
147 ret = loop_add_udp_address(params->kpasswd_port,
148 params->kpasswd_listen);
149 if (ret)
150 return ret;
151 ret = loop_add_tcp_address(params->kpasswd_port,
152 params->kpasswd_listen);
153 if (ret)
154 return ret;
155 ret = loop_add_rpc_service(params->kadmind_port,
156 params->kadmind_listen,
157 KADM, KADMVERS, kadm_1);
158 if (ret)
159 return ret;
160 ret = loop_add_unix_socket(params->kpasswd_listen);
161 if (ret)
162 return ret;
163 }
164 #ifndef DISABLE_IPROP
165 if (params->iprop_enabled) {
166 ret = loop_add_rpc_service(params->iprop_port, params->iprop_listen,
167 KRB5_IPROP_PROG, KRB5_IPROP_VERS,
168 krb5_iprop_prog_1);
169 if (ret)
170 return ret;
171 }
172 #endif
173 return loop_setup_network(ctx, &global_server_handle, progname,
174 DEFAULT_TCP_LISTEN_BACKLOG);
175 }
176
177 /* Point GSSAPI at the KDB keytab so we don't need an actual file keytab. */
178 static krb5_error_code
setup_kdb_keytab(void)179 setup_kdb_keytab(void)
180 {
181 krb5_error_code ret;
182
183 ret = krb5_ktkdb_set_context(context);
184 if (ret)
185 return ret;
186 ret = krb5_db_register_keytab(context);
187 if (ret)
188 return ret;
189 return krb5_gss_register_acceptor_identity("KDB:");
190 }
191
192
193 /* Return "name@realm". */
194 static char *
build_princ_name(char * name,char * realm)195 build_princ_name(char *name, char *realm)
196 {
197 char *fullname;
198
199 if (asprintf(&fullname, "%s@%s", name, realm) < 0)
200 return NULL;
201 return fullname;
202 }
203
204 /* Callback from GSSRPC for garbled/forged/replayed/etc messages. */
205 static void
log_badverf(gss_name_t client_name,gss_name_t server_name,struct svc_req * rqst,struct rpc_msg * msg,char * data)206 log_badverf(gss_name_t client_name, gss_name_t server_name,
207 struct svc_req *rqst, struct rpc_msg *msg, char *data)
208 {
209 static const struct {
210 rpcproc_t proc;
211 const char *proc_name;
212 } proc_names[] = {
213 {1, "CREATE_PRINCIPAL"},
214 {2, "DELETE_PRINCIPAL"},
215 {3, "MODIFY_PRINCIPAL"},
216 {4, "RENAME_PRINCIPAL"},
217 {5, "GET_PRINCIPAL"},
218 {6, "CHPASS_PRINCIPAL"},
219 {7, "CHRAND_PRINCIPAL"},
220 {8, "CREATE_POLICY"},
221 {9, "DELETE_POLICY"},
222 {10, "MODIFY_POLICY"},
223 {11, "GET_POLICY"},
224 {12, "GET_PRIVS"},
225 {13, "INIT"},
226 {14, "GET_PRINCS"},
227 {15, "GET_POLS"},
228 {16, "SETKEY_PRINCIPAL"},
229 /* 17 was "SETV4KEY_PRINCIPAL" */
230 {18, "CREATE_PRINCIPAL3"},
231 {19, "CHPASS_PRINCIPAL3"},
232 {20, "CHRAND_PRINCIPAL3"},
233 {21, "SETKEY_PRINCIPAL3"},
234 {22, "PURGEKEYS"},
235 {23, "GET_STRINGS"},
236 {24, "SET_STRING"}
237 };
238 OM_uint32 minor;
239 gss_buffer_desc client, server;
240 gss_OID gss_type;
241 const char *a, *cname, *sname;
242 rpcproc_t proc;
243 unsigned int i;
244 const char *procname;
245 size_t clen, slen;
246 char *cdots, *sdots;
247
248 client.length = 0;
249 client.value = NULL;
250 server.length = 0;
251 server.value = NULL;
252
253 (void)gss_display_name(&minor, client_name, &client, &gss_type);
254 (void)gss_display_name(&minor, server_name, &server, &gss_type);
255 cname = (client.value == NULL) ? "(null)" : client.value;
256 clen = (client.value == NULL) ? sizeof("(null)") - 1 : client.length;
257 trunc_name(&clen, &cdots);
258 sname = (server.value == NULL) ? "(null)" : server.value;
259 slen = (server.value == NULL) ? sizeof("(null)") - 1 : server.length;
260 trunc_name(&slen, &sdots);
261 a = client_addr(rqst->rq_xprt);
262
263 proc = msg->rm_call.cb_proc;
264 procname = NULL;
265 for (i = 0; i < sizeof(proc_names) / sizeof(*proc_names); i++) {
266 if (proc_names[i].proc == proc) {
267 procname = proc_names[i].proc_name;
268 break;
269 }
270 }
271 if (procname != NULL) {
272 krb5_klog_syslog(LOG_NOTICE,
273 _("WARNING! Forged/garbled request: %s, claimed "
274 "client = %.*s%s, server = %.*s%s, addr = %s"),
275 procname, (int)clen, cname, cdots, (int)slen, sname,
276 sdots, a);
277 } else {
278 krb5_klog_syslog(LOG_NOTICE,
279 _("WARNING! Forged/garbled request: %d, claimed "
280 "client = %.*s%s, server = %.*s%s, addr = %s"),
281 proc, (int)clen, cname, cdots, (int)slen, sname,
282 sdots, a);
283 }
284
285 (void)gss_release_buffer(&minor, &client);
286 (void)gss_release_buffer(&minor, &server);
287 }
288
289 /* Callback from GSSRPC for miscellaneous errors */
290 static void
log_miscerr(struct svc_req * rqst,struct rpc_msg * msg,char * error,char * data)291 log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char *error, char *data)
292 {
293 krb5_klog_syslog(LOG_NOTICE, _("Miscellaneous RPC error: %s, %s"),
294 client_addr(rqst->rq_xprt), error);
295 }
296
297 static void
log_badauth_display_status_1(char * m,OM_uint32 code,int type)298 log_badauth_display_status_1(char *m, OM_uint32 code, int type)
299 {
300 OM_uint32 gssstat, minor_stat;
301 gss_buffer_desc msg;
302 OM_uint32 msg_ctx;
303
304 msg_ctx = 0;
305 while (1) {
306 gssstat = gss_display_status(&minor_stat, code, type, GSS_C_NULL_OID,
307 &msg_ctx, &msg);
308 if (gssstat != GSS_S_COMPLETE) {
309 krb5_klog_syslog(LOG_ERR, _("%s Cannot decode status %d"), m,
310 (int)code);
311 return;
312 }
313
314 krb5_klog_syslog(LOG_NOTICE, "%s %.*s", m, (int)msg.length,
315 (char *)msg.value);
316 (void)gss_release_buffer(&minor_stat, &msg);
317
318 if (!msg_ctx)
319 break;
320 }
321 }
322
323 /* Callback from GSSRPC for authentication failures */
324 void
log_badauth(OM_uint32 major,OM_uint32 minor,SVCXPRT * xprt,char * data)325 log_badauth(OM_uint32 major, OM_uint32 minor, SVCXPRT *xprt, char *data)
326 {
327 krb5_klog_syslog(LOG_NOTICE, _("Authentication attempt failed: %s, "
328 "GSS-API error strings are:"),
329 client_addr(xprt));
330 log_badauth_display_status_1(" ", major, GSS_C_GSS_CODE);
331 log_badauth_display_status_1(" ", minor, GSS_C_MECH_CODE);
332 krb5_klog_syslog(LOG_NOTICE, _(" GSS-API error strings complete."));
333 }
334
335 int
main(int argc,char * argv[])336 main(int argc, char *argv[])
337 {
338 OM_uint32 minor_status;
339 gss_buffer_desc in_buf;
340 gss_OID nt_krb5_name_oid = (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME;
341 auth_gssapi_name names[4];
342 kadm5_config_params params;
343 verto_ctx *vctx;
344 const char *pid_file = NULL;
345 char **db_args = NULL, **tmpargs;
346 const char *acl_file;
347 size_t db_args_size = 0;
348 int ret, i, proponly = 0;
349
350 setlocale(LC_ALL, "");
351 setvbuf(stderr, NULL, _IONBF, 0);
352
353 names[0].name = names[1].name = names[2].name = names[3].name = NULL;
354 names[0].type = names[1].type = names[2].type = names[3].type =
355 nt_krb5_name_oid;
356
357 progname = (strrchr(argv[0], '/') != NULL) ? strrchr(argv[0], '/') + 1 :
358 argv[0];
359
360 memset(¶ms, 0, sizeof(params));
361
362 argc--, argv++;
363 while (argc) {
364 if (strcmp(*argv, "-x") == 0) {
365 argc--, argv++;
366 if (!argc)
367 usage();
368 db_args_size++;
369 tmpargs = realloc(db_args, sizeof(char *) * (db_args_size + 1));
370 if (tmpargs == NULL) {
371 fprintf(stderr, _("%s: cannot initialize. Not enough "
372 "memory\n"), progname);
373 exit(1);
374 }
375 db_args = tmpargs;
376 db_args[db_args_size - 1] = *argv;
377 db_args[db_args_size] = NULL;
378 } else if (strcmp(*argv, "-r") == 0) {
379 argc--, argv++;
380 if (!argc)
381 usage();
382 params.realm = *argv;
383 params.mask |= KADM5_CONFIG_REALM;
384 argc--, argv++;
385 continue;
386 } else if (strcmp(*argv, "-m") == 0) {
387 params.mkey_from_kbd = 1;
388 params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
389 } else if (strcmp(*argv, "-nofork") == 0) {
390 nofork = 1;
391 #ifndef DISABLE_IPROP
392 } else if (strcmp(*argv, "-proponly") == 0) {
393 proponly = 1;
394 #endif
395 } else if (strcmp(*argv, "-port") == 0) {
396 argc--, argv++;
397 if (!argc)
398 usage();
399 params.kadmind_port = atoi(*argv);
400 params.mask |= KADM5_CONFIG_KADMIND_PORT;
401 } else if (strcmp(*argv, "-P") == 0) {
402 argc--, argv++;
403 if (!argc)
404 usage();
405 pid_file = *argv;
406 } else if (strcmp(*argv, "-W") == 0) {
407 /* Ignore (deprecated weak random option). */
408 } else if (strcmp(*argv, "-p") == 0) {
409 argc--, argv++;
410 if (!argc)
411 usage();
412 kdb5_util = *argv;
413 } else if (strcmp(*argv, "-F") == 0) {
414 argc--, argv++;
415 if (!argc)
416 usage();
417 dump_file = *argv;
418 } else if (strcmp(*argv, "-K") == 0) {
419 argc--, argv++;
420 if (!argc)
421 usage();
422 kprop = *argv;
423 } else if (strcmp(*argv, "-k") == 0) {
424 argc--, argv++;
425 if (!argc)
426 usage();
427 kprop_port = *argv;
428 } else {
429 break;
430 }
431 argc--, argv++;
432 }
433
434 if (argc != 0)
435 usage();
436
437 ret = kadm5_init_krb5_context(&context);
438 if (ret) {
439 fprintf(stderr, _("%s: %s while initializing context, aborting\n"),
440 progname, error_message(ret));
441 exit(1);
442 }
443
444 krb5_klog_init(context, "admin_server", progname, 1);
445
446 ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms,
447 KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args,
448 &global_server_handle);
449 if (ret)
450 fail_to_start(ret, _("initializing"));
451
452 ret = kadm5_get_config_params(context, 1, ¶ms, ¶ms);
453 if (ret)
454 fail_to_start(ret, _("getting config parameters"));
455 if (!(params.mask & KADM5_CONFIG_REALM))
456 fail_to_start(0, _("Missing required realm configuration"));
457 if (!(params.mask & KADM5_CONFIG_ACL_FILE))
458 fail_to_start(0, _("Missing required ACL file configuration"));
459 if (proponly && !params.iprop_enabled) {
460 fail_to_start(0, _("-proponly can only be used when "
461 "iprop_enable is true"));
462 }
463
464 ret = setup_loop(¶ms, proponly, &vctx);
465 if (ret)
466 fail_to_start(ret, _("initializing network"));
467
468 names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm);
469 names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm);
470 if (names[0].name == NULL || names[1].name == NULL)
471 fail_to_start(0, _("Cannot build GSSAPI auth names"));
472
473 ret = setup_kdb_keytab();
474 if (ret)
475 fail_to_start(0, _("Cannot set up KDB keytab"));
476
477 if (svcauth_gssapi_set_names(names, 2) == FALSE)
478 fail_to_start(0, _("Cannot set GSSAPI authentication names"));
479
480 /* if set_names succeeded, this will too */
481 in_buf.value = names[1].name;
482 in_buf.length = strlen(names[1].name) + 1;
483 (void)gss_import_name(&minor_status, &in_buf, nt_krb5_name_oid,
484 &gss_changepw_name);
485
486 svcauth_gssapi_set_log_badauth2_func(log_badauth, NULL);
487 svcauth_gssapi_set_log_badverf_func(log_badverf, NULL);
488 svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL);
489
490 svcauth_gss_set_log_badauth2_func(log_badauth, NULL);
491 svcauth_gss_set_log_badverf_func(log_badverf, NULL);
492 svcauth_gss_set_log_miscerr_func(log_miscerr, NULL);
493
494 if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE)
495 fail_to_start(0, _("Cannot initialize GSSAPI service name"));
496
497 acl_file = (*params.acl_file != '\0') ? params.acl_file : NULL;
498 ret = auth_init(context, acl_file);
499 if (ret)
500 fail_to_start(ret, _("initializing ACL file"));
501
502 /* Since some KDB modules are not fork-safe, we must reinitialize the
503 * server handle after daemonizing. */
504 kadm5_destroy(global_server_handle);
505 global_server_handle = NULL;
506
507 if (!nofork && daemon(0, 0) != 0)
508 fail_to_start(errno, _("spawning daemon process"));
509 if (pid_file != NULL) {
510 ret = write_pid_file(pid_file);
511 if (ret)
512 fail_to_start(ret, _("creating PID file"));
513 }
514
515 ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms,
516 KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args,
517 &global_server_handle);
518 if (ret)
519 fail_to_start(ret, _("initializing"));
520
521 if (params.iprop_enabled == TRUE) {
522 ulog_set_role(context, IPROP_PRIMARY);
523
524 ret = ulog_map(context, params.iprop_logfile, params.iprop_ulogsize);
525 if (ret)
526 fail_to_start(ret, _("mapping update log"));
527
528 if (nofork) {
529 fprintf(stderr,
530 _("%s: create IPROP svc (PROG=%d, VERS=%d)\n"),
531 progname, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
532 }
533 }
534
535 if (kprop_port == NULL)
536 kprop_port = getenv("KPROP_PORT");
537
538 krb5_klog_syslog(LOG_INFO, _("starting"));
539 if (nofork)
540 fprintf(stderr, _("%s: starting...\n"), progname);
541
542 verto_run(vctx);
543 krb5_klog_syslog(LOG_INFO, _("finished, exiting"));
544
545 /* Clean up memory, etc */
546 svcauth_gssapi_unset_names();
547 kadm5_destroy(global_server_handle);
548 loop_free(vctx);
549 auth_fini(context);
550 (void)gss_release_name(&minor_status, &gss_changepw_name);
551 (void)gss_release_name(&minor_status, &gss_oldchangepw_name);
552 for (i = 0; i < 4; i++)
553 free(names[i].name);
554
555 krb5_klog_close(context);
556 krb5_free_context(context);
557 exit(0);
558 }
559