1   /*
   2    * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
   3    * Use is subject to license terms.
   4    * 
   5    * All rights reserved.
   6    * 
   7    * Export of this software from the United States of America may require
   8    * a specific license from the United States Government.  It is the
   9    * responsibility of any person or organization contemplating export to
  10    * obtain such a license before exporting.
  11    * 
  12    * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
  13    * distribute this software and its documentation for any purpose and
  14    * without fee is hereby granted, provided that the above copyright
  15    * notice appear in all copies and that both that copyright notice and
  16    * this permission notice appear in supporting documentation, and that
  17    * the name of FundsXpress. not be used in advertising or publicity pertaining
  18    * to distribution of the software without specific, written prior
  19    * permission.  FundsXpress makes no representations about the suitability of
  20    * this software for any purpose.  It is provided "as is" without express
  21    * or implied warranty.
  22    * 
  23    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  24    * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  25    * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  26    */
  27   
  28 | #pragma ident        "@(#)kpropd.c        1.7        04/09/08 SMI"
  28 | #pragma ident        "@(#)kpropd.c        1.6        04/07/27 SMI"
  29   
  30   /*
  31    * slave/kpropd.c
  32    *
  33    * Copyright 1990,1991 by the Massachusetts Institute of Technology.
  34    * All Rights Reserved.
  35    *
  36    * Export of this software from the United States of America may
  37    *   require a specific license from the United States Government.
  38    *   It is the responsibility of any person or organization contemplating
  39    *   export to obtain such a license before exporting.
  40    * 
  41    * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
  42    * distribute this software and its documentation for any purpose and
  43    * without fee is hereby granted, provided that the above copyright
  44    * notice appear in all copies and that both that copyright notice and
  45    * this permission notice appear in supporting documentation, and that
  46    * the name of M.I.T. not be used in advertising or publicity pertaining
  47    * to distribution of the software without specific, written prior
  48    * permission.  Furthermore if you modify this software you must label
  49    * your software as modified software and not distribute it in such a
  50    * fashion that it might be confused with the original M.I.T. software.
  51    * M.I.T. makes no representations about the suitability of
  52    * this software for any purpose.  It is provided "as is" without express
  53    * or implied warranty.
  54    * 
  55    *
  56    * XXX We need to modify the protocol so that an acknowledge is set
  57    * after each block, instead after the entire series is sent over.
  58    * The reason for this is so that error packets can get interpreted
  59    * right away.  If you don't do this, the sender may never get the
  60    * error packet, because it will die an EPIPE trying to complete the
  61    * write...
  62    */
  63   
  64   #include <stdio.h>
  65   #include <ctype.h>
  66   #include <sys/file.h>
  67   #include <signal.h>
  68   #include <string.h>
  69   #ifndef POSIX_TERMIOS
  70   #include <sgtty.h>
  71   #endif
  72   #include <fcntl.h>
  73   #include <sys/types.h>
  74   #include <sys/time.h>
  75   #include <sys/stat.h>
  76   #include <sys/socket.h>
  77   #include <sys/wait.h>
  78   #include <netinet/in.h>
  79   #include <arpa/inet.h>
  80   #include <sys/param.h>
  81   #include <netdb.h>
  82   #include <syslog.h>
  83   #include <libintl.h>
  84   #include <locale.h>
  85   #include <k5-int.h>
  86   #include <socket-utils.h>
  87   #include "com_err.h"
  88   #include <errno.h>
  89   
  90   #include "kprop.h"
  91 + #include <iprop_hdr.h>
  92 + #include "iprop.h"
  93 + #include <kadm5/admin.h>
  94 + #include <kdb/kdb_log.h>
  95   
  96   #define SYSLOG_CLASS LOG_DAEMON
  97   
  98 + char *poll_time = NULL;
  99 + char *def_realm = NULL;
 100 + boolean_t runonce = B_FALSE;
 101 + 
 102 + /*
 103 +  * This struct simulates the use of _kadm5_server_handle_t
 104 +  */
 105 + typedef struct _kadm5_iprop_handle_t {
 106 +         krb5_ui_4        magic_number;
 107 +         krb5_ui_4        struct_version;
 108 +         krb5_ui_4        api_version;
 109 +         char                 *cache_name;
 110 +         int                destroy_cache;
 111 +         CLIENT                *clnt;
 112 +         krb5_context        context;
 113 +         kadm5_config_params params;
 114 +         struct _kadm5_iprop_handle_t *lhandle;
 115 + } *kadm5_iprop_handle_t;
 116 + 
 117   static char *kprop_version = KPROP_PROT_VERSION;
 118   
 119   char        *progname;
 120   int     debug = 0;
 121   char        *srvtab = 0;
 122   int        standalone = 0;
 123   
 124   krb5_principal        server;                /* This is our server principal name */
 125   krb5_principal        client;                /* This is who we're talking to */
 126   krb5_context kpropd_context;
 127   krb5_auth_context auth_context;
 128   char        *realm = NULL;                /* Our realm */
 129   char        *file = KPROPD_DEFAULT_FILE;
 130   char        *temp_file_name;
 131   char        *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL;
 132   char        *kerb_database = NULL;
 133   char        *acl_file_name = KPROPD_ACL_FILE;
 134   
 135   int                database_fd;
 136   krb5_address        sender_addr;
 137   krb5_address        receiver_addr;
 138   short                 port = 0;
 139   
 140   void        PRS
 141            (int, char**);
 142   int        do_standalone
 120 |          (void);
 143 |          (iprop_role iproprole);
 144   void        doit
 145            (int);
 146 + krb5_error_code        do_iprop(kdb_log_context *log_ctx);
 147 + 
 148   void        kerberos_authenticate
 149            (krb5_context,
 150                      int,
 151                      krb5_principal *,
 152                      krb5_enctype *,
 153                      struct sockaddr_storage);
 154   
 155   krb5_boolean authorized_principal
 156            (krb5_context,
 157                          krb5_principal,
 158                      krb5_enctype);
 159   void        recv_database
 160            (krb5_context,
 161                      int,
 162                      int,
 163                      krb5_data *);
 164   void        load_database
 165            (krb5_context,
 166                          char *,
 167                          char *);
 168   void        send_error
 169            (krb5_context,
 170                          int,
 171                      krb5_error_code,
 172                          char        *);
 173   void        recv_error
 174            (krb5_context,
 175                          krb5_data *);
 176 + int        convert_polltime
 177 +         (char *);
 178 + unsigned int        backoff_from_master
 179 +         (int *);
 180   
 181   static void usage()
 182   {
 183           fprintf(stderr,
 184                   gettext("\nUsage: %s\n"), /* progname may be a long pathname */
 185                   progname);
 186   
 187           fprintf(stderr,
 188                   gettext("\t[-r realm] [-s srvtab] [-dS] [-f slave_file]\n"));
 189   
 190           fprintf(stderr,
 191                   gettext("\t[-F kerberos_db_file ] [-p kdb5_util_pathname]\n"));
 192                   
 193           fprintf(stderr, gettext("\t[-P port] [-a acl_file]\n"));
 194   
 195           exit(1);
 196   }
 197   
 198   int
 199   main(argc, argv)
 200           int        argc;
 201           char        **argv;
 202   {
 203 +         krb5_error_code retval;
 204 +         int ret = 0;
 205 +         kdb_log_context        *log_ctx;
 206 + 
 207           PRS(argc, argv);
 208   
 209 +         log_ctx = kpropd_context->kdblog_context;
 210 + 
 211 +         if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
 212 +                 /*
 213 +                  * We wanna do iprop !
 214 +                  */
 215 +                 retval = do_iprop(log_ctx);
 216 +                 if (retval) {
 217 +                         com_err(progname, retval,
 218 +                                         gettext("do_iprop failed.\n"));
 219 +                         exit(1);
 220 +                 }
 221 + 
 222 +         } else {
 223                   if (standalone)
 177 |                 do_standalone();
 224 |                         ret = do_standalone(IPROP_NULL);
 225                   else
 180 -         exit(0);
 226                           doit(0);
 227           }
 228   
 183 | int do_standalone()
 229 |         exit(ret);
 230 + }
 231 + 
 232 + int do_standalone(iprop_role iproprole)
 233   {
 234       struct        linger linger;
 235       struct        servent *sp;
 236       int        finet, fromlen, s;
 237       int        on = 1;
 238       int        ret, status = 0;
 239       struct        sockaddr_in6 sin6 = { AF_INET6 };
 240       int sin6_size = sizeof (sin6);
 241 + 
 242       /* listen for either ipv4 or ipv6 */
 243       finet = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
 244       if (finet < 0 ) {
 245           com_err(progname, errno, gettext("while obtaining socket"));
 246           exit(1);
 247       } 
 248   
 249       if(!port) {
 250           sp = getservbyname(KPROP_SERVICE, "tcp");
 251           if (sp == NULL) {
 252               com_err(progname, 0, gettext("%s/tcp: unknown service"),
 253                       KPROP_SERVICE);
 254               exit(1);
 255 +         }
 256           sin6.sin6_port = sp->s_port;
 257       } else
 258           sin6.sin6_port = port;
 259 + 
 260 +     /*
 261 +      * We need to close the socket immediately if iprop is enabled,
 262 +      * since back-to-back full resyncs are possible, so we do not
 263 +      * linger around for too long
 264 +      */
 265 +     if (iproprole == IPROP_SLAVE) {
 266 +             if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR,
 267 +                         (char *)&on, sizeof(on)) < 0)
 268 +                     com_err(progname, errno,
 269 +                             gettext("in setsockopt(SO_REUSEADDR)"));
 270 +             linger.l_onoff = 1;
 271 +             linger.l_linger = 2;
 272 +             if (setsockopt(finet, SOL_SOCKET, SO_LINGER,
 273 +                         (void *)&linger, sizeof(linger)) < 0)
 274 +                     com_err(progname, errno,
 275 +                             gettext("in setsockopt(SO_LINGER)"));
 276 +     }
 277       if ((ret = bind(finet, (struct sockaddr *)&sin6, sizeof(sin6))) < 0) {
 278           if (debug) {
 279               on = 1;
 280               fprintf(stderr,
 281                       gettext("%s: attempting to rebind socket "
 282                       "with SO_REUSEADDR\n"), progname);
 283               if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR,
 284                           (char *)&on, sizeof(on)) < 0) {
 285                   com_err(progname, errno, 
 286                           gettext("in setsockopt(SO_REUSEADDR)"));
 287               }
 288               ret = bind(finet, (struct sockaddr *) &sin6, sizeof(sin6));
 289           }
 290   
 291           if (ret < 0) {
 292               perror(gettext("bind"));
 293               com_err(progname, errno, 
 294                       gettext("while binding listener socket"));
 295               exit(1);
 296           }
 297       }
 298   
 230 |     if (!debug)
 299 |     if (!debug && (iproprole != IPROP_SLAVE))
 300           daemon(1, 0);
 301   
 302   #ifdef PID_FILE
 303       if ((pidfile = fopen(PID_FILE, "w")) != NULL) {
 304           fprintf(pidfile, gettext("%d\n"), getpid());
 305           fclose(pidfile);
 306       } else
 307           com_err(progname, errno,
 308                   gettext("while opening pid file %s for writing"),
 309                   PID_FILE);
 310   #endif
 311   
 312       if (listen(finet, 5) < 0) {
 313           com_err(progname, errno, gettext("in listen call"));
 314           exit(1);
 315       }
 316 + 
 317       while (1) {
 318           int child_pid;
 319 + 
 320           s = accept(finet, (struct sockaddr *) &sin6, &sin6_size);
 321   
 322           if (s < 0) {
 323               if (errno != EINTR)
 324                   com_err(progname, errno,
 325                       gettext("from accept system call")); 
 326               continue;
 327           }
 328   
 258 |         if (debug)
 329 |         if (debug && (iproprole != IPROP_SLAVE))
 330               child_pid = 0;
 331           else
 332               child_pid = fork();
 333 + 
 334           switch (child_pid) {
 335           case -1:
 336               com_err(progname, errno, gettext("while forking"));
 337               exit(1);
 338               /*NOTREACHED*/
 339           case 0:
 340               /* child */
 341               (void) close(finet);
 342               doit(s);
 343               close(s);
 344               _exit(0);
 345               /*NOTREACHED*/
 346           default:
 347               /* parent */
 276 |             wait(0);
 348 |             if (wait(&status) < 0) {
 349 +                 com_err(progname, errno,
 350 +                     gettext("while waiting to receive database"));
 351 +                 exit(1);
 352 +             }
 353 + 
 354               close(s);
 355 +             if (iproprole == IPROP_SLAVE)
 356 +                 close(finet);
 357   
 358 +             if ((ret = WEXITSTATUS(status)) != 0)
 359 +                 return (ret);
 360           }
 361   
 362 +         if (iproprole == IPROP_SLAVE)
 363 +             break;
 364       }
 365 + 
 366 +     return (0);
 367   }
 368   
 369   void doit(fd)
 370           int        fd;
 371   {
 372           struct sockaddr_storage from;
 373           socklen_t fromlen;
 374           int on = 1;
 375           struct hostent        *hp;
 376           krb5_error_code        retval;
 377           krb5_data confmsg;
 378           int lock_fd;
 379           int omask;
 380           krb5_enctype etype;
 381           char ntop[NI_MAXHOST] = "";
 382           krb5_context doit_context;
 383 +         kdb_log_context *log_ctx;
 384   
 385           retval = krb5_init_context(&doit_context);
 386           if (retval) {
 387                   com_err(progname, retval, gettext("while initializing krb5"));
 388                   exit(1);
 389           }
 390 +         log_ctx = kpropd_context->kdblog_context;
 391 +         if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE))
 392 +                 ulog_set_role(doit_context, IPROP_SLAVE);
 393   
 394           fromlen = (socklen_t)sizeof (from);
 395   
 396           if (getpeername(fd, (struct sockaddr *) &from, &fromlen) < 0) {
 397                   fprintf(stderr, "%s: ", progname);
 398                   perror(gettext("getpeername"));
 399                   exit(1);
 400           }
 401           if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (caddr_t) &on,
 402                          sizeof (on)) < 0) {
 403                   com_err(progname, errno,
 404                   gettext("while attempting setsockopt (SO_KEEPALIVE)"));
 405           }
 406   
 407           if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
 408                   NULL, 0, NI_NUMERICHOST) != 0) {
 409   
 410                   /* getnameifo failed so use inet_ntop() to get printable addresses */
 411                   if (from.ss_family == AF_INET) {
 412   
 413                           inet_ntop(AF_INET,
 414                               (const void *)&ss2sin(&from)->sin_addr,
 415                               ntop, sizeof(ntop));
 416   
 417                   } else if (from.ss_family == AF_INET6 &&
 418                           ! IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from)->sin6_addr)) {
 419   
 420                           ipaddr_t v4addr;
 421   
 422                           inet_ntop(AF_INET6,
 423                                   (const void *)&ss2sin6(&from)->sin6_addr, ntop,
 424                                   sizeof(ntop));
 425                   }
 426                   /* ipv4 mapped ipv6 addrs handled later */
 427           }
 428   
 429           if (from.ss_family == AF_INET || from.ss_family == AF_INET6) {
 430   
 431                   if (from.ss_family == AF_INET6 && 
 432                           IN6_IS_ADDR_V4MAPPED(&ss2sin6(&from)->sin6_addr)) {
 433   
 434                           ipaddr_t v4addr;
 435   
 436                           /* coerce ipv4 mapped ipv6 addr to normal ipv4 addr */
 437                           IN6_V4MAPPED_TO_IPADDR(&(ss2sin6(&from)->sin6_addr),
 438                                   v4addr);
 439   
 440                           inet_ntop(AF_INET, (const void *) &v4addr,
 441                                   ntop, sizeof(ntop));
 442                   }
 443   
 444                   syslog(LOG_INFO, gettext("Connection from %s"), ntop);
 445   
 446                   if (debug)
 447                           printf("Connection from %s\n", ntop);
 448   
 449           } else {
 450                   /* address family isn't either AF_INET || AF_INET6 */
 451                   syslog(LOG_INFO, 
 452                       gettext("Connection from unknown address family:%d"),
 453                       from.ss_family);
 454   
 455                   if (debug) {
 456                           printf(gettext("Connection from unknown address family:%d"),
 457                               from.ss_family);
 458                   }
 459           }
 460   
 461           /*
 462            * Now do the authentication
 463            */
 464           kerberos_authenticate(doit_context, fd, &client, &etype, from);
 465   
 466           if (!authorized_principal(doit_context, client, etype)) {
 467               char        *name;
 468   
 469               if (retval = krb5_unparse_name(doit_context, client, &name)) {
 470                   com_err(progname, retval,
 471                       gettext("While unparsing client name"));
 472                   exit(1);
 473               }
 474               syslog(LOG_WARNING,
 475                   gettext("Rejected connection from unauthorized principal %s"),
 476                   name);
 477               free(name);
 478               exit(1);
 479           }
 480           omask = umask(077);
 481           lock_fd = open(temp_file_name, O_RDWR|O_CREAT, 0600);
 482           (void) umask(omask);
 483           retval = krb5_lock_file(doit_context, lock_fd, 
 484                                   KRB5_LOCKMODE_EXCLUSIVE|KRB5_LOCKMODE_DONTBLOCK);
 485           if (retval) {
 486               com_err(progname, retval, 
 487                           gettext("while trying to lock '%s'"),
 488                       temp_file_name);
 489               exit(1);
 490           }
 491           if ((database_fd = open(temp_file_name,
 492                                   O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
 493                   com_err(progname, errno,
 494                           gettext("while opening database file, '%s'"),
 495                           temp_file_name);
 496                   exit(1);
 497           }
 498           recv_database(doit_context, fd, database_fd, &confmsg);
 499           if (rename(temp_file_name, file)) {
 500                   com_err(progname, errno, 
 501                           gettext("While renaming %s to %s"),
 502                           temp_file_name, file);
 503                   exit(1);
 504           }
 505           retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_SHARED);
 506           if (retval) {
 507               com_err(progname, retval,
 508                           gettext("while downgrading lock on '%s'"),
 509                       temp_file_name);
 510               exit(1);
 511           }
 512           load_database(doit_context, kdb5_util, file);
 513           retval = krb5_lock_file(doit_context, lock_fd, KRB5_LOCKMODE_UNLOCK);
 514           if (retval) {
 515               com_err(progname, retval, 
 516                   gettext("while unlocking '%s'"), temp_file_name);
 517               exit(1);
 518           }
 519           (void)close(lock_fd);
 520   
 521           /*
 522            * Send the acknowledgement message generated in
 523            * recv_database, then close the socket.
 524            */
 525           if (retval = krb5_write_message(doit_context, (void *) &fd,
 526                                           &confmsg)) { 
 527                   krb5_free_data_contents(doit_context, &confmsg);
 528                   com_err(progname, retval,
 529                           gettext("while sending # of received bytes"));
 530                   exit(1);
 531           }
 532           krb5_free_data_contents(doit_context, &confmsg);
 533           if (close(fd) < 0) {
 534                   com_err(progname, errno,
 535                           gettext("while trying to close database file"));
 536                   exit(1);
 537           }
 538   
 539           exit(0);
 540   }
 541   
 542 + 
 543 + /*
 544 +  * Routine to handle incremental update transfer(s) from master KDC
 545 +  */
 546 + krb5_error_code do_iprop(kdb_log_context *log_ctx) {
 547 +         CLIENT *cl;
 548 +         kadm5_ret_t retval;
 549 +         kadm5_config_params params;
 550 +         krb5_ccache cc;
 551 +         krb5_principal iprop_svc_principal;
 552 +         void *server_handle = NULL;
 553 +         char *iprop_svc_princstr = NULL;
 554 +         char *master_svc_princstr = NULL;
 555 +         char *admin_server = NULL;
 556 +         char *keytab_name = NULL;
 557 +         unsigned int pollin, backoff_time;
 558 +         int backoff_cnt = 0;
 559 +         int reinit_cnt = 0;
 560 +         int ret;
 561 +         boolean_t frdone = B_FALSE;
 562 + 
 563 +         kdb_incr_result_t *incr_ret;
 564 +         static kdb_last_t mylast;
 565 + 
 566 +         kdb_fullresync_result_t *full_ret;
 567 +         char *full_resync_arg = NULL;
 568 + 
 569 +         kadm5_iprop_handle_t handle;
 570 +         kdb_hlog_t *ulog;
 571 + 
 572 +         if (!debug)
 573 +                 daemon(0, 0);
 574 + 
 575 +         pollin = (unsigned int)0;
 576 +         (void) memset((char *)&params, 0, sizeof (params));
 577 +         ulog = log_ctx->ulog;
 578 + 
 579 +         params.mask |= KADM5_CONFIG_REALM;
 580 +         params.realm = def_realm;
 581 + 
 582 +         if (master_svc_princstr == NULL) {
 583 +                 if (retval = kadm5_get_kiprop_host_srv_name(kpropd_context,
 584 +                                         def_realm, &master_svc_princstr)) {
 585 +                         com_err(progname, retval,
 586 +                                 gettext("%s: unable to get kiprop host based "
 587 +                                         "service name for realm %s\n"),
 588 +                                         progname, def_realm);
 589 +                         exit(1);
 590 +                 }
 591 +         }
 592 + 
 593 +         /*
 594 +          * Set cc to the default credentials cache
 595 +          */
 596 +         if (retval = krb5_cc_default(kpropd_context, &cc)) {
 597 +                 com_err(progname, retval,
 598 +                         gettext("while opening default "
 599 +                                 "credentials cache"));
 600 +                 exit(1);
 601 +         }
 602 + 
 603 +         retval = krb5_sname_to_principal(kpropd_context, NULL, KIPROP_SVC_NAME,
 604 +                                 KRB5_NT_SRV_HST, &iprop_svc_principal);
 605 +         if (retval) {
 606 +                 com_err(progname, retval, gettext("while trying to construct "
 607 +                                                 "host service principal"));
 608 +                 exit(1);
 609 +         }
 610 + 
 611 +         if (retval = krb5_unparse_name(kpropd_context, iprop_svc_principal,
 612 +                                 &iprop_svc_princstr)) {
 613 +                 com_err(progname, retval,
 614 +                         gettext("while canonicalizing "
 615 +                                 "principal name"));
 616 +                 krb5_free_principal(kpropd_context, iprop_svc_principal);
 617 +                 exit(1);
 618 +         }
 619 +         krb5_free_principal(kpropd_context, iprop_svc_principal);
 620 + 
 621 + reinit:
 622 +         /*
 623 +          * Authentication, initialize rpcsec_gss handle etc.
 624 +          */
 625 +         retval = kadm5_init_with_skey(iprop_svc_princstr, keytab_name,
 626 +                                     master_svc_princstr,
 627 +                                     &params,
 628 +                                     KADM5_STRUCT_VERSION,
 629 +                                     KADM5_API_VERSION_2,
 630 +                                      &server_handle);
 631 + 
 632 +         if (retval) {
 633 +                 if (retval == KADM5_RPC_ERROR) {
 634 +                         reinit_cnt++;
 635 +                         if (server_handle)
 636 +                                 kadm5_destroy((void *) server_handle);
 637 +                         server_handle = (void *)NULL;
 638 +                         handle = (kadm5_iprop_handle_t)NULL;
 639 + 
 640 +                         com_err(progname, retval, gettext(
 641 +                                         "while attempting to connect"
 642 +                                         " to master KDC ... retrying"));
 643 +                         backoff_time = backoff_from_master(&reinit_cnt);
 644 +                         (void) sleep(backoff_time);
 645 +                         goto reinit;
 646 +                 } else {
 647 +                         com_err(progname, retval,
 648 +                                 gettext("while initializing %s interface"),
 649 +                                 progname);
 650 +                         if (retval == KADM5_BAD_CLIENT_PARAMS ||
 651 +                             retval == KADM5_BAD_SERVER_PARAMS)
 652 +                                 usage();
 653 +                         exit(1);
 654 +                 }
 655 +         }
 656 + 
 657 +         /*
 658 +          * Reset re-initialization count to zero now.
 659 +          */
 660 +         reinit_cnt = backoff_time = 0;
 661 + 
 662 +         /*
 663 +          * Reset the handle to the correct type for the RPC call
 664 +          */
 665 +         handle = server_handle;
 666 +         
 667 +         /*
 668 +          * If we have reached this far, we have succesfully established
 669 +          * a RPCSEC_GSS connection; we now start polling for updates
 670 +          */
 671 +         if (poll_time == NULL) {
 672 +                 if ((poll_time = (char *)strdup("2m")) == NULL) {
 673 +                         com_err(progname, ENOMEM,
 674 +                                 gettext("Unable to allocate poll_time"));
 675 +                         exit(1);
 676 +                 }
 677 +         }
 678 + 
 679 +         if (pollin == (unsigned int)0)
 680 +                 pollin = convert_polltime(poll_time);
 681 + 
 682 +         for (;;) {
 683 +                 incr_ret = NULL;
 684 +                 full_ret = NULL;
 685 + 
 686 +                 /*
 687 +                  * Get the most recent ulog entry sno + ts, which
 688 +                  * we package in the request to the master KDC
 689 +                  */
 690 +                 mylast.last_sno = ulog->kdb_last_sno;
 691 +                 mylast.last_time = ulog->kdb_last_time;
 692 + 
 693 +                 /*
 694 +                  * Loop continuously on an iprop_get_updates_1(),
 695 +                  * so that we can keep probing the master for updates
 696 +                  * or (if needed) do a full resync of the krb5 db.
 697 +                  */
 698 + 
 699 +                 incr_ret = iprop_get_updates_1(&mylast, handle->clnt);
 700 +                 if (incr_ret == (kdb_incr_result_t *)NULL) {
 701 +                         clnt_perror(handle->clnt,
 702 +                                     "iprop_get_updates call failed");
 703 +                         if (server_handle)
 704 +                                 kadm5_destroy((void *)server_handle);
 705 +                         server_handle = (void *)NULL;
 706 +                         handle = (kadm5_iprop_handle_t)NULL;
 707 +                         goto reinit;
 708 +                 }
 709 + 
 710 +                 switch (incr_ret->ret) {
 711 + 
 712 +                 case UPDATE_FULL_RESYNC_NEEDED:
 713 +                         /*
 714 +                          * We dont do a full resync again, if the last
 715 +                          * X'fer was a resync and if the master sno is
 716 +                          * still "0", i.e. no updates so far.
 717 +                          */
 718 +                         if ((frdone == B_TRUE) && (incr_ret->lastentry.last_sno
 719 +                                                 == 0)) {
 720 +                                 break;
 721 +                         } else {
 722 + 
 723 +                                 full_ret = iprop_full_resync_1((void *)
 724 +                                                 &full_resync_arg, handle->clnt);
 725 + 
 726 +                                 if (full_ret == (kdb_fullresync_result_t *)
 727 +                                                         NULL) {
 728 +                                         clnt_perror(handle->clnt,
 729 +                                             "iprop_full_resync call failed");
 730 +                                         if (server_handle)
 731 +                                                 kadm5_destroy((void *)
 732 +                                                         server_handle);
 733 +                                         server_handle = (void *)NULL;
 734 +                                         handle = (kadm5_iprop_handle_t)NULL;
 735 +                                         goto reinit;
 736 +                                 }
 737 +                         }
 738 + 
 739 +                         switch (full_ret->ret) {
 740 +                         case UPDATE_OK:
 741 +                                 backoff_cnt = 0;
 742 +                                 /* 
 743 +                                  * We now listen on the kprop port for
 744 +                                  * the full dump
 745 +                                  */
 746 +                                 ret = do_standalone(log_ctx->iproprole);
 747 +                                 if (ret)
 748 +                                         syslog(LOG_WARNING,
 749 +                                             gettext("kpropd: Full resync, "
 750 +                                             "invalid return."));
 751 +                                 if (debug)
 752 +                                         if (ret)
 753 +                                                 fprintf(stderr,
 754 +                                                     gettext("Full resync "
 755 +                                                     "was unsuccessful\n"));
 756 +                                         else
 757 +                                                 fprintf(stderr,
 758 +                                                     gettext("Full resync "
 759 +                                                     "was successful\n"));
 760 +                                 frdone = B_TRUE;
 761 +                                 break;
 762 + 
 763 +                         case UPDATE_BUSY:
 764 +                                 /*
 765 +                                  * Exponential backoff
 766 +                                  */
 767 +                                 backoff_cnt++;
 768 +                                 break;
 769 + 
 770 +                         case UPDATE_FULL_RESYNC_NEEDED:
 771 +                         case UPDATE_NIL:
 772 +                         default:
 773 +                                 backoff_cnt = 0;
 774 +                                 frdone = B_FALSE;
 775 +                                 syslog(LOG_ERR, gettext("kpropd: Full resync,"
 776 +                                         " invalid return from master KDC."));
 777 +                                 break;
 778 + 
 779 +                         case UPDATE_PERM_DENIED:
 780 +                                 syslog(LOG_ERR, gettext("kpropd: Full resync,"
 781 +                                         " permission denied."));
 782 +                                 goto error;
 783 + 
 784 +                         case UPDATE_ERROR:
 785 +                                 syslog(LOG_ERR, gettext("kpropd: Full resync,"
 786 +                                         " error returned from master KDC."));
 787 +                                 goto error;
 788 +                         }
 789 +                         break;
 790 + 
 791 +                 case UPDATE_OK:
 792 +                         backoff_cnt = 0;
 793 +                         frdone = B_FALSE;
 794 + 
 795 +                         /*
 796 +                          * ulog_replay() will convert the ulog updates to db
 797 +                          * entries using the kdb conv api and will commit
 798 +                          * the entries to the slave kdc database
 799 +                          */
 800 +                         retval = ulog_replay(kpropd_context, incr_ret);
 801 + 
 802 +                         if (retval) {
 803 +                                 syslog(LOG_ERR, gettext("kpropd: ulog_replay"
 804 +                                         " failed, updates not registered."));
 805 +                                 break;
 806 +                         }
 807 + 
 808 +                         if (debug)
 809 +                                 fprintf(stderr, gettext("Update transfer "
 810 +                                         "from master was OK\n"));
 811 +                         break;
 812 + 
 813 +                 case UPDATE_PERM_DENIED:
 814 +                         syslog(LOG_ERR, gettext("kpropd: get_updates,"
 815 +                                                 " permission denied."));
 816 +                         goto error;
 817 + 
 818 +                 case UPDATE_ERROR:
 819 +                         syslog(LOG_ERR, gettext("kpropd: get_updates, error "
 820 +                                                 "returned from master KDC."));
 821 +                         goto error;
 822 + 
 823 +                 case UPDATE_BUSY:
 824 +                         /*
 825 +                          * Exponential backoff
 826 +                          */
 827 +                         backoff_cnt++;
 828 +                         break;
 829 + 
 830 +                 case UPDATE_NIL:
 831 +                         /*
 832 +                          * Master-slave are in sync
 833 +                          */
 834 +                         if (debug)
 835 +                                 fprintf(stderr, gettext("Master, slave KDC's "
 836 +                                         "are in-sync, no updates\n"));
 837 +                         backoff_cnt = 0;
 838 +                         frdone = B_FALSE;
 839 +                         break;
 840 + 
 841 +                 default:
 842 +                         backoff_cnt = 0;
 843 +                         syslog(LOG_ERR, gettext("kpropd: get_updates,"
 844 +                                         " invalid return from master KDC."));
 845 +                         break;
 846 +                 }
 847 + 
 848 +                 if (runonce == B_TRUE)
 849 +                         goto done;
 850 + 
 851 +                 /*
 852 +                  * Sleep for the specified poll interval (Default is 2 mts),
 853 +                  * or do a binary exponential backoff if we get an
 854 +                  * UPDATE_BUSY signal
 855 +                  */
 856 +                 if (backoff_cnt > 0) {
 857 +                         backoff_time = backoff_from_master(&backoff_cnt);
 858 +                         if (debug)
 859 +                                 fprintf(stderr, gettext("Busy signal received "
 860 +                                         "from master, backoff for %d secs\n"),
 861 +                                         backoff_time);
 862 +                         (void) sleep(backoff_time);
 863 +                 }
 864 +                 else
 865 +                         (void) sleep(pollin);
 866 + 
 867 +         }
 868 + 
 869 + 
 870 + error:
 871 +         if (debug)
 872 +                 fprintf(stderr, gettext("ERROR returned by master, bailing\n"));
 873 +         syslog(LOG_ERR, gettext("kpropd: ERROR returned by master KDC,"
 874 +                         " bailing.\n"));
 875 + done:
 876 +         if (poll_time)
 877 +                 free(poll_time);
 878 +         if(iprop_svc_princstr)
 879 +                 free(iprop_svc_princstr);
 880 +         if (master_svc_princstr)
 881 +                 free(master_svc_princstr);
 882 +         if (retval = krb5_cc_close(kpropd_context, cc)) {
 883 +                 com_err(progname, retval,
 884 +                         gettext("while closing default ccache"));
 885 +                 exit(1);
 886 +         }
 887 +         if (def_realm)
 888 +                 free(def_realm);
 889 +         if (server_handle)
 890 +                 kadm5_destroy((void *)server_handle);
 891 +         if (kpropd_context)
 892 +                 krb5_free_context(kpropd_context);
 893 + 
 894 +         if (runonce == B_TRUE)
 895 +                 return (0);
 896 +         else
 897 +                 exit(1);
 898 + }
 899 + 
 900 + 
 901 + /*
 902 +  * Do exponential backoff, since master KDC is BUSY or down
 903 +  */
 904 + unsigned int backoff_from_master(int *cnt) {
 905 +         unsigned int btime;
 906 + 
 907 +         btime = (unsigned int)(2<<(*cnt));
 908 +         if (btime > MAX_BACKOFF) {
 909 +                 btime = MAX_BACKOFF;
 910 +                 *cnt--;
 911 +         }
 912 + 
 913 +         return (btime);
 914 + }
 915 + 
 916 + 
 917 + /*
 918 +  * Routine to convert the `pollstr' string to seconds
 919 +  */
 920 + int convert_polltime(char *pollstr) {
 921 +         char *tokenptr = NULL;
 922 +         int len, polltime;
 923 + 
 924 +         len = polltime = 0;
 925 + 
 926 +         if ((len = strcspn(pollstr, "s")) < strlen(pollstr)) {
 927 +                 tokenptr = malloc((len + 1) * sizeof(char));
 928 +                 (void) strlcpy(tokenptr, pollstr, len + 1);
 929 +                 polltime = atoi(tokenptr);
 930 +         }
 931 + 
 932 +         if ((len = strcspn(pollstr, "m")) < strlen(pollstr)) {
 933 +                 tokenptr = malloc((len + 1) * sizeof(char));
 934 +                 (void) strlcpy(tokenptr, pollstr, len + 1);
 935 +                 polltime = atoi(tokenptr) * 60;
 936 +         }
 937 + 
 938 +         if ((len = strcspn(pollstr, "h")) < strlen(pollstr)) {
 939 +                 tokenptr = malloc((len + 1) * sizeof(char));
 940 +                 (void) strlcpy(tokenptr, pollstr, len + 1);
 941 +                 polltime = atoi(tokenptr) * 3600;
 942 +         }
 943 + 
 944 +         if (tokenptr != NULL)
 945 +                 free(tokenptr);
 946 +         /*
 947 +          * If we have a bogus pollstr value, set polltime to the
 948 +          * default of 2 mts (120 seconds).
 949 +          */
 950 +         if (polltime == 0)
 951 +                 polltime = 120;
 952 +         return (polltime);
 953 + }
 954 + 
 955   static void
 956   kpropd_com_err_proc(whoami, code, fmt, args)
 957           const char        *whoami;
 958           long                code;
 959           const char        *fmt;
 960           va_list                args;
 961   {
 962           char        error_buf[8096];
 963   
 964           error_buf[0] = '\0';
 965           if (fmt)
 966                   vsprintf(error_buf, fmt, args);
 967           syslog(LOG_ERR, "%s%s%s%s%s", whoami ? whoami : "", whoami ? ": " : "",
 968                  code ? error_message(code) : "", code ? " " : "", error_buf);
 969   }
 970   
 971   void PRS(argc,argv)
 972           int        argc;
 973           char        **argv;
 974   {
 975           register char        *word, ch;
 976           char        *cp;
 977           int c;
 978           struct hostent *hp;
 979           char        my_host_name[MAXHOSTNAMELEN], buf[BUFSIZ];
 980           krb5_error_code        retval;
 480 -         
 981           static const char        tmp[] = ".temp";
 982           kadm5_config_params        params;
 983 + 
 984           (void) setlocale(LC_ALL, "");
 985   
 986   #if !defined(TEXT_DOMAIN)                /* Should be defined by cc -D */
 987   #define        TEXT_DOMAIN        "KPROPD_TEST"        /* Use this only if it weren't */
 988   #endif
 989   
 990           (void) textdomain(TEXT_DOMAIN);
 991   
 992           (void) memset((char *) &params, 0, sizeof (params));
 993   
 994           retval = krb5_init_context(&kpropd_context);
 995           if (retval) {
 996                   com_err(argv[0], retval, 
 997                           gettext("while initializing krb5"));
 998                   exit(1);
 999           }
1000           progname = argv[0];
 499 |         while ((c = getopt(argc, argv, "df:F:p:P:r:s:Sa:")) != EOF){
1001 |         while ((c = getopt(argc, argv, "dtf:F:p:P:r:s:Sa:")) != EOF){
1002                   switch (c) {
1003                   case 'd':
1004                           debug++;
1005                           break;
1006 +                 case 't':
1007 +                         /*
1008 +                          * Undocumented option - for testing only.
1009 +                          *
1010 +                          * Option to run the kpropd server exactly
1011 +                          * once (this is true only if iprop is enabled).
1012 +                          */
1013 +                         runonce = B_TRUE;
1014 +                         break;
1015 + 
1016                   case 'f':
1017                           file = optarg;
1018                           if (!file)
1019                                   usage();
1020                           break;
1021                   case 'F':
1022                           kerb_database = optarg;
1023                           if (!kerb_database)
1024                                   usage();
1025                           break;
1026                   case 'p':
1027                           kdb5_util = optarg;
1028                           if (!kdb5_util)
1029                                   usage();
1030                           break;
1031                   case 'P':
1032                           port = htons(atoi(optarg));
1033                           if (!port)
1034                                   usage();
1035                           break;
1036                   case 'r':
1037                           realm = optarg;
1038                           if (!realm)
 528 -                                         break;
 529 -                                 case 's':
1039                                   usage();
1040                           params.realm = realm;
1041                           params.mask |= KADM5_CONFIG_REALM;
1042 +                         break;
1043 +                 case 's':
1044                           srvtab = optarg; 
1045                           if (!srvtab)
1046                                   usage();
1047                           break;
1048                   case 'S':
1049                           standalone++;
1050                           break;
1051                   case 'a':
1052                           acl_file_name = optarg;
1053                           if (!acl_file_name)
1054                                   usage();
1055                           break;
1056                   case '?':
1057                                   default:
1058                                           usage();
1059                                   }
1060                                   
1061                           }
1062           /*
1063            * If not in debug mode, switch com_err reporting to syslog
1064            */
1065           if (! debug) {
1066               openlog("kpropd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS);
1067               set_com_err_hook(kpropd_com_err_proc);
1068           }
1069           /*
1070            * Get my hostname, so we can construct my service name
1071            */
1072           retval = krb5_sname_to_principal(kpropd_context,
1073                                            NULL, KPROP_SERVICE_NAME,
1074                                            KRB5_NT_SRV_HST, &server);
1075           if (retval) {
1076                   com_err(progname, retval,
1077                           gettext("While trying to construct my service name"));
1078                   exit(1);
1079           }
1080           if (realm) {
1081               (void) krb5_xfree(krb5_princ_realm(context, server)->data);
1082               krb5_princ_set_realm_length(context, server, strlen(realm));
1083               krb5_princ_set_realm_data(context, server, strdup(realm));
1084           }
1085           /*
1086            * Construct the name of the temporary file.
1087            */
1088           if ((temp_file_name = (char *) malloc(strlen(file) +
1089                                                  strlen(tmp) + 1)) == NULL) {
1090                   com_err(progname, ENOMEM,
1091                           gettext("while allocating filename for temp file"));
1092                   exit(1);
1093           }
1094           strcpy(temp_file_name, file);
1095           strcat(temp_file_name, tmp);
1096 + 
1097 +         retval = kadm5_get_config_params(kpropd_context, NULL, NULL, &params,
1098 +             &params);
1099 +         if (retval) {
1100 +                 com_err(progname, retval, gettext("while initializing"));
1101 +                 exit(1);
1102           }
1103 +         if (params.iprop_enabled == TRUE) {
1104 +                 ulog_set_role(kpropd_context, IPROP_SLAVE);
1105 +                 poll_time = params.iprop_polltime;
1106   
1107 +                 if (ulog_map(kpropd_context, &params, FKPROPD)) { 
1108 +                          com_err(progname, errno,
1109 +                             gettext("Unable to map log!\n")); 
1110 +                         exit(1); 
1111 +                 } 
1112 +         }
1113 + 
1114           /*
1115 +          * Grab the realm info and check if iprop is enabled.
1116 +          */
1117 +         if (def_realm == NULL) {
1118 +                 retval = krb5_get_default_realm(kpropd_context, &def_realm);
1119 +                 if (retval) {
1120 +                         com_err(progname, retval,
1121 +                                 gettext("Unable to get default realm"));
1122 +                         exit(1);
1123 +                 }
1124 +         }
1125 + }
1126 + 
1127 + /*
1128    * Figure out who's calling on the other end of the connection....
1129    */
1130   void
1131   kerberos_authenticate(context, fd, clientp, etype, ss)
1132       krb5_context           context;
1133       int                           fd;
1134       krb5_principal        * clientp;
1135       krb5_enctype        * etype;
1136       struct sockaddr_storage          ss;
1137   {
1138       krb5_error_code          retval;
1139       krb5_ticket                * ticket;
1140       struct sockaddr_storage          r_ss;
1141       int                          ss_length;
1142       krb5_keytab                  keytab = NULL;
1143   
1144       /*
1145        * Set recv_addr and send_addr
1146        */
1147       if (cvtkaddr(&ss, &sender_addr) == NULL) {
1148           com_err(progname, errno, 
1149                   gettext("while converting socket address"));
1150           exit(1);
1151       }
1152   
1153       ss_length = sizeof (r_ss);
1154       if (getsockname(fd, (struct sockaddr *) &r_ss, &ss_length)) {
1155           com_err(progname, errno, 
1156                   gettext("while getting local socket address"));
1157           exit(1);
1158       }
1159   
1160       if (cvtkaddr(&r_ss, &receiver_addr) == NULL) {
1161           com_err(progname, errno, 
1162                   gettext("while converting socket address"));
1163           exit(1);
1164       }
1165   
1166       if (debug) {
1167           char *name;
1168           if (retval = krb5_unparse_name(context, server, &name)) {
1169               com_err(progname, retval, gettext("While unparsing server name"));
1170               exit(1);
1171           }
1172           printf(gettext("krb5_recvauth(%d, %s, %s, ...)\n"), fd, kprop_version,
1173               name);
1174           free(name);
1175       }
1176   
1177       if (retval = krb5_auth_con_init(context, &auth_context)) {
1178           syslog(LOG_ERR, gettext("Error in krb5_auth_con_init: %s"),
1179               error_message(retval));
1180               exit(1);
1181       }
1182   
1183       if (retval = krb5_auth_con_setflags(context, auth_context, 
1184                                           KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
1185           syslog(LOG_ERR, gettext("Error in krb5_auth_con_setflags: %s"),
1186                  error_message(retval));
1187           exit(1);
1188       }
1189   
1190       if (retval = krb5_auth_con_setaddrs(context, auth_context, &receiver_addr,
1191                                           &sender_addr)) {
1192           syslog(LOG_ERR, gettext("Error in krb5_auth_con_setaddrs: %s"),
1193                  error_message(retval));
1194           exit(1);
1195       }
1196   
1197       if (srvtab) {
1198           if (retval = krb5_kt_resolve(context, srvtab, &keytab)) {
1199             syslog(LOG_ERR, gettext("Error in krb5_kt_resolve: %s"), error_message(retval));
1200             exit(1);
1201           }
1202       }
1203   
1204       if (retval = krb5_recvauth(context, &auth_context, (void *) &fd,
1205                                  kprop_version, server, 0, keytab, &ticket)){
1206           syslog(LOG_ERR, gettext("Error in krb5_recvauth: %s"),
1207               error_message(retval));
1208           exit(1);
1209       }
1210   
1211       if (retval = krb5_copy_principal(context, 
1212                                        ticket->enc_part2->client, clientp)) {
1213           syslog(LOG_ERR, gettext("Error in krb5_copy_prinicpal: %s"), 
1214                  error_message(retval));
1215           exit(1);
1216       }
1217   
1218       *etype = ticket->enc_part.enctype;
1219   
1220       if (debug) {
1221           char * name;
1222           char etypebuf[100];
1223   
1224           if (retval = krb5_unparse_name(context, *clientp, &name)) {
1225               com_err(progname, retval, 
1226                   gettext("While unparsing client name"));
1227               exit(1);
1228           }
1229   
1230           if (retval = krb5_enctype_to_string(*etype, etypebuf,
1231                                               sizeof(etypebuf))) {
1232               com_err(progname, retval, gettext("While unparsing ticket etype"));
1233               exit(1);
1234           }
1235   
1236           printf("authenticated client: %s (etype == %s)\n", name, etypebuf);
1237           free(name);
1238       }
1239   
1240       krb5_free_ticket(context, ticket);
1241   }

 ----Unchanged portion omitted----

1488   
1489   void
1490   load_database(context, kdb5_util, database_file_name)
1491       krb5_context context;
1492       char *kdb5_util;
1493       char *database_file_name;
1494   {
1495           static char        *edit_av[10];
1496           int        error_ret, save_stderr;
1497           int        child_pid;
1498           int         count;
1499           int        waitb;
1500           krb5_error_code        retval;
1501 +         kdb_log_context        *log_ctx;
1502   
1503           if (debug)
1504                   printf(gettext("calling kdb5_util to load database\n"));
1505   
1506 +         log_ctx = context->kdblog_context;
1507 + 
1508           edit_av[0] = kdb5_util;
1509           count = 1;
1510           if (realm) {
1511                   edit_av[count++] = "-r";        
1512                   edit_av[count++] = realm;        
1513           }
1514           edit_av[count++] = "load";
1515           if (kerb_database) {
1516                   edit_av[count++] = "-d";
1517                   edit_av[count++] = kerb_database;
1518           }
1519 + 
1520 +         if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
1521 +                 edit_av[count++] = "-i";
1522 +         }
1523           edit_av[count++] = database_file_name;
1524           edit_av[count++] = NULL;
1525   
1526           switch(child_pid = fork()) {
1527           case -1:
1528                   com_err(progname, errno, gettext("while trying to fork %s"),
1529                           kdb5_util);
1530                   exit(1);
1531                   /*NOTREACHED*/
1532           case 0:
1533                   if (!debug) {
1534                           save_stderr = dup(2);
1535                           close(0);
1536                           close(1);
1537                           close(2);
1538                           open("/dev/null", O_RDWR);
1539                           dup(0);
1540                           dup(0);
1541                   }
1542   
1543                   execv(kdb5_util, edit_av);
1544                   retval = errno;
1545                   if (!debug)
1546                           dup2(save_stderr, 2);
1547                   com_err(progname, retval, gettext("while trying to exec %s"),
1548                           kdb5_util);
1549                   _exit(1);
1550                   /*NOTREACHED*/
1551           default:
1552                   if (debug)
1553                       printf(gettext("Child PID is %d\n"), child_pid);
1554                   if (wait(&waitb) < 0) {
1555                           com_err(progname, errno, gettext("while waiting for %s"),
1556                                   kdb5_util);
1557                           exit(1);
1558                   }
1559           }
1560           
1561           if ((error_ret = WEXITSTATUS(waitb)) != 0) {
1562                   com_err(progname, 0,
1563                       gettext("%s returned a bad exit status (%d)"), kdb5_util,
1564                       error_ret);
1565                   exit(1);
1566           }
1567           return;
1568   }

 ----Unchanged portion omitted----