1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #include <stdio.h> 7 #include <stdlib.h> /* getenv, exit */ 8 #include <signal.h> 9 #include <sys/types.h> 10 #include <memory.h> 11 #include <stropts.h> 12 #include <netconfig.h> 13 #include <sys/resource.h> /* rlimit */ 14 #include <syslog.h> 15 16 #include <kadm5/admin.h> 17 #include <kadm5/kadm_rpc.h> 18 #include <kadm5/server_internal.h> 19 #include <server_acl.h> 20 #include <krb5/adm_proto.h> 21 #include <string.h> 22 #include <gssapi_krb5.h> 23 #include <sys/socket.h> 24 #include <netinet/in.h> 25 #include <arpa/inet.h> 26 #include <netdb.h> 27 #include <libintl.h> 28 #include <kdb/kdb_log.h> 29 #include "misc.h" 30 31 extern int setup_gss_names(struct svc_req *, char **, char **); 32 extern gss_name_t get_clnt_name(struct svc_req *); 33 extern char *client_addr(struct svc_req *, char *); 34 extern void *global_server_handle; 35 extern int nofork; 36 extern short l_port; 37 static char abuf[33]; 38 39 static char *reply_ok_str = "UPDATE_OK"; 40 static char *reply_err_str = "UPDATE_ERROR"; 41 static char *reply_fr_str = "UPDATE_FULL_RESYNC_NEEDED"; 42 static char *reply_busy_str = "UPDATE_BUSY"; 43 static char *reply_nil_str = "UPDATE_NIL"; 44 static char *reply_perm_str = "UPDATE_PERM_DENIED"; 45 static char *reply_unknown_str = "<UNKNOWN_CODE>"; 46 47 #define LOG_UNAUTH gettext("Unauthorized request: %s, %s, " \ 48 "client=%s, service=%s, addr=%s") 49 #define LOG_DONE gettext("Request: %s, %s, %s, client=%s, " \ 50 "service=%s, addr=%s") 51 52 #define KDB5_UTIL_DUMP_STR "/usr/sbin/kdb5_util dump -i " 53 54 #ifdef DPRINT 55 #undef DPRINT 56 #endif 57 #define DPRINT(i) if (nofork) printf i 58 59 #ifdef POSIX_SIGNALS 60 static struct sigaction s_action; 61 #endif /* POSIX_SIGNALS */ 62 63 static void 64 debprret(char *w, update_status_t ret, kdb_sno_t sno) 65 { 66 switch (ret) { 67 case UPDATE_OK: 68 printf("%s: end (OK, sno=%u)\n", 69 w, sno); 70 break; 71 case UPDATE_ERROR: 72 printf("%s: end (ERROR)\n", w); 73 break; 74 case UPDATE_FULL_RESYNC_NEEDED: 75 printf("%s: end (FR NEEDED)\n", w); 76 break; 77 case UPDATE_BUSY: 78 printf("%s: end (BUSY)\n", w); 79 break; 80 case UPDATE_NIL: 81 printf("%s: end (NIL)\n", w); 82 break; 83 case UPDATE_PERM_DENIED: 84 printf("%s: end (PERM)\n", w); 85 break; 86 default: 87 printf("%s: end (UNKNOWN return code (%d))\n", w, ret); 88 } 89 } 90 91 static char * 92 replystr(update_status_t ret) 93 { 94 switch (ret) { 95 case UPDATE_OK: 96 return (reply_ok_str); 97 case UPDATE_ERROR: 98 return (reply_err_str); 99 case UPDATE_FULL_RESYNC_NEEDED: 100 return (reply_fr_str); 101 case UPDATE_BUSY: 102 return (reply_busy_str); 103 case UPDATE_NIL: 104 return (reply_nil_str); 105 case UPDATE_PERM_DENIED: 106 return (reply_perm_str); 107 default: 108 return (reply_unknown_str); 109 } 110 } 111 112 kdb_incr_result_t * 113 iprop_get_updates_1(kdb_last_t *arg, struct svc_req *rqstp) 114 { 115 static kdb_incr_result_t ret; 116 char *whoami = "iprop_get_updates_1"; 117 int kret; 118 kadm5_server_handle_t handle = global_server_handle; 119 char *client_name = NULL, *service_name = NULL; 120 gss_name_t name = NULL; 121 OM_uint32 min_stat; 122 char obuf[256] = {0}; 123 124 /* default return code */ 125 ret.ret = UPDATE_ERROR; 126 127 DPRINT(("%s: start, last_sno=%u\n", whoami, (ulong_t)arg->last_sno)); 128 129 if (!handle) { 130 krb5_klog_syslog(LOG_ERR, 131 gettext("%s: server handle is NULL"), 132 whoami); 133 goto out; 134 } 135 136 if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { 137 krb5_klog_syslog(LOG_ERR, 138 gettext("%s: setup_gss_names failed"), 139 whoami); 140 goto out; 141 } 142 143 DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n", 144 whoami, client_name, service_name)); 145 146 if (!(name = get_clnt_name(rqstp))) { 147 krb5_klog_syslog(LOG_ERR, 148 gettext("%s: Couldn't obtain client's name"), 149 whoami); 150 goto out; 151 } 152 if (!kadm5int_acl_check(handle->context, 153 name, 154 ACL_IPROP, 155 NULL, 156 NULL)) { 157 ret.ret = UPDATE_PERM_DENIED; 158 159 audit_kadmind_unauth(rqstp->rq_xprt, l_port, 160 whoami, 161 "<null>", client_name); 162 krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami, 163 "<null>", client_name, service_name, 164 client_addr(rqstp, abuf)); 165 goto out; 166 } 167 168 kret = ulog_get_entries(handle->context, *arg, &ret); 169 170 if (ret.ret == UPDATE_OK) { 171 (void) snprintf(obuf, sizeof (obuf), 172 gettext("%s; Incoming SerialNo=%u; Outgoing SerialNo=%u"), 173 replystr(ret.ret), 174 (ulong_t)arg->last_sno, 175 (ulong_t)ret.lastentry.last_sno); 176 } else { 177 (void) snprintf(obuf, sizeof (obuf), 178 gettext("%s; Incoming SerialNo=%u; Outgoing SerialNo=N/A"), 179 replystr(ret.ret), 180 (ulong_t)arg->last_sno); 181 } 182 183 audit_kadmind_auth(rqstp->rq_xprt, l_port, 184 whoami, 185 obuf, client_name, kret); 186 187 krb5_klog_syslog(LOG_NOTICE, LOG_DONE, whoami, 188 obuf, 189 ((kret == 0) ? "success" : error_message(kret)), 190 client_name, service_name, 191 client_addr(rqstp, abuf)); 192 193 out: 194 if (nofork) 195 debprret(whoami, ret.ret, ret.lastentry.last_sno); 196 if (client_name) 197 free(client_name); 198 if (service_name) 199 free(service_name); 200 if (name) 201 gss_release_name(&min_stat, &name); 202 return (&ret); 203 } 204 205 206 /* 207 * Given a client princ (foo/fqdn@R), copy (in arg cl) the fqdn substring. 208 * Return arg cl str ptr on success, else NULL. 209 */ 210 static char * 211 getclhoststr(char *clprinc, char *cl, int len) 212 { 213 char *s; 214 if (s = strchr(clprinc, '/')) { 215 if (!++s || strlcpy(cl, s, len) >= len) { 216 return (NULL); 217 } 218 if (s = strchr(cl, '@')) { 219 *s = '\0'; 220 return (cl); /* success */ 221 } 222 } 223 224 return (NULL); 225 } 226 227 kdb_fullresync_result_t * 228 iprop_full_resync_1( 229 /* LINTED */ 230 void *argp, 231 struct svc_req *rqstp) 232 { 233 static kdb_fullresync_result_t ret; 234 char tmpf[MAX_FILENAME] = {0}; 235 char ubuf[MAX_FILENAME + sizeof (KDB5_UTIL_DUMP_STR)] = {0}; 236 char clhost[MAXHOSTNAMELEN] = {0}; 237 int pret, fret; 238 kadm5_server_handle_t handle = global_server_handle; 239 OM_uint32 min_stat; 240 gss_name_t name = NULL; 241 char *client_name = NULL, *service_name = NULL; 242 char *whoami = "iprop_full_resync_1"; 243 244 /* default return code */ 245 ret.ret = UPDATE_ERROR; 246 247 if (!handle) { 248 krb5_klog_syslog(LOG_ERR, 249 gettext("%s: server handle is NULL"), 250 whoami); 251 goto out; 252 } 253 254 DPRINT(("%s: start\n", whoami)); 255 256 if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { 257 krb5_klog_syslog(LOG_ERR, 258 gettext("%s: setup_gss_names failed"), 259 whoami); 260 goto out; 261 } 262 263 DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n", 264 whoami, client_name, service_name)); 265 266 if (!(name = get_clnt_name(rqstp))) { 267 krb5_klog_syslog(LOG_ERR, 268 gettext("%s: Couldn't obtain client's name"), 269 whoami); 270 goto out; 271 } 272 if (!kadm5int_acl_check(handle->context, 273 name, 274 ACL_IPROP, 275 NULL, 276 NULL)) { 277 ret.ret = UPDATE_PERM_DENIED; 278 279 audit_kadmind_unauth(rqstp->rq_xprt, l_port, 280 whoami, 281 "<null>", client_name); 282 krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami, 283 "<null>", client_name, service_name, 284 client_addr(rqstp, abuf)); 285 goto out; 286 } 287 288 if (!getclhoststr(client_name, clhost, sizeof (clhost))) { 289 krb5_klog_syslog(LOG_ERR, 290 gettext("%s: getclhoststr failed"), 291 whoami); 292 goto out; 293 } 294 295 /* 296 * construct db dump file name; kprop style name + clnt fqdn 297 */ 298 (void) strcpy(tmpf, "/var/krb5/slave_datatrans_"); 299 if (strlcat(tmpf, clhost, sizeof (tmpf)) >= sizeof (tmpf)) { 300 krb5_klog_syslog(LOG_ERR, 301 gettext("%s: db dump file name too long; max length=%d"), 302 whoami, 303 (sizeof (tmpf) - 1)); 304 goto out; 305 } 306 307 /* 308 * note the -i; modified version of kdb5_util dump format 309 * to include sno (serial number) 310 */ 311 if (strlcpy(ubuf, KDB5_UTIL_DUMP_STR, sizeof (ubuf)) >= 312 sizeof (ubuf)) { 313 goto out; 314 } 315 if (strlcat(ubuf, tmpf, sizeof (ubuf)) >= sizeof (ubuf)) { 316 krb5_klog_syslog(LOG_ERR, 317 gettext("%s: kdb5 util dump string too long; max length=%d"), 318 whoami, 319 (sizeof (ubuf) - 1)); 320 goto out; 321 } 322 323 /* 324 * Fork to dump the db and xfer it to the slave. 325 * (the fork allows parent to return quickly and the child 326 * acts like a callback to the slave). 327 */ 328 fret = fork(); 329 DPRINT(("%s: fork=%d (%d)\n", whoami, fret, getpid())); 330 331 switch (fret) { 332 case -1: /* error */ 333 if (nofork) { 334 perror(whoami); 335 } 336 krb5_klog_syslog(LOG_ERR, 337 gettext("%s: fork failed: %s"), 338 whoami, 339 error_message(errno)); 340 goto out; 341 342 case 0: /* child */ 343 DPRINT(("%s: run `%s' ...\n", whoami, ubuf)); 344 #ifdef POSIX_SIGNALS 345 (void) sigemptyset(&s_action.sa_mask); 346 s_action.sa_handler = SIG_DFL; 347 (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL); 348 #else 349 (void) signal(SIGCHLD, SIG_DFL); 350 #endif /* POSIX_SIGNALS */ 351 /* run kdb5_util(1M) dump for IProp */ 352 pret = pclose(popen(ubuf, "w")); 353 DPRINT(("%s: pclose=%d\n", whoami, pret)); 354 if (pret == -1) { 355 if (nofork) { 356 perror(whoami); 357 } 358 krb5_klog_syslog(LOG_ERR, 359 gettext("%s: pclose(popen) failed: %s"), 360 whoami, 361 error_message(errno)); 362 goto out; 363 } 364 365 DPRINT(("%s: exec `kprop -f %s %s' ...\n", 366 whoami, tmpf, clhost)); 367 pret = execl("/usr/lib/krb5/kprop", "kprop", "-f", tmpf, 368 clhost, NULL); 369 if (pret == -1) { 370 if (nofork) { 371 perror(whoami); 372 } 373 krb5_klog_syslog(LOG_ERR, 374 gettext("%s: exec failed: %s"), 375 whoami, 376 error_message(errno)); 377 goto out; 378 } 379 380 default: /* parent */ 381 ret.ret = UPDATE_OK; 382 /* not used by slave (sno is retrieved from kdb5_util dump) */ 383 ret.lastentry.last_sno = 0; 384 ret.lastentry.last_time.seconds = 0; 385 ret.lastentry.last_time.useconds = 0; 386 387 audit_kadmind_auth(rqstp->rq_xprt, l_port, 388 whoami, 389 "<null>", client_name, 0); 390 391 krb5_klog_syslog(LOG_NOTICE, LOG_DONE, whoami, 392 "<null>", 393 "success", 394 client_name, service_name, 395 client_addr(rqstp, abuf)); 396 397 goto out; 398 } 399 400 out: 401 if (nofork) 402 debprret(whoami, ret.ret, 0); 403 if (client_name) 404 free(client_name); 405 if (service_name) 406 free(service_name); 407 if (name) 408 gss_release_name(&min_stat, &name); 409 return (&ret); 410 } 411 412 void 413 krb5_iprop_prog_1( 414 struct svc_req *rqstp, 415 register SVCXPRT *transp) 416 { 417 union { 418 kdb_last_t iprop_get_updates_1_arg; 419 } argument; 420 char *result; 421 bool_t (*_xdr_argument)(), (*_xdr_result)(); 422 char *(*local)(); 423 char *whoami = "krb5_iprop_prog_1"; 424 425 switch (rqstp->rq_proc) { 426 case NULLPROC: 427 (void) svc_sendreply(transp, xdr_void, 428 (char *)NULL); 429 return; 430 431 case IPROP_GET_UPDATES: 432 _xdr_argument = xdr_kdb_last_t; 433 _xdr_result = xdr_kdb_incr_result_t; 434 local = (char *(*)()) iprop_get_updates_1; 435 break; 436 437 case IPROP_FULL_RESYNC: 438 _xdr_argument = xdr_void; 439 _xdr_result = xdr_kdb_fullresync_result_t; 440 local = (char *(*)()) iprop_full_resync_1; 441 break; 442 443 default: 444 krb5_klog_syslog(LOG_ERR, 445 gettext("RPC unknown request: %d (%s)"), 446 rqstp->rq_proc, whoami); 447 svcerr_noproc(transp); 448 return; 449 } 450 (void) memset((char *)&argument, 0, sizeof (argument)); 451 if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) { 452 krb5_klog_syslog(LOG_ERR, 453 gettext("RPC svc_getargs failed (%s)"), 454 whoami); 455 svcerr_decode(transp); 456 return; 457 } 458 result = (*local)(&argument, rqstp); 459 460 if (_xdr_result && result != NULL && 461 !svc_sendreply(transp, _xdr_result, result)) { 462 krb5_klog_syslog(LOG_ERR, 463 gettext("RPC svc_sendreply failed (%s)"), 464 whoami); 465 svcerr_systemerr(transp); 466 } 467 if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) { 468 krb5_klog_syslog(LOG_ERR, 469 gettext("RPC svc_freeargs failed (%s)"), 470 whoami); 471 472 exit(1); 473 } 474 475 if (rqstp->rq_proc == IPROP_GET_UPDATES) { 476 /* LINTED */ 477 kdb_incr_result_t *r = (kdb_incr_result_t *)result; 478 479 if (r->ret == UPDATE_OK) { 480 ulog_free_entries(r->updates.kdb_ulog_t_val, 481 r->updates.kdb_ulog_t_len); 482 r->updates.kdb_ulog_t_val = NULL; 483 r->updates.kdb_ulog_t_len = 0; 484 } 485 } 486 487 } 488 489 /* 490 * Get the host base service name for the kiprop principal. Returns 491 * KADM5_OK on success. Caller must free the storage allocated for 492 * host_service_name. 493 */ 494 kadm5_ret_t 495 kiprop_get_adm_host_srv_name( 496 krb5_context context, 497 const char *realm, 498 char **host_service_name) 499 { 500 kadm5_ret_t ret; 501 char *name; 502 char *host; 503 504 if (ret = kadm5_get_master(context, realm, &host)) 505 return (ret); 506 507 name = malloc(strlen(KIPROP_SVC_NAME)+ strlen(host) + 2); 508 if (name == NULL) { 509 free(host); 510 return (ENOMEM); 511 } 512 (void) sprintf(name, "%s@%s", KIPROP_SVC_NAME, host); 513 free(host); 514 *host_service_name = name; 515 516 return (KADM5_OK); 517 } 518