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