Imported Upstream version 1.17
[platform/upstream/krb5.git] / src / kadmin / server / ovsec_kadmd.c
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 #ifdef USE_PASSWORD_SERVER
80 void kadm5_set_use_password_server(void);
81 #endif
82
83 static void
84 usage()
85 {
86     fprintf(stderr, _("Usage: kadmind [-x db_args]* [-r realm] [-m] [-nofork] "
87                       "[-port port-number]\n"
88                       "\t\t[-proponly] [-p path-to-kdb5_util] [-F dump-file]\n"
89                       "\t\t[-K path-to-kprop] [-k kprop-port] [-P pid_file]\n"
90                       "\nwhere,\n\t[-x db_args]* - any number of database "
91                       "specific arguments.\n"
92                       "\t\t\tLook at each database documentation for "
93                       "supported arguments\n"));
94     exit(1);
95 }
96
97 /*
98  * Output a message to stderr and the admin server log, and exit with status 1.
99  * msg should not be punctuated.  If code is given, msg should indicate what
100  * operation was taking place in the present progressive.  Otherwise msg should
101  * be capitalized and should indicate what went wrong.
102  */
103 static void
104 fail_to_start(krb5_error_code code, const char *msg)
105 {
106     const char *errmsg;
107
108     if (code) {
109         errmsg = krb5_get_error_message(context, code);
110         fprintf(stderr, _("%s: %s while %s, aborting\n"), progname, errmsg,
111                 msg);
112         krb5_klog_syslog(LOG_ERR, _("%s while %s, aborting\n"), errmsg, msg);
113     } else {
114         fprintf(stderr, _("%s: %s, aborting\n"), progname, msg);
115         krb5_klog_syslog(LOG_ERR, _("%s, aborting"), msg);
116     }
117     exit(1);
118 }
119
120 static int
121 write_pid_file(const char *pid_file)
122 {
123     FILE *file;
124     unsigned long pid;
125     int st1, st2;
126
127     file = fopen(pid_file, "w");
128     if (file == NULL)
129         return errno;
130     pid = (unsigned long)getpid();
131     st1 = (fprintf(file, "%ld\n", pid) < 0) ? errno : 0;
132     st2 = (fclose(file) == EOF) ? errno : 0;
133     return st1 ? st1 : st2;
134 }
135
136 /* Set up the main loop.  If proponly is set, don't set up ports for kpasswd or
137  * kadmin.  May set *ctx_out even on error. */
138 static krb5_error_code
139 setup_loop(kadm5_config_params *params, int proponly, verto_ctx **ctx_out)
140 {
141     krb5_error_code ret;
142     verto_ctx *ctx;
143
144     *ctx_out = ctx = loop_init(VERTO_EV_TYPE_SIGNAL);
145     if (ctx == NULL)
146         return ENOMEM;
147     ret = loop_setup_signals(ctx, global_server_handle, NULL);
148     if (ret)
149         return ret;
150     if (!proponly) {
151         ret = loop_add_udp_address(params->kpasswd_port,
152                                    params->kpasswd_listen);
153         if (ret)
154             return ret;
155         ret = loop_add_tcp_address(params->kpasswd_port,
156                                    params->kpasswd_listen);
157         if (ret)
158             return ret;
159         ret = loop_add_rpc_service(params->kadmind_port,
160                                    params->kadmind_listen,
161                                    KADM, KADMVERS, kadm_1);
162         if (ret)
163             return ret;
164     }
165 #ifndef DISABLE_IPROP
166     if (params->iprop_enabled) {
167         ret = loop_add_rpc_service(params->iprop_port, params->iprop_listen,
168                                    KRB5_IPROP_PROG, KRB5_IPROP_VERS,
169                                    krb5_iprop_prog_1);
170         if (ret)
171             return ret;
172     }
173 #endif
174     return loop_setup_network(ctx, global_server_handle, progname,
175                               DEFAULT_TCP_LISTEN_BACKLOG);
176 }
177
178 /* Point GSSAPI at the KDB keytab so we don't need an actual file keytab. */
179 static krb5_error_code
180 setup_kdb_keytab()
181 {
182     krb5_error_code ret;
183
184     ret = krb5_ktkdb_set_context(context);
185     if (ret)
186         return ret;
187     ret = krb5_db_register_keytab(context);
188     if (ret)
189         return ret;
190     return krb5_gss_register_acceptor_identity("KDB:");
191 }
192
193
194 /* Return "name@realm". */
195 static char *
196 build_princ_name(char *name, char *realm)
197 {
198     char *fullname;
199
200     if (asprintf(&fullname, "%s@%s", name, realm) < 0)
201         return NULL;
202     return fullname;
203 }
204
205 /* Callback from GSSRPC for garbled/forged/replayed/etc messages. */
206 static void
207 log_badverf(gss_name_t client_name, gss_name_t server_name,
208             struct svc_req *rqst, struct rpc_msg *msg, char *data)
209 {
210     static const struct {
211         rpcproc_t proc;
212         const char *proc_name;
213     } proc_names[] = {
214         {1, "CREATE_PRINCIPAL"},
215         {2, "DELETE_PRINCIPAL"},
216         {3, "MODIFY_PRINCIPAL"},
217         {4, "RENAME_PRINCIPAL"},
218         {5, "GET_PRINCIPAL"},
219         {6, "CHPASS_PRINCIPAL"},
220         {7, "CHRAND_PRINCIPAL"},
221         {8, "CREATE_POLICY"},
222         {9, "DELETE_POLICY"},
223         {10, "MODIFY_POLICY"},
224         {11, "GET_POLICY"},
225         {12, "GET_PRIVS"},
226         {13, "INIT"},
227         {14, "GET_PRINCS"},
228         {15, "GET_POLS"},
229         {16, "SETKEY_PRINCIPAL"},
230         {17, "SETV4KEY_PRINCIPAL"},
231         {18, "CREATE_PRINCIPAL3"},
232         {19, "CHPASS_PRINCIPAL3"},
233         {20, "CHRAND_PRINCIPAL3"},
234         {21, "SETKEY_PRINCIPAL3"},
235         {22, "PURGEKEYS"},
236         {23, "GET_STRINGS"},
237         {24, "SET_STRING"}
238     };
239     OM_uint32 minor;
240     gss_buffer_desc client, server;
241     gss_OID gss_type;
242     const char *a;
243     rpcproc_t proc;
244     unsigned int i;
245     const char *procname;
246     size_t clen, slen;
247     char *cdots, *sdots;
248
249     client.length = 0;
250     client.value = NULL;
251     server.length = 0;
252     server.value = NULL;
253
254     (void)gss_display_name(&minor, client_name, &client, &gss_type);
255     (void)gss_display_name(&minor, server_name, &server, &gss_type);
256     if (client.value == NULL) {
257         client.value = "(null)";
258         clen = sizeof("(null)") - 1;
259     } else {
260         clen = client.length;
261     }
262     trunc_name(&clen, &cdots);
263     if (server.value == NULL) {
264         server.value = "(null)";
265         slen = sizeof("(null)") - 1;
266     } else {
267         slen = server.length;
268     }
269     trunc_name(&slen, &sdots);
270     a = client_addr(rqst->rq_xprt);
271
272     proc = msg->rm_call.cb_proc;
273     procname = NULL;
274     for (i = 0; i < sizeof(proc_names) / sizeof(*proc_names); i++) {
275         if (proc_names[i].proc == proc) {
276             procname = proc_names[i].proc_name;
277             break;
278         }
279     }
280     if (procname != NULL) {
281         krb5_klog_syslog(LOG_NOTICE,
282                          _("WARNING! Forged/garbled request: %s, claimed "
283                            "client = %.*s%s, server = %.*s%s, addr = %s"),
284                          procname, (int)clen, (char *)client.value, cdots,
285                          (int)slen, (char *)server.value, sdots, a);
286     } else {
287         krb5_klog_syslog(LOG_NOTICE,
288                          _("WARNING! Forged/garbled request: %d, claimed "
289                            "client = %.*s%s, server = %.*s%s, addr = %s"),
290                          proc, (int)clen, (char *)client.value, cdots,
291                          (int)slen, (char *)server.value, sdots, a);
292     }
293
294     (void)gss_release_buffer(&minor, &client);
295     (void)gss_release_buffer(&minor, &server);
296 }
297
298 /* Callback from GSSRPC for miscellaneous errors */
299 static void
300 log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char *error, char *data)
301 {
302     krb5_klog_syslog(LOG_NOTICE, _("Miscellaneous RPC error: %s, %s"),
303                      client_addr(rqst->rq_xprt), error);
304 }
305
306 static void
307 log_badauth_display_status_1(char *m, OM_uint32 code, int type)
308 {
309     OM_uint32 gssstat, minor_stat;
310     gss_buffer_desc msg;
311     OM_uint32 msg_ctx;
312
313     msg_ctx = 0;
314     while (1) {
315         gssstat = gss_display_status(&minor_stat, code, type, GSS_C_NULL_OID,
316                                      &msg_ctx, &msg);
317         if (gssstat != GSS_S_COMPLETE) {
318             krb5_klog_syslog(LOG_ERR, _("%s Cannot decode status %d"), m,
319                              (int)code);
320             return;
321         }
322
323         krb5_klog_syslog(LOG_NOTICE, "%s %.*s", m, (int)msg.length,
324                          (char *)msg.value);
325         (void)gss_release_buffer(&minor_stat, &msg);
326
327         if (!msg_ctx)
328             break;
329     }
330 }
331
332 /* Callback from GSSRPC for authentication failures */
333 void
334 log_badauth(OM_uint32 major, OM_uint32 minor, SVCXPRT *xprt, char *data)
335 {
336     krb5_klog_syslog(LOG_NOTICE, _("Authentication attempt failed: %s, "
337                                    "GSS-API error strings are:"),
338                      client_addr(xprt));
339     log_badauth_display_status_1("   ", major, GSS_C_GSS_CODE);
340     log_badauth_display_status_1("   ", minor, GSS_C_MECH_CODE);
341     krb5_klog_syslog(LOG_NOTICE, _("   GSS-API error strings complete."));
342 }
343
344 int
345 main(int argc, char *argv[])
346 {
347     OM_uint32 minor_status;
348     gss_buffer_desc in_buf;
349     gss_OID nt_krb5_name_oid = (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME;
350     auth_gssapi_name names[4];
351     kadm5_config_params params;
352     verto_ctx *vctx;
353     const char *pid_file = NULL;
354     char **db_args = NULL, **tmpargs;
355     const char *acl_file;
356     int ret, i, db_args_size = 0, strong_random = 1, proponly = 0;
357
358     setlocale(LC_ALL, "");
359     setvbuf(stderr, NULL, _IONBF, 0);
360
361     names[0].name = names[1].name = names[2].name = names[3].name = NULL;
362     names[0].type = names[1].type = names[2].type = names[3].type =
363         nt_krb5_name_oid;
364
365     progname = (strrchr(argv[0], '/') != NULL) ? strrchr(argv[0], '/') + 1 :
366         argv[0];
367
368     memset(&params, 0, sizeof(params));
369
370     argc--, argv++;
371     while (argc) {
372         if (strcmp(*argv, "-x") == 0) {
373             argc--, argv++;
374             if (!argc)
375                 usage();
376             db_args_size++;
377             tmpargs = realloc(db_args, sizeof(char *) * (db_args_size + 1));
378             if (tmpargs == NULL) {
379                 fprintf(stderr, _("%s: cannot initialize. Not enough "
380                                   "memory\n"), progname);
381                 exit(1);
382             }
383             db_args = tmpargs;
384             db_args[db_args_size - 1] = *argv;
385             db_args[db_args_size] = NULL;
386         } else if (strcmp(*argv, "-r") == 0) {
387             argc--, argv++;
388             if (!argc)
389                 usage();
390             params.realm = *argv;
391             params.mask |= KADM5_CONFIG_REALM;
392             argc--, argv++;
393             continue;
394         } else if (strcmp(*argv, "-m") == 0) {
395             params.mkey_from_kbd = 1;
396             params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
397         } else if (strcmp(*argv, "-nofork") == 0) {
398             nofork = 1;
399 #ifdef USE_PASSWORD_SERVER
400         } else if (strcmp(*argv, "-passwordserver") == 0) {
401             kadm5_set_use_password_server();
402 #endif
403 #ifndef DISABLE_IPROP
404         } else if (strcmp(*argv, "-proponly") == 0) {
405             proponly = 1;
406 #endif
407         } else if (strcmp(*argv, "-port") == 0) {
408             argc--, argv++;
409             if (!argc)
410                 usage();
411             params.kadmind_port = atoi(*argv);
412             params.mask |= KADM5_CONFIG_KADMIND_PORT;
413         } else if (strcmp(*argv, "-P") == 0) {
414             argc--, argv++;
415             if (!argc)
416                 usage();
417             pid_file = *argv;
418         } else if (strcmp(*argv, "-W") == 0) {
419             strong_random = 0;
420         } else if (strcmp(*argv, "-p") == 0) {
421             argc--, argv++;
422             if (!argc)
423                 usage();
424             kdb5_util = *argv;
425         } else if (strcmp(*argv, "-F") == 0) {
426             argc--, argv++;
427             if (!argc)
428                 usage();
429             dump_file = *argv;
430         } else if (strcmp(*argv, "-K") == 0) {
431             argc--, argv++;
432             if (!argc)
433                 usage();
434             kprop = *argv;
435         } else if (strcmp(*argv, "-k") == 0) {
436             argc--, argv++;
437             if (!argc)
438                 usage();
439             kprop_port = *argv;
440         } else {
441             break;
442         }
443         argc--, argv++;
444     }
445
446     if (argc != 0)
447         usage();
448
449     ret = kadm5_init_krb5_context(&context);
450     if (ret) {
451         fprintf(stderr, _("%s: %s while initializing context, aborting\n"),
452                 progname, error_message(ret));
453         exit(1);
454     }
455
456     krb5_klog_init(context, "admin_server", progname, 1);
457
458     ret = kadm5_init(context, "kadmind", NULL, NULL, &params,
459                      KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args,
460                      &global_server_handle);
461     if (ret)
462         fail_to_start(ret, _("initializing"));
463
464     ret = kadm5_get_config_params(context, 1, &params, &params);
465     if (ret)
466         fail_to_start(ret, _("getting config parameters"));
467     if (!(params.mask & KADM5_CONFIG_REALM))
468         fail_to_start(0, _("Missing required realm configuration"));
469     if (!(params.mask & KADM5_CONFIG_ACL_FILE))
470         fail_to_start(0, _("Missing required ACL file configuration"));
471     if (proponly && !params.iprop_enabled) {
472         fail_to_start(0, _("-proponly can only be used when "
473                            "iprop_enable is true"));
474     }
475
476     ret = setup_loop(&params, proponly, &vctx);
477     if (ret)
478         fail_to_start(ret, _("initializing network"));
479
480     names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm);
481     names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm);
482     if (names[0].name == NULL || names[1].name == NULL)
483         fail_to_start(0, _("Cannot build GSSAPI auth names"));
484
485     ret = setup_kdb_keytab();
486     if (ret)
487         fail_to_start(0, _("Cannot set up KDB keytab"));
488
489     if (svcauth_gssapi_set_names(names, 2) == FALSE)
490         fail_to_start(0, _("Cannot set GSSAPI authentication names"));
491
492     /* if set_names succeeded, this will too */
493     in_buf.value = names[1].name;
494     in_buf.length = strlen(names[1].name) + 1;
495     (void)gss_import_name(&minor_status, &in_buf, nt_krb5_name_oid,
496                           &gss_changepw_name);
497
498     svcauth_gssapi_set_log_badauth2_func(log_badauth, NULL);
499     svcauth_gssapi_set_log_badverf_func(log_badverf, NULL);
500     svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL);
501
502     svcauth_gss_set_log_badauth2_func(log_badauth, NULL);
503     svcauth_gss_set_log_badverf_func(log_badverf, NULL);
504     svcauth_gss_set_log_miscerr_func(log_miscerr, NULL);
505
506     if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE)
507         fail_to_start(0, _("Cannot initialize GSSAPI service name"));
508
509     acl_file = (*params.acl_file != '\0') ? params.acl_file : NULL;
510     ret = auth_init(context, acl_file);
511     if (ret)
512         fail_to_start(ret, _("initializing ACL file"));
513
514     if (!nofork && daemon(0, 0) != 0)
515         fail_to_start(errno, _("spawning daemon process"));
516     if (pid_file != NULL) {
517         ret = write_pid_file(pid_file);
518         if (ret)
519             fail_to_start(ret, _("creating PID file"));
520     }
521
522     krb5_klog_syslog(LOG_INFO, _("Seeding random number generator"));
523     ret = krb5_c_random_os_entropy(context, strong_random, NULL);
524     if (ret)
525         fail_to_start(ret, _("getting random seed"));
526
527     if (params.iprop_enabled == TRUE) {
528         ulog_set_role(context, IPROP_MASTER);
529
530         ret = ulog_map(context, params.iprop_logfile, params.iprop_ulogsize);
531         if (ret)
532             fail_to_start(ret, _("mapping update log"));
533
534         if (nofork) {
535             fprintf(stderr,
536                     _("%s: create IPROP svc (PROG=%d, VERS=%d)\n"),
537                     progname, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
538         }
539     }
540
541     if (kprop_port == NULL)
542         kprop_port = getenv("KPROP_PORT");
543
544     krb5_klog_syslog(LOG_INFO, _("starting"));
545     if (nofork)
546         fprintf(stderr, _("%s: starting...\n"), progname);
547
548     verto_run(vctx);
549     krb5_klog_syslog(LOG_INFO, _("finished, exiting"));
550
551     /* Clean up memory, etc */
552     svcauth_gssapi_unset_names();
553     kadm5_destroy(global_server_handle);
554     loop_free(vctx);
555     auth_fini(context);
556     (void)gss_release_name(&minor_status, &gss_changepw_name);
557     (void)gss_release_name(&minor_status, &gss_oldchangepw_name);
558     for (i = 0; i < 4; i++)
559         free(names[i].name);
560
561     krb5_klog_close(context);
562     krb5_free_context(context);
563     exit(0);
564 }