/* @(#)00        1.4  src/examples/pwdstren/pwd_strengthd.c, examples.src, os2dce21.dss, 960602a.1  2/26/96  13:27:32                                          */
/*
 * COMPONENT_NAME:  security.src
 *
 * FUNCTIONS:
 *
 * ORIGINS: 72
 *
 * OBJECT CODE ONLY SOURCE MATERIALS
 *
 */
/*
 * Copyright (c) 1990, 1991, 1992, 1993, 1994 Open Software Foundation, Inc.
 * ALL RIGHTS RESERVED (DCE).  See the file named COPYRIGHT.DCE for
 * the full copyright text.
 */
/*
 * HISTORY
 * $Log: pwd_strengthd.c,v $
 * Revision 1.1.8.1  1994/10/25  20:04:31  aha
 *      CR12184: export binding information to /.:/subsys/dce/pwd_mgmt/pwd_strength.
 *      [1994/10/25  20:02:28  aha]
 *
 * Revision 1.1.2.8  1994/10/25  17:17:35  aha
 *      CR12184: export binding information to /.:/subsys/dce/pwd_mgmt/pwd_strength.
 *      [1994/10/25  17:13:10  aha]
 *
 * Revision 1.1.2.7  1994/09/09  20:41:05  kline_s
 *      OT 12028: I18N code cleanup for Security.
 *      [1994/09/09  20:30:37  kline_s]
 *
 * Revision 1.1.2.6  1994/08/31  16:06:49  mullan_s
 *      [OT11775] Initialize contents of gen_info_out, str_info_out.
 *      [1994/08/31  16:06:19  mullan_s]
 *
 * Revision 1.1.2.5  1994/08/19  17:59:22  mdf
 *      Bug fixes from Hewlett Packard
 *      [1994/08/19  13:10:48  mdf]
 *
 * Revision 1.1.3.5  94/08/18  17:13:05  mullan_s
 *      [OT 11634] On strength check, do not check password cache if pwd_val_type=2.
 *
 * Revision 1.1.3.4  1994/08/18  19:36:53  mullan_s
 *      Make log file read/write only by root.
 *
 * Revision 1.1.3.3  1994/08/18  16:53:44  mullan_s
 *      Remove references to pwd_checkd, support options in pwd_strengthd.
 *
 * Revision 1.1.3.2  1994/08/15  17:38:11  mullan_s
 *      [OT11657] Fix pwd_strengthd crash when running in background.
 *      Also allow pwd_strengthd to be started remotely via remsh by
 *      redirecting stdin, stdout, & stderr to /dev/null.
 *
 * Revision 1.1.2.3  1994/07/15  15:01:00  mdf
 *      Hewlett Packard Security Code Drop
 *      [1994/07/14  17:18:34  mdf]
 *
 * Revision 1.1.2.2  1994/06/17  18:42:42  devsrc
 *      cr10872 - fix copyright
 *      [1994/06/17  18:14:19  devsrc]
 *
 * Revision 1.1.2.1  1994/05/11  19:26:31  ahop
 *      move PWD_STRENGTHD_LOG to sec_login_file_loc.h
 *      hp_sec_to_osf_2 drop
 *      Password strength server
 *      [1994/05/06  20:52:42  ahop]
 *
 *      hp_sec_to_osf_2 drop
 *      [1994/04/29  21:26:52  ahop]
 *
 * $EndLog$
 */
/*
**
** Copyright (c) Hewlett-Packard Company 1994
** Unpublished work. All Rights Reserved.
**
*/
/*
 * Password Strength server
 *
 *   NOTE NOTE NOTE
 *   NOTE NOTE NOTE
 *
 *      No attempt is made to use the #if statements specifying both AIX_PROD
 *      and IBMOS2 any longer, just to be able to see what the OSF original
 *      source looked like.  Such #if statements only led to more difficult
 *      code readability.  The only situations where such #if statements
 *      remain in this code are:
 *
 *        - "messaging" changes, where the #else shows, at least roughly,
 *            what message text is involved, which has its advantages over
 *            having to look at the sad.sams file for message contents.
 *
 *        - any location where an IBM DCE change has functional design
 *            significance as compared to the OSF DCE original.  These will
 *            be very very few.
 *
 *      Significant reorganization from the OSF DCE original has taken place,
 *      and is no longer marked by #if.  Highlights of this category of change
 *      would be server registration/unregistration, and certain details of
 *      the error() and message() utilities.
 *
 */

/*  Common source code for initial strength server, as built in the DCE tree
    for both OS/2 and AIX, and for the OS/2 DCE customer strength server
    example, which is built a little differently (with the OS2_EXAMPLE
    compilation variable turned on.)
*/

/*
 * CMVC 18549 name changes:
 *
 *   - principal name for primary server (AIX & OS/2) is now "pwdstred"
 *   - principal name for secondary server, built by customer, is "pwdstrn2"
 *   - no changes to existing object UUID values
 *   - keytable file name equals principal name.  FILE: and C: removed from
 *           the example's define since the full name now built in realtime
 *   - log file name equals principal name, with ".log" appended.  C: removed
 *        from the example's define since location now computed in realtime.
 *   - program name now obtained in proper manner, via argv[0]
 *
 */
#ifdef OS2_EXAMPLE

/*   CUSTOMER EXAMPLE STRENGTH SERVER FOR OS/2   */
/*   Customers can tailor the following parameters for their unique needs. */
/*   The only other location in code that a customer would want to alter is
     the actual composition check.  Search for string "CUSTOMER COMPOSITION"
     to see that location in the code.                                     */
#define SERVER_PNAME  "pwdstrn2"     /* service principal name */
        /* NOTE: use principal name <= 8 characters if server run on FAT fs */
#define OBJUUID "73724ea0-638e-11ce-81d0-10005a8d49d4"  /* object uuid */
#define KEYTABNAME "/krb5/" SERVER_PNAME   /* keytable file name */
#define PWD_STRENGTHD_LOG  "/var/security/" SERVER_PNAME ".log"  /* log name */
#undef OS2_FATMAP      /* ensure this flag is off for customer example ! */

#else   /* not OS/2 example code */

/*   DCE-BUILT-AND-DELIVERED STRENGTH SERVER (FOR EITHER OS/2 OR AIX)  */
#define SERVER_PNAME  "pwdstred"
#define OBJUUID "5e234a40-638e-11ce-89ff-10005a8d49d4"
#ifdef IBMOS2

/*   OS/2 DCE STRENGTH SERVER PARMS */
#ifdef DCELOCAL_PATH
#undef DCELOCAL_PATH
#endif
#define DCELOCAL_PATH  "/opt/dcelocal"
/* Following two strings supplied as input to mapname(), to get real prefix */
#define KEYTABNAME DCELOCAL_PATH "/krb5/" SERVER_PNAME
#define PWD_STRENGTHD_LOG  DCELOCAL_PATH "/var/security/" SERVER_PNAME ".log"
#define DCESTARTSTOP     /* OS/2 DCE provided server participates in the
                            dceos2_set_process_state() function for the
                            benefit of the dcestart and dcestop utilities. */

#else  /* AIX_PROD */

/*   AIX DCE STRENGTH SERVER PARMS  */
#define KEYTABNAME "FILE:/krb5/" SERVER_PNAME
#define PWD_STRENGTHD_LOG  DCELOCAL_PATH "/var/security/" SERVER_PNAME ".log"

#endif   /* IBMOS2 */
#endif   /* OS2_EXAMPLE */

/* Common definition for CDS namespace.  OS/2 DCE customer example might
    choose to change this, so long as GUI not used for server configuration */
#define CDSPATH "/.:/subsys/dce/pwd_mgmt/" SERVER_PNAME  /* export bindings */

/*
 * NOTE ON PROGRAM TERMINATION AND DEREGISTRATION:
 *
 *   Both OS/2 and AIX have been modified to:
 *      - call routine strength_unregister() to do most exit cleanup.
 *            One reason for isolating this into a separate routine is to
 *            allow OS/2 DCE an option as to how and where deregistration
 *            occurs.
 *      - unexport information in the CDS namespace during init; in the
 *            event that a strength server is to be located to a different
 *            machine, this eases the transition.  (Server keytable must be
 *            moved manually, of course; but CDS information can be handled
 *            by code.)
 *      - unexport information in the CDS namespace during init, for the
 *            same reason....in case the strength server shutdown from the
 *            previous run did not go cleanly.
 *
 */

#ifdef IBMOS2
/* Use the "cds style" way of catching signals and terminating.  The essential
   features of this style are:

       - separate thread to do rpc_server_listen
       - use of mutex and condition variable between signal catcher and main
           thread
       - elimination of TRY/ENDTRY macros, etc.
*/
#define TERMINATE_CDS_STYLE
#endif

#include <fcntl.h>
#include <locale.h>
#ifdef OS2_EXAMPLE
#include <dce/pthread.h>
#include <dce/dcefree.h>
#else
#include <pthread.h>
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>

#ifdef IBMOS2
#ifdef OS2_EXAMPLE
#include <dce/utc.h>
#include <share.h>           /* for sopen() parameters */
#include <io.h>              /* to convert sopen to C/Set's _sopen */
#if defined (__BORLANDC__)
# define _System __syscall
#endif
#include <sys/time.h>                    /* for gettimeofday()    */
#else
#include <sys/time.h>                    /* for gettimeofday()    */
#endif
#ifdef DCESTARTSTOP
#include <dceos2.h>
#endif
#endif /* IBMOS2 */

#include <dce/dce.h>
#include <dce/dce_cf.h>
#include <dce/dce_error.h>
#include <dce/id_base.h>
#include <dce/keymgmt.h>
#include <dce/rgynbase.h>
#include <dce/rpc.h>
#ifdef OS2_EXAMPLE
#include "rsec_pwd_mgmt.h"
#else
#include <dce/rsec_pwd_mgmt.h>
#endif
#include <dce/sec_login.h>

#ifdef OS2_EXAMPLE
#include "gen.h"
#include "pwd_cache.h"
#else
#include <gen.h>
#include <pwd_cache.h>
#endif

#include <dce/dce_msg.h>
#include <dce/dcesadmsg.h>
unsigned char   *ibm_msg_p;

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 15955 */
#include <dce/dceibsmsg.h>
#include <dce/dceibssvc.h>      /* CMVC 18710 */
#include <dce/dcesvcmsg.h>      /* CMVC 18710 */
#include <dce/sec_nls.h>        /* CMVC 16603, msg separation */
#endif /* CMVC 15955 */

#include <dce/uuid.h>

#include <dce/dce_utils.h>
#include <ctype.h>
extern void strength_unregister(void);

#define NOT_USED 0xffffffff

/*
 * global variables
 */

char                    PWD_PROGNAME[25];   /* Breaks if .exe name absurdly
                                                  long.  Not worrying about. */
sec_login_handle_t      login_context;
int                     ten_minutes = 10*60;
boolean32  debug_mode   = false;
boolean32  verbose      = false;
int        pd_fd;
FILE       *pd_fp      = NULL;
signed32   ovrd_min_len;
boolean32  ovrd_len     = false;
boolean32  ovrd_all_spaces = NOT_USED;
boolean32  ovrd_alpha_num  = NOT_USED;
char       *server_name;
signed32   cache_size      = 100; /* default cache size for password cache */
signed32   timeout         = 1800; /* maximum number of seconds a generated password
                                      should be cached before flushing from memory */
pthread_mutex_t cache_mutex;  /* Mutex lock used when writing to cache */

enum { none, user_select, user_can_select, generation_required };

#ifdef IBMOS2
boolean32  port_test = false;             /* extra debug to validate port */
#endif

/* These globals necessary in order to support termination functions from
 * subroutine strength_unregister(). */
    rpc_binding_vector_p_t bindings;
    char *cds_path = CDSPATH;
    char           *obj_id_str = OBJUUID;
    uuid_t                  obj_id;
    uuid_vector_t           obj_uuids;

#ifdef IBMOS2
/* Other persistent data */
static boolean32 sig_in_progress = false;
#ifdef TERMINATE_CDS_STYLE
static pthread_mutex_t strength_sig_mutex;
static pthread_cond_t  strength_sig_cond;
static sigset_t        strength_sig_block;
static int             strength_sig_init = 0;
#endif
#endif

#ifdef IBMOS2  /* CMVC 18549: need this for OS/2 example, too. */
/* Lift MAXPATHLEN define from sys/param.h (lost this now that we don't   */
/* include sec_login_file_loc.h - CMVC 11253)                             */
#define    MAXPATHLEN 260
#endif  /* IBMOS2 */
#ifdef OS2_FATMAP
char       mapoutput[MAXPATHLEN];
char       *mappedname;
/* Name of key table associated with this server, for use in several routines */
char       *inkeytabnm = KEYTABNAME;
#endif  /* OS2_FATMAP */

#ifdef IBMOS2
/* CMVC 18559: storage of DCELOCAL-based names for OS/2 example too */
char       fullkeytabnm[MAXPATHLEN + 6];
char       pwdlognm[MAXPATHLEN];
#endif  /* IBMOS2 */

/* maximum number of passwords server will generate per request */
#define MAX_PWDS     10

/*
 * error messaging routine
 */
void error (
    error_status_t  *st,
    char            *msg
)
{
    dce_error_string_t  err_string;
    int                 lst;
    char                tbuf[128];
#ifdef OS2_EXAMPLE
    utc_t       utc_buf;
    timespec_t  time_component;
    timespec_t  inacc_component;
    long        tdf;
#else
    struct timeval      now;
    struct timezone     tz;
#endif
    struct tm           time_info;
    FILE                *efp = NULL;
    FILE                *efp2 = NULL;
    time_t      now_in_seconds;

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
    char                ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

    /* errors go to screen and log, always.  "It's the law".    */
    efp = pd_fp;
    efp2 = stderr;

#ifdef OS2_EXAMPLE
    if (utc_gettime(&utc_buf) == -1 ||
        utc_bintime(&time_component,&inacc_component,&tdf,&utc_buf)==-1) {
       ibs_get_msg(ibs_msg_buf,ibm_msg_1045);   /* CMVC 16603 & 17768 */
       strcpy(tbuf, ibs_msg_buf);               /* CMVC 16603 & 17768 */
    } else {
        now_in_seconds = time_component.tv_sec;
#else
#ifdef SNI_SVR4_POSIX
    if (gettimeofday(&now) == -1) {
#else  /* not SNI_SVR4_POSIX */
    if (gettimeofday(&now, &tz) == -1) {
#endif /* SNI_SVR4_POSIX */
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
       ibs_get_msg(ibs_msg_buf,ibm_msg_1046);
       strcpy(tbuf, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
       strcpy(tbuf, "<Time Unavailable (gettimeofday)>");
#endif /* CMVC 16603 */
    } else {
       now_in_seconds = (time_t)now.tv_sec;
#endif  /* OS2_EXAMPLE */

#if defined(AIX_PROD) && defined(_POSIX_REENTRANT_FUNCTIONS)
    if (localtime_r((time_t *) &now_in_seconds, &time_info) != 0)
    {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1047);  /* CMVC 16603 */
        strcpy(tbuf, ibs_msg_buf);              /* CMVC 16603 */
    }
    else
#else
    time_info = *localtime((time_t *) &now_in_seconds);
#endif /* defined(AIX_PROD) && defined(_POSIX_REENTRANT_FUNCTIONS) */
#ifdef IBMOS2
    strftime(tbuf, sizeof(tbuf), "%x %H:%M:%S", &time_info);
#else
    strftime(tbuf, sizeof(tbuf), "%x %X", &time_info);
#endif
    if (tbuf == NULL) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
        ibs_get_msg(ibs_msg_buf,ibm_msg_1048);
        strcpy(tbuf, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
        strcpy(tbuf, "<Time Unavailable (strftime)>");
#endif /* CMVC 16603 */
    }
  }  /* end if gettimeofday/utc_gettime failed */

    /* Now actually output the error message.  It only makes sense to have
        such data go to both screen and log, always.  */
    if ((st != NULL) && (*st != error_status_ok)) {
       dce_error_inq_text(*st, err_string, &lst);
       if (lst != error_status_ok) {
          /* CMVC 14567 & 13302 */
          dce_fprintf(efp, ibm_msg_404, server_name, tbuf, msg, *st);
          dce_fprintf(efp2, ibm_msg_404, server_name, tbuf, msg, *st);
        } else {
          fprintf(efp, "%s %s - %s\n... %s\n", server_name, tbuf, msg,
                    err_string);
          fprintf(efp2, "%s %s - %s\n... %s\n", server_name, tbuf, msg,
                    err_string);
        }
    } else {
       fprintf(efp, "%s %s - %s\n", server_name, tbuf, msg);
       fprintf(efp2, "%s %s - %s\n", server_name, tbuf, msg);
    } /* end if need to translate DCE status code to a message string */
}

/*
 * Generic messaging routine.
 */
static void message (
    char *msg
)
{
    char                tbuf[128];
#ifdef OS2_EXAMPLE
    utc_t       utc_buf;
    timespec_t  time_component;
    timespec_t  inacc_component;
    long        tdf;
#else
    struct timeval      now;
    struct timezone     tz;
#endif
    struct tm           time_info;
    FILE                *mfp = NULL;
    time_t      now_in_seconds;

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
    char                ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

    if (!debug_mode)
        mfp = pd_fp;
    else
        mfp = stdout;

#ifdef OS2_EXAMPLE
    if (utc_getusertime(&utc_buf) == -1 ||
        utc_bintime(&time_component,&inacc_component,&tdf,&utc_buf)==-1) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1049);  /* CMVC 16603 & 17768 */
        strcpy(tbuf, ibs_msg_buf);              /* CMVC 16603 & 17768 */
    } else {
        now_in_seconds = time_component.tv_sec;
#else
#ifdef SNI_SVR4_POSIX
    if (gettimeofday(&now) == -1) {
#else  /* not SNI_SVR4_POSIX */
    if (gettimeofday(&now, &tz) == -1) {
#endif /* SNI_SVR4_POSIX */
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
       ibs_get_msg(ibs_msg_buf,ibm_msg_1046);
       strcpy(tbuf, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
       strcpy(tbuf, "<Time Unavailable (gettimeofday)>");
#endif /* CMVC 16603 */
    } else {
       now_in_seconds = (time_t)now.tv_sec;
#endif  /* OS2_EXAMPLE */

#if defined(AIX_PROD) && defined(_POSIX_REENTRANT_FUNCTIONS)
    if (localtime_r((time_t *) &now_in_seconds, &time_info) != 0)
    {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1047);  /* CMVC 16603 */
        strcpy(tbuf, ibs_msg_buf);              /* CMVC 16603 */
    }
    else
#else
    time_info = *localtime((time_t *) &now_in_seconds);
#endif /* defined(AIX_PROD) && defined(_POSIX_REENTRANT_FUNCTIONS) */
#ifdef IBMOS2
    strftime(tbuf, sizeof(tbuf), "%x %H:%M:%S", &time_info);
#else
    strftime(tbuf, sizeof(tbuf), "%x %X", &time_info);
#endif
    if (tbuf == NULL) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
        ibs_get_msg(ibs_msg_buf,ibm_msg_1048);
        strcpy(tbuf, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
        strcpy(tbuf, "<Time Unavailable (strftime)>");
#endif /* CMVC 16603 */
    }
  }  /* end if gettimeofday/utc_gettime failed */

    fprintf(mfp, "%s %s - %s\n", server_name, tbuf, msg);
}

/* matches command line arguments */
static boolean32 match_arg (
    char            *key,
    char            *arg,
    int             min
)
{
    int i = 0;

    if (*key) while (*key == *arg) {
        i++;
        key++;
        arg++;
        if (*arg == '\0' || *key == '\0')
            break;
    }
    if (*arg == '\0' && i >= min)
        return true;
    return false;
}

/*
 * Parses command line arguments.
 */
static boolean32 process_args (
    int             argc,
    char            *argv[]
)
{
    int             i;


    /* determine program name */
    server_name = strrchr(argv[0], '/');
    if (server_name == NULL) {
        server_name = argv[0];
    } else {
        server_name++;
    }
#ifdef IBMOS2
/* correct for os/2 separator and extension */
    if (strrchr(server_name, '\\'))
        server_name = strrchr(server_name, '\\') + 1;
    if (strchr(server_name, '.'))
      *strchr(server_name, '.') = '\0';
#endif  /* IBMOS2 */

    for (i = 1; i < argc; i++) {
        if (match_arg("-verbose", argv[i], 2)) {
            verbose = true;
        } else if (match_arg("-debug", argv[i], 2)) {
            debug_mode = true;
        } else if (match_arg("-min_len", argv[i], 2)) {
            /* get value! */
            if (++i == argc) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 13302, msg separation */
                dce_fprintf(stderr, ibm_msg_608);
#else /* CMVC 13302, msg separation */
                fprintf(stderr, "-m[in_len] option requires an argument\n");
#endif /* CMVC 13302 */
                goto usage;
            }
            ovrd_min_len = atol(argv[i]);
            ovrd_len = true;
        } else if (match_arg("-all_spaces", argv[i], 4)) {
            ovrd_all_spaces = false;
        } else if (match_arg("+all_spaces", argv[i], 4)) {
            ovrd_all_spaces = true;
        } else if (match_arg("-alpha_num", argv[i], 4)) {
            ovrd_alpha_num = false;
        } else if (match_arg("+alpha_num", argv[i], 4)) {
            ovrd_alpha_num = true;
        } else if (match_arg("-cache_size", argv[i], 2)) {
            /* get value */
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 14567 & 13302, msg separation */
            if (++i == argc) {
                dce_fprintf(stderr, ibm_msg_609);
                goto usage;
            }
            cache_size = atol(argv[i]);
        } else if (match_arg("-timeout", argv[i], 2)) {
            /* get value */
            if (++i == argc) {
                dce_fprintf(stderr, ibm_msg_610);
                goto usage;
            }
            timeout = atol(argv[i]) * 60;
        } else {
            dce_fprintf(stderr, ibm_msg_406, argv[i]); /* CMVC 14567 */
#else /* CMVC 14567 & 13302, msg separation */
            if (++i == argc) {
                fprintf(stderr, "-c[ache_size] option requires an argument\n");
                goto usage;
            }
            cache_size = atol(argv[i]);
        } else if (match_arg("-timeout", argv[i], 2)) {
            /* get value */
            if (++i == argc) {
                fprintf(stderr, "-t[imeout] option requires an argument\n");
                goto usage;
            }
            timeout = atol(argv[i]) * 60;
        } else {
            fprintf(stderr, "Unknown command option %s\n", argv[i]);
#endif /* CMVC 14567 & 13302 */
            goto usage;
        }
    }

    return(true);

usage:
#ifdef IBMOS2
/* Usage message incorrectly implied that password min length, cache size,
 * and timeout minutes ALWAYS had to be supplied, due to bad bracketing.
 */
    dce_fprintf(stderr, ibm_msg_612, argv[0]); /* CMVC 14567 & 13302 */
    dce_fprintf(stderr, ibm_msg_613); /* CMVC 14567 & 13302 */
#else
#if defined(AIX_PROD) /* CMVC 14567 & 13302, msg separation */
    /* XXXXXX:  SUSPECTED BUG BUG  This original OSF usage message was
                incorrect in some details, and we only corrected stuff
                for OS/2.  */
    dce_fprintf(stderr, ibm_msg_614, argv[0]);
    dce_fprintf(stderr, ibm_msg_615);
#else /* CMVC 14567 & 13302, msg separation */
    fprintf(stderr, "Usage: %s [-m[in_len]] password_min_length [+/-all[_spaces]] ", argv[0]);
    fprintf(stderr, "[+/-alp[ha_num]] [-c[ache_size]] size [-d[ebug]] [-t[imeout]] minutes [-v[erbose]]\n");
#endif /* CMVC 14567 & 13302 */
#endif  /* IBMOS2 */
    return(false);
}


/* concatenates 3 substrings */
static void string_concat (
    char                    **buf,
    char                    *prefix,
    char                    *str,
    char                    *suffix
)
{
    int len1 = 0;
    int len2 = 0;
    int len3 = 0;
    int len;
    char *p;
    char *q;

    if (prefix) {
        len1 = strlen(prefix);
    }

    if (str) {
        len2 = strlen(str);
    }

    if (suffix) {
        len3 = strlen(suffix);
    }

    len = len1 + len2 + len3;

    p = malloc(len + 1);
    if (p) {
        q = p;
        strcpy(q, prefix);
        q += len1;
        strcpy(q, str);
        q += len2;
        strcpy(q, suffix);
    }

    *buf = p;
}


/*
 * check_auth_client ensures that the client is properly
 * authenticated and authorized.
 */
static void check_auth_client (
    handle_t                 handle,
    unsigned32               min_protect_level,
    unsigned32               *protect_level,
    unsigned32               exp_authn_svc,
    char                     *exp_client_princ,
    error_status_t           *stp
)
{
    rpc_authz_handle_t       privs;
    unsigned_char_p_t        svr_princ_name,
                             client_princ_full_name,
                             pwd_strength_princ_full_name,
                             cell_name;
    unsigned32               authn_svc,
                             authz_svc;
    error_status_t           lst;
    sec_rgy_name_t           client_princ;
    int                      len, clen;

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
    char                    ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

    /* get clients authorization and authentication information */
    rpc_binding_inq_auth_client(handle, &privs, &svr_princ_name,
        protect_level, &authn_svc, &authz_svc, stp);
    if (*stp != error_status_ok) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
        ibs_get_msg(ibs_msg_buf,ibm_msg_1053);
        error(stp, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
        error(stp, "rpc_binding_inq_auth_client failed");
#endif /* CMVC 16603 */
        *stp = sec_pwd_mgmt_svr_error;
        return;
    }

    /* XXX - How do we handle default protection levels,
       authn_svc, etc .. */

    /* check minimum required protection level */
    if (*protect_level < min_protect_level) {
        *stp = sec_pwd_mgmt_not_authorized;
        if (svr_princ_name)
            rpc_string_free(&svr_princ_name, &lst);
        return;
    }

    /* check expected authentication service */
    if (authn_svc != exp_authn_svc) {
        *stp = sec_pwd_mgmt_not_authorized;
        if (svr_princ_name)
            rpc_string_free(&svr_princ_name, &lst);
        return;
    }

    /*
     * check authorization service - will only accept
     * name based authorization. We do not accept
     * pac based authorization (authz_dce) because
     * this would involve a callback to secd to convert
     * the uuid stored in the pac to a name.
     */
    if (authz_svc != rpc_c_authz_name) {
        *stp = sec_pwd_mgmt_not_authorized;
        if (svr_princ_name)
            rpc_string_free(&svr_princ_name, &lst);
        return;
    }

    /* check expected server principal name */
    dce_cf_get_cell_name((char **) &cell_name, stp);
    if (*stp != error_status_ok) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
        ibs_get_msg(ibs_msg_buf,ibm_msg_1054);
        error(stp, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
        error(stp, "dce_cf_get_cell_name failed");
#endif /* CMVC 16603 */
        if (svr_princ_name)
            rpc_string_free(&svr_princ_name, &lst);
        *stp = sec_pwd_mgmt_svr_error;
        return;
    }
    /* Let's use the string definition for easier support of multiple
        servers being built using this source code. */
    string_concat((char **)&pwd_strength_princ_full_name,
                  (char *)cell_name, "/", SERVER_PNAME);
    if (strcmp((char *)svr_princ_name,
               (char *)pwd_strength_princ_full_name) != 0) {
        *stp = sec_pwd_mgmt_not_authorized;
        if (svr_princ_name)
            rpc_string_free(&svr_princ_name, &lst);
        free((char *)pwd_strength_princ_full_name);
        return;
    }
    free((char *)pwd_strength_princ_full_name);
    if (svr_princ_name)
        rpc_string_free(&svr_princ_name, &lst);

    /* check privs */
    strcpy((char *)client_princ, (char *)privs);

    /* check expected client principal */
    if ((*stp == error_status_ok) && exp_client_princ != NULL) {
        string_concat((char **)&client_princ_full_name,
                      (char *)cell_name, "/",
                      exp_client_princ);
        len = strlen((char *)client_princ_full_name);
        clen = strlen((char *)client_princ);
        if (! (clen == len && strcmp((char *)client_princ,
              (char *)client_princ_full_name) == 0)) {
            *stp = sec_pwd_mgmt_not_authorized;
        }
        free((char *)client_princ_full_name);
        dce_free((char *)cell_name);
    }
}


/*
 * check_plain_pwd checks a plaintext password
 * against the minimum length of a password and
 * the password text flags.
 */
static boolean32 check_plain_pwd (
    char                     *pwd_text,
    signed32                 pwd_textlen,
    signed32                 pwd_min_len,
    sec_rgy_plcy_pwd_flags_t pwd_flags
)
{
    boolean32    all_spaces = true,
                 alpha_num  = true;
    int          i;

    /* check if server is overriding default registry policy */
    if (ovrd_len)
        pwd_min_len = ovrd_min_len;

    if (pwd_textlen < pwd_min_len)
        return false;

    /* step through each character in the passwd_text looking for a non-space
     * or a non-alpha-numeric character and set the value of all_spaces
     * and alpha_num based on what we find
     */
    for (i = 0; i < pwd_textlen && (all_spaces || alpha_num);
         i++, pwd_text++ ) {
        if (!isdcepcs(*pwd_text)) {
            /* not ascii ==> not blank or alpha-num */
            alpha_num = false;
            all_spaces = false;
        } else {
            if (!isalnum(*pwd_text)) {
                /* not alpha-num but might be blank */
                alpha_num = false;
                if (!(*pwd_text == ' ')) /* nope, not a blank either */
                    all_spaces = false;
            } else                    /* is alpha-num ==> not a blank */
                all_spaces = false;
        }
    }

    if (ovrd_alpha_num != NOT_USED) {
        if (alpha_num && !ovrd_alpha_num)
            return false;
    } else {
        if (((pwd_flags & sec_rgy_plcy_pwd_non_alpha) != 0)
            && (alpha_num))
            return false;
    }

    if (ovrd_all_spaces != NOT_USED) {
        if (all_spaces && !ovrd_all_spaces)
            return false;
    } else {
        if (((pwd_flags & sec_rgy_plcy_pwd_no_spaces) != 0)
            && (all_spaces))
            return false;
    }

    return true;
}


void DCEAPI rsec_pwd_mgmt_gen_pwd (
    handle_t                 pwd_mgmt_svr_h,           /* [in]  */
    sec_rgy_name_t           princ_name,               /* [in]  */
    unsigned32               plcy_args,                /* [in]  */
    sec_attr_t               plcy[],                   /* [in]  */
    sec_bytes_t              gen_info_in,              /* [in]  */
    unsigned32               num_pwds,                 /* [in]  */
    unsigned32               *num_returned,            /* [out] */
    sec_passwd_rec_t         gen_pwd_set[],            /* [out] */
    sec_bytes_t              *gen_info_out,            /* [out] */
    error_status_t           *stp                      /* [out] */
)
{
    char buf[sec_rgy_name_t_size + 100];
    unsigned32               protect_level;
    int                      x;
#ifdef IBMOS2
    dce_error_string_t  err_string;
    int                 lst;
#endif

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
    char                    ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

    *stp = error_status_ok;
    *num_returned = 0;
    gen_info_out->num_bytes = 0;
    gen_info_out->bytes = NULL;

    if (verbose) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 13302, msg separation */
        /* CMVC 14843 & 15039 */
        ibm_msg_p=(unsigned char *)dce_sprintf(ibm_msg_616, princ_name);
        if (ibm_msg_p) {
            sprintf(buf,"%s",(char *)ibm_msg_p);
            dce_free(ibm_msg_p);
        }
        else    dce_printf(MsgDce_sprintfCallFailed);
#else /* CMVC 13302, msg separation */
        sprintf(buf, "rsec_pwd_mgmt_gen_pwd request: princ- %s", princ_name);
#endif /* CMVC 13302 */
        message(buf);
    }

    /*
     * Check authorization and authentication of client
     * NOTE : We do not care who is making the call to the
     * generator, thus the NULL argument for the principal.
     * XXX - However, we may want to limit the number of
     * times a principal can call the generator successively.
     * (except if cell_admin).
     */
    check_auth_client(pwd_mgmt_svr_h, rpc_c_protect_level_pkt_integ,
        &protect_level, rpc_c_authn_dce_secret, NULL, stp);
    if (*stp != error_status_ok) {
#ifdef IBMOS2
        if (port_test) {
           dce_error_inq_text(*stp, err_string, &lst);
           dce_fprintf(stderr, ibm_msg_617, err_string); /* 14567 & 13302 */
        }
#endif /* IBMOS2 */
        return;
    }

    /*
     * generate password(s)
     */
    for (x = 0; x < num_pwds && x < MAX_PWDS; x++) {
        gen_pwd_set[x].version_number = 0;
        gen_pwd_set[x].pepper = NULL;
        gen_pwd_set[x].key.key_type = sec_passwd_plain;
        generate_pwd(plcy_args, plcy,
                     &gen_pwd_set[x].key.tagged_union.plain, stp);
        if (*stp != error_status_ok) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
            ibs_get_msg(ibs_msg_buf,ibm_msg_1055);
            error(stp, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
            error(stp, "Unable to generate password");
#endif /* CMVC 16603 */
            *stp = sec_pwd_mgmt_svr_error;
            break;
        }
        (*num_returned)++;
    }

    /*
     * cache generated passwords
     */
    if (*stp == error_status_ok) {
        create_record((char *) princ_name, *num_returned,
                      gen_pwd_set, stp);
        if (*stp != error_status_ok) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
            ibs_get_msg(ibs_msg_buf,ibm_msg_1056);
            error(stp, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
            error(stp, "Unable to create record");
#endif /* CMVC 16603 */
            *stp = sec_pwd_mgmt_svr_error;
        }
    }

    if (verbose && (*stp == error_status_ok)) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 13302, msg separation */
        /* CMVC 14843 & 15039 */
        ibm_msg_p=(unsigned char *)dce_sprintf(ibm_msg_618, princ_name);
        if (ibm_msg_p) {
            sprintf(buf,"%s",(char *)ibm_msg_p);
            dce_free(ibm_msg_p);
        }
        else    dce_printf(MsgDce_sprintfCallFailed);
#else /* CMVC 13302, msg separation */
        sprintf(buf, "rsec_pwd_mgmt_gen_pwd: princ- %s", princ_name);
#endif /* CMVC 13302 */
        message(buf);
    }

    /*
     * XXX -
     * if RPC protection level privacy is not available,
     * we should not send the password back in the clear.
     * Therefore we must send the password to a trusted proxy and
     * inform the client to get the password from the proxy.
     * However support for proxies has not been implemented yet,
     * so we will send the password back unencrypted for the
     * time being. A pickle type should be defined which holds
     * the information needed by the client to retrieve the
     * password from the proxy. This pickle should be sent in
     * the gen_info_out parameter.
     *
     * if (protect_level == rpc_c_protect_level_pkt_integ) {
     *      *
     *      * Send password to proxy.
     *      * Encode necessary information into gen_info_out pickle.
     *      *
     *
     *      * destroy password *
     *     if (gen_pwd->key.tagged_union.plain)
     *         rpc_ss_free(gen_pwd->key.tagged_union.plain);
     *     *stp = sec_pwd_mgmt_proxy_req;
     * }
     */
}


/*
 * rsec_pwd_mgmt_str_chk
 */
boolean32 DCEAPI rsec_pwd_mgmt_str_chk (
    handle_t                 handle,                   /* [in] */
    sec_rgy_name_t           princ,                    /* [in] */
    sec_passwd_rec_t         *pwd,                     /* [in] */
    signed32                 pwd_val_type,             /* [in] */
    unsigned32               plcy_args,                /* [in] */
    sec_attr_t               plcy[],                   /* [in] */
    sec_bytes_t              str_info_in,              /* [in] */
    sec_bytes_t              *str_info_out,            /* [out] */
    error_status_t           *stp                      /* [out] */
)
{
    long                     passwd_len;
    char                     buf[sec_rgy_name_t_size + 100];
    unsigned32               protect_level;
    boolean32                record_exists;

#ifdef IBMOS2
    dce_error_string_t  err_string;
    int                 lst;
    boolean32           check_result = false;
#endif

    *stp = error_status_ok;
    str_info_out->num_bytes = 0;
    str_info_out->bytes = NULL;

    if (verbose) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 13302, msg separation */
        /* CMVC 14843 & 15039 */
        ibm_msg_p=(unsigned char *)dce_sprintf(ibm_msg_619, princ);
        if (ibm_msg_p) {
            sprintf(buf,"%s",(char *)ibm_msg_p);
            dce_free(ibm_msg_p);
        }
        else    dce_printf(MsgDce_sprintfCallFailed);
#else /* CMVC 13302, msg separation */
        sprintf(buf, "rsec_pwd_mgmt_str_chk request: princ- %s", princ);
#endif /* CMVC 13302 */
        message(buf);
    }

    /* Check authorization and authentication of client */
#if defined(AIX_PROD) || defined(IBMOS2)
    /*  CMVC Defect 11392 */
    check_auth_client(handle, rpc_c_protect_level_pkt_integ,
        &protect_level, rpc_c_authn_dce_secret, NULL, stp);
#else
    check_auth_client(handle, rpc_c_protect_level_pkt_integ,
        &protect_level, rpc_c_authn_dce_secret, SEC_RGY_SERVER_NAME, stp);
#endif

    if (*stp != error_status_ok) {
#ifdef IBMOS2
    if (port_test) {
       dce_error_inq_text(*stp, err_string, &lst);
       dce_fprintf(stderr, ibm_msg_620, err_string); /* 14567 & 13302 */
    }
#endif /* IBMOS2 */
        return false;
    }

    /*
     * XXX - If RPC transmission used anything other than packet level
     * privacy, the password to be strength checked should not have
     * been sent over the wire unencrypted. A proxy mechanism should
     * have been used (see rsec_pwd_mgmt.idl for more info). However,
     * since this functionality is not available, the password *is*
     * sent over the wire unencrypted.
     */

    /*
     * If password is DES, just return.
     * XXX - check DES passwords to make sure they are the
     * same ones generated. Check against weak DES keys.
     * Also, check des key bit on account to see if user is
     * allowed to specify a DES password.
     */
    if (pwd->key.key_type == sec_passwd_des)
        return true;

    /*
     * If password was generated, look in cache to make sure
     * that user sent the password which was generated!
     */
    if (pwd_val_type == generation_required) {

#ifdef IBMOS2
    if (port_test) {
       check_result = check_cache((char *) princ, pwd, &record_exists, stp);
       if (check_result)
          dce_fprintf(stderr, ibm_msg_621); /* CMVC 14567 & 13302 */
       else {
          /* Put out different message for hard error vs. not found */
          if (*stp != error_status_ok) {
             dce_error_inq_text(*stp, err_string, &lst);
             dce_fprintf(stderr, ibm_msg_622, err_string); /* CMVC 14567 & 13302 */
          } else {
             dce_fprintf(stderr, ibm_msg_623); /* CMVC 14567 & 13302 */
          }  /* end if check_cache returned bad status (hard error) */
       }  /* end if check_cache returned true */
       return(check_result);
    } /* endif user set env var requesting extra debug */
#endif /* IBMOS2 */

       return check_cache((char *) princ, pwd, &record_exists, stp);
    }

    /*
     * If pwd_val_type is user_can_select, we do not care if the
     * user selected their own password or asked for a generated
     * password. Also, we do not *know* whether the user selected
     * their own password or not, so if we did check the generated
     * password cache and found a password that did not match, we
     * cannot be assured that the user simply generated a password
     * but then decided to pick their own anyway. In this scenario,
     * the user should not fail the strength check. So, to solve
     * this, we never check the generated password cache at all
     * because we really don't care if the user didn't use the generated
     * password. The generated passwords will get flushed after a while.
     */

    /* check password against effective policy */
    passwd_len = strlen((char *)pwd->key.tagged_union.plain);

#ifdef OS2_EXAMPLE
    /*  CUSTOMER COMPOSITION CHECK WOULD BE MADE HERE, OPTIONALLY CALLING
        CHECK_PLAIN_PWD() FIRST IF IT WAS DESIRED TO ALSO ENFORCE THE MINIMAL
        COMPOSITION RULES AS DEFINED IN THE DCE REGISTRY POLICY.   */
    /* Also see example below where, for test purposes, a password is rejected
        if either a 'y' or a 'z' appears in the password. */
#endif

#ifdef IBMOS2
    if (port_test) {
       /*  ONLY GET HERE IF THE PWD_PORT_TEST ENVIRONMENT VARIABLE SET */
       check_result = check_plain_pwd((char *) pwd->key.tagged_union.plain,
          passwd_len, plcy[0].attr_value.tagged_union.signed_int,
          plcy[1].attr_value.tagged_union.signed_int);
       if (check_result)
          dce_fprintf(stderr, ibm_msg_624); /* CMVC 14567 & 13302 */
       else {
          dce_fprintf(stderr, ibm_msg_625); /* CMVC 14567 & 13302 */
       }
       /*  SOMEWHAT SILLY, BUT SIMPLE, COMPOSITION CHECK RENDERED FOR DCE
           TESTING PURPOSES HERE.  THE DEFAULT CUSTOMER EXAMPLE WILL ALSO
           REJECT ANY PASSWORD CONTAINING THE LETTER 'z', SO LONG AS THE
           ENVIRONMENT VARIABLE "PWD_PORT_TEST" WERE SET NON-NULL PRIOR TO
           INVOKING THE CUSTOMER STRENGTH SERVER.      */
       /* Let's pretend we have additional strength checks to perform, as
        * a customized password strength server would.
        * If our check fails, override check_plain_pwd result; else leave it.
        */
       if (strchr((char *) pwd->key.tagged_union.plain, 'z')) {
          dce_fprintf(stderr, ibm_msg_626); /* CMVC 14567 & 13302 */
          check_result = false;
       } else {
          dce_fprintf(stderr, ibm_msg_627); /* CMVC 14567 & 13302 */
       }
       return(check_result);
    }  /* end if user set env var requesting extra debug */
#endif /* IBMOS2 */

    return check_plain_pwd((char *) pwd->key.tagged_union.plain, passwd_len,
        plcy[0].attr_value.tagged_union.signed_int,
        plcy[1].attr_value.tagged_union.signed_int);

}

#ifndef IBMOS2
/*
 * sigcatch() -- Catch and handle asynchronous signals for the server.  This
 * function waits a separate thread using the sigwait system call.  When an
 * asynchronous signal the thread is waiting for comes in the thread is
 * scheduled again and tells the server to stop listening, causing a return
 * from the rpc_server_listen() above.  This thread then exits.
 */
static pthread_addr_t sigcatch(pthread_addr_t arg)
{
    error_status_t      st, _ignore;            /* returned by DCE calls */
    dce_error_string_t  dce_err_string;         /* text describing error code */
    sigset_t            mask;                   /* signal values to wait for */
    int                 signo;

#if defined(AIX_PROD) /* CMVC 16603, msg separation */
    char                ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

    /*
     * Create the signal mask -- only the following signals will be caught.
     * See signal(5).
     */
    sigemptyset(&mask);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);
#ifdef  _INCLUDE_POSIX_SOURCE
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
#endif  /* _INCLUDE_POSIX_SOURCE */

    /*
     * Calling sigwait will cause this thread to block until the process
     * receives one of the signals in the mask.  If no threads were waiting
     * for these fatal asynchronous signals and such a signal were received,
     * the process would die immediately without giving the server a chance to
     * unregister its bindings with the endpoint mapper.  sigwait is the only
     * way to catch a fatal asynchronous signal and have the opportunity to
     * cleanup before exiting.
     */
    signo = sigwait(&mask);

    /* XXX  - Need some way to tell user what signal was caught */

    /*  rpc_mgmt_stop_server_listening() --
     *
     * Stop the server from listening for more requests, but let current
     * requests run to completion.  The first parameter is the binding
     * handle associated with the server we wish to make stop listening.  A
     * value of NULL means to stop this (local) server from listening.  The
     * second parameter is the DCE error status.
     *
     * Note that if there are pending RPC's that don't complete in a timely
     * manner, another asynchronous signal will kill the server process: we
     * will no longer have a thread to catch these signals!
     */
    rpc_mgmt_stop_server_listening(NULL, &st);
    if (st != error_status_ok) {
#if defined(AIX_PROD) /* CMVC 16603, msg separation */
        ibs_get_msg(ibs_msg_buf,ibm_msg_1057);
        error(&st, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
        error(&st, "Unable to stop server listening");
#endif /* CMVC 16603 */
    }
    pthread_exit((pthread_addr_t) NULL);
}
#endif /* not IBMOS2 */

/*
 * terminate_thread() -- Terminate the current thread.  This is called in
 * place of exit in any routines that could be called by manager functions
 * so the server process can clean up and exit without leaving any orphaned
 * clients.  If the server simply exits any other manager threads would
 * disappear, causing clients to timeout with partially executed requests.
 */
static void terminate_thread(void)
{
    error_status_t      st;                      /* returned by DCE calls */

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
    char                ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

/* #if !defined(IBMOS2) || !defined(TERMINATE_CDS_STYLE)*/
#if defined(IBMOS2) || defined(TERMINATE_CDS_STYLE)
    /*
     * Stop the server from listening for more requests, but let current
     * requests run to completion.
     */
    rpc_mgmt_stop_server_listening(NULL, &st);
    if (st != rpc_s_ok) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
        ibs_get_msg(ibs_msg_buf,ibm_msg_1057);
        error(&st, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
        error(&st, "Unable to stop server listening");
#endif /* CMVC 16603 */
    }
#endif
    pthread_exit((pthread_addr_t)NULL);
}

/*
 * strength_unregister():
 *
 * - unregister interfaces from CDS namespace, endpoint map, and RPC runtime
 * - purge server context (identity)
 * - close the log file descriptor
 */

static void strength_unregister(void)
{

        error_status_t lst;

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 13302,16603 & 17768, msg separation */
        char           ibs_msg_buf[MSGSIZE];
        /* cleanup namespace; doesn't hurt, and makes it easier to move
             strength server to another machine, if desired. */
        if (verbose) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1059);
            message(ibs_msg_buf);
        }
        rpc_ns_binding_unexport(rpc_c_ns_syntax_default,
                                (unsigned_char_p_t) cds_path,
                                rsec_pwd_mgmt_v1_0_s_ifspec,
                                &obj_uuids, &lst);
        if (lst != error_status_ok) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1060);
            error(NULL, ibs_msg_buf);
        }

        /* cleanup endpoints */
        if (verbose) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1061);
            message(ibs_msg_buf);
        }
        rpc_ep_unregister(rsec_pwd_mgmt_v1_0_s_ifspec, bindings,
            &obj_uuids, &lst);

        /* cleanup binding vector */
        rpc_binding_vector_free(&bindings, &lst);

        /* unregister the interface from the RPC runtime */
        if (verbose) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1062);
            message(ibs_msg_buf);
        }
        rpc_server_unregister_if(rsec_pwd_mgmt_v1_0_s_ifspec, NULL, &lst);

        /* purge login context */
        if (verbose) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1063);
            message(ibs_msg_buf);
        }
        sec_login_purge_context(&login_context, &lst);

        /* close file */
        if (verbose) {
            dce_fprintf(stderr, ibm_msg_628);
        }
#else /* CMVC 13302,16603 & 17768, msg separation */
        /* cleanup namespace; doesn't hurt, and makes it easier to move
             strength server to another machine, if desired. */
        if (verbose) {
            message("unexporting current rsec_pwd_mgmt info from namespace");
        }
        rpc_ns_binding_unexport(rpc_c_ns_syntax_default,
                                (unsigned_char_p_t) cds_path,
                                rsec_pwd_mgmt_v1_0_s_ifspec,
                                &obj_uuids, &lst);
        if (lst != error_status_ok) {
            error(NULL, "Can't unexport pwd_mgmt from namespace on exit");
        }

        /* cleanup endpoints */
        if (verbose) {
            message("unregistering all endpoints");
        }
        rpc_ep_unregister(rsec_pwd_mgmt_v1_0_s_ifspec, bindings,
            &obj_uuids, &lst);

        /* cleanup binding vector */
        rpc_binding_vector_free(&bindings, &lst);

        /* unregister the interface from the RPC runtime */
        if (verbose) {
            message("unregistering all interfaces from RPC runtime");
        }
        rpc_server_unregister_if(rsec_pwd_mgmt_v1_0_s_ifspec, NULL, &lst);

        /* purge login context */
        if (verbose) {
            message("purging server context");
        }
        sec_login_purge_context(&login_context, &lst);

        /* close file */
        if (verbose) {
            fprintf(stderr, "closing strength server log file\n");
        }
#endif /* CMVC 13302,16603 & 17768 */
        fclose(pd_fp);
        close(pd_fd);
}


#ifdef IBMOS2
#if defined (__BORLANDC__)
static void _USERENTRY os2_sigcatch(int sig)
#else
static void os2_sigcatch(int sig)
#endif
{
    signal(SIGINT, os2_sigcatch);
    signal(SIGBREAK, os2_sigcatch);
    signal(SIGTERM, os2_sigcatch);
    signal(SIGABRT, os2_sigcatch);
    signal(SIGSEGV, os2_sigcatch);

  if (!sig_in_progress) {
    sig_in_progress = true;
    switch(sig) {
    case SIGABRT:
#ifdef DCESTARTSTOP
        dceos2_set_process_state(DCEOS2_STRENGTH_NAME, getpid(),
                                  DCEOS2_PROCESS_ENDED);
        dce_svc_printf(DCE_SVC(ibs_svc_handle, ""),
                   ibs_s_pwdstr,
                   svc_c_sev_notice | svc_c_route_stderr,
                   ibs_pwd_strength_aborted);           /* CMVC 18710 */
        /* PRINT_DCE_OS2_MSG(DCEOS2_STRENGTH_ABORT, stderr);        */
#else
        dce_fprintf(stderr, ibm_msg_629, PWD_PROGNAME); /* CMVC 14567 & 13302 */
#endif
        exit(1);
    case SIGSEGV:
    case SIGBREAK:
    case SIGINT:
    case SIGTERM:
#ifdef TERMINATE_CDS_STYLE
        if (strength_sig_init == 1) {
            sigprocmask(SIG_BLOCK, &strength_sig_block, NULL);
            strength_sig_init = 2;
            dce_fprintf(stdout, ibm_msg_630, PWD_PROGNAME); /* CMVC 14567 & 13302 */
            pthread_mutex_lock(&strength_sig_mutex);
            /* Wake up main thread to perform cleanup */
            pthread_cond_signal(&strength_sig_cond);
            pthread_mutex_unlock(&strength_sig_mutex);
        }
#else
#ifdef DCESTARTSTOP
        dceos2_set_process_state(DCEOS2_STRENGTH_NAME, getpid(),
                                  DCEOS2_PROCESS_ENDED);
        dce_svc_printf(DCE_SVC(ibs_svc_handle, ""),
                   ibs_s_pwdstr,
                   svc_c_sev_notice | svc_c_route_stderr,
                   ibs_pwd_strength_ended);             /* CMVC 18710 */
        /* PRINT_DCE_OS2_MSG(DCEOS2_STRENGTH_ENDED, stdout);    */
#else
        dce_fprintf(stdout, ibm_msg_631); /* CMVC 14567 & 13302 */
#endif
        terminate_thread();
#endif  /* TERMINATE_CDS_STYLE */
    }
  }
}

#ifdef TERMINATE_CDS_STYLE
pthread_addr_t  manage_rpclisten(pthread_addr_t arg)
{
    error_status_t status;

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
    char           ibs_msg_buf[MSGSIZE];
    rpc_server_listen(rpc_c_listen_max_calls_default, &status);
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1064);
        error(&status, ibs_msg_buf);
    } else {
        if (verbose) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1065);
            message(ibs_msg_buf);
        }
    }
#else /* CMVC 16603 & 17768, msg separation */
    rpc_server_listen(rpc_c_listen_max_calls_default, &status);
    if (status != error_status_ok) {
        error(&status, "Unable to listen for requests");
    } else {
        if (verbose) {
            message("return from rpc_server_listen()");
        }
    }
#endif /* CMVC 16603 & 17768 */
    return (0);
}
#endif  /* TERMINATE_CDS_STYLE */

#endif /* IBMOS2 */

/*
 * manage_key manages the server principal's key, by
 * changing it every so often.
 */
static pthread_addr_t manage_key(pthread_addr_t arg)
{
    error_status_t  st;
    struct timespec timer;

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
    char            ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

    timer.tv_nsec = 0;

    while (1) {

#ifdef IBMOS2
        /* CMVC 18549: use DCELOCAL-based name for OS/2 example also */
        sec_key_mgmt_manage_key(rpc_c_authn_dce_secret,
            fullkeytabnm, (idl_char *) SERVER_PNAME, &st);
#else
        /* Altered OSF originial to use constant for keyfile name */
        sec_key_mgmt_manage_key(rpc_c_authn_dce_secret,
            (void *) KEYTABNAME, (idl_char *) SERVER_PNAME, &st);
#endif /* IBMOS2 */
        if (st != error_status_ok) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
            ibs_get_msg(ibs_msg_buf,ibm_msg_1066);
            error(&st, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
            error(&st, "Server principal key management failure");
#endif /* CMVC 16603 */
            terminate_thread();
        }

        timer.tv_sec = ten_minutes;
        pthread_delay_np(&timer);
    }
    return (0);
}


/*
 * cleanup_cache removes any stale entries from the password cache
 * by checking it every so often (determined by cache timeout
 * value).
 */
static pthread_addr_t cleanup_cache(pthread_addr_t arg)
{
    struct timespec ts;
    error_status_t  lst;

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
    char            ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

    /* set timer to value of cache timeout */
    ts.tv_sec = timeout;
    ts.tv_nsec = 0;
    while (1) {

        pthread_delay_np(&ts);

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
        if (verbose) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1067);
            message(ibs_msg_buf);
        }

        cleanup_table(&lst);
        if (lst != error_status_ok) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1068);
            error(&lst, ibs_msg_buf);
            terminate_thread();
        }
#else /* CMVC 16603, msg separation */
        if (verbose) {
            message("Cleaning up password cache");
        }

        cleanup_table(&lst);
        if (lst != error_status_ok) {
            error(&lst, "Unable to cleanup password cache");
            terminate_thread();
        }
#endif /* CMVC 16603 */
    }
    return (0);
}


/*
 * refresh_context manages the server's identity, by refreshing
 * and revalidating it before it expires.
 */
static pthread_addr_t refresh_context(pthread_addr_t arg)
{
    signed32        expiration;
    error_status_t  st, lst;
    time_t          now;
    int             seconds;
    struct timespec ts;
#ifdef IBMOS2
#ifdef OS2_EXAMPLE
    utc_t       utc_buf;
    timespec_t  time_component;
    timespec_t  inacc_component;
    long        tdf;
#else
    struct timeval  time_for_os2;
#endif
#endif
    boolean32       reset_passwd;
    sec_login_auth_src_t auth_src;
    sec_passwd_rec_t *keydata;

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
    char             ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

    ts.tv_sec = ten_minutes;
    ts.tv_nsec = 0;

    while (1) {

        /* get the absolute expiration time expressed in seconds */
        sec_login_get_expiration(login_context, &expiration, &st);
        if (st != error_status_ok) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
            ibs_get_msg(ibs_msg_buf,ibm_msg_1069);
            error(&st, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
            error(&st, "Unable to get expiration of server identity");
#endif /* CMVC 16603 */
            terminate_thread();
        }

#ifdef IBMOS2
#ifdef OS2_EXAMPLE
        if (utc_gettime(&utc_buf) == -1) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1070); /* CMVC 16603 & 17768 */
            error(NULL, ibs_msg_buf);              /* CMVC 16603 & 17768 */
            terminate_thread();
        }
        if (utc_bintime(&time_component,&inacc_component,&tdf,&utc_buf)==-1) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1071); /* CMVC 16603 & 17768 */
            error(NULL, ibs_msg_buf);              /* CMVC 16603 & 17768 */
            terminate_thread();
        }
        now = time_component.tv_sec;
#else
/* Do not trust the os/2 time() function. Use gettimeofday() instead. */
#ifdef SNI_SVR4_POSIX
        gettimeofday(&time_for_os2);
#else
        gettimeofday(&time_for_os2, 0);
#endif /* SNI_SVR4_POSIX */
        now = (time_t) time_for_os2.tv_sec;
#endif  /* OS2_EXAMPLE */
#else
        time(&now);
#endif  /* IBMOS2 */

        seconds = expiration - now;
        if (seconds > ten_minutes) {
            ts.tv_sec = seconds - ten_minutes;
            pthread_delay_np(&ts);
        }

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
        if (verbose) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1072);
            message(ibs_msg_buf);
        }

        sec_login_refresh_identity(login_context, &st);
        if (st != error_status_ok) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1073);
            error(&st, ibs_msg_buf);
            terminate_thread();
        }
#else /* CMVC 16603 & 17768, msg separation */
        if (verbose) {
            message("Refreshing server context");
        }

        sec_login_refresh_identity(login_context, &st);
        if (st != error_status_ok) {
            error(&st, "Unable to refresh server identity");
            terminate_thread();
        }
#endif /* CMVC 16603 & 17768 */

        /* get key */
#ifdef IBMOS2
        /* CMVC 18549: use DCELOCAL-based name for OS/2 example, too. */
        sec_key_mgmt_get_key(rpc_c_authn_dce_secret, fullkeytabnm,
            (idl_char *) SERVER_PNAME, 0, (void **) &keydata, &st);
#else
        /* Modified OSF original to use constant for key file name */
        sec_key_mgmt_get_key(rpc_c_authn_dce_secret, KEYTABNAME,
            (idl_char *) SERVER_PNAME, 0, (void **) &keydata, &st);
#endif /* IBMOS2 */
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
        if (st != error_status_ok) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1074);
            error(&st, ibs_msg_buf);
            terminate_thread();
        }

        /* validate login context */
        sec_login_validate_identity(login_context, keydata, &reset_passwd,
            &auth_src, &st);
        /* XXX - There may be a chance that the key changed since the get_key */
        if (st != error_status_ok) {
            sec_key_mgmt_free_key(keydata, &lst);
            ibs_get_msg(ibs_msg_buf,ibm_msg_1075);
            error(&st, ibs_msg_buf);
            terminate_thread();
        }

        sec_key_mgmt_free_key(keydata, &lst);

        /* certify login context */
        sec_login_certify_identity(login_context, &st);
        if (st != error_status_ok) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1076);
            error(&st, ibs_msg_buf);
            terminate_thread();
        }
#else /* CMVC 16603 & 17768, msg separation */
        if (st != error_status_ok) {
            error(&st, "Unable to get server key");
            terminate_thread();
        }

        /* validate login context */
        sec_login_validate_identity(login_context, keydata, &reset_passwd,
            &auth_src, &st);
        /* XXX - There may be a chance that the key changed since the get_key */
        if (st != error_status_ok) {
            sec_key_mgmt_free_key(keydata, &lst);
            error(&st, "Unable to validate identity");
            terminate_thread();
        }

        sec_key_mgmt_free_key(keydata, &lst);

        /* certify login context */
        sec_login_certify_identity(login_context, &st);
        if (st != error_status_ok) {
            error(&st, "Unable to certify identity");
            terminate_thread();
        }
#endif /* CMVC 16603 & 17768  */
    }
    return (0);
}


#ifndef IBMOS2
static void become_server(void)
{
    int child;
    int fd;

    if (!debug_mode) {

        child = fork();

        /* parent */
        if (child != 0) {
            exit(0);
        } else {
            /* Disassociate from controlling terminal */
#if defined(_POSIX_SOURCE)
            if (setsid() == -1) {
#if defined(AIX_PROD) /* CMVC 14567 & 13302, msg separation */
                dce_fprintf(stderr, ibm_msg_414);
#else /* CMVC 14567 & 13302, msg separation */
                fprintf(stderr, "Cannot disassociate from controlling terminal\n");
#endif /* CMVC  14567 &13302 */
                perror("setsid");
                exit(1);
            }
#else
            if (setpgrp(0, getpid()) == -1) {
#if defined(AIX_PROD) /* CMVC 13302, msg separation */
                dce_fprintf(stderr, ibm_msg_414); /* CMVC 14567 */
#else /* CMVC 13302, msg separation */
                fprintf(stderr, "Cannot disassociate from controlling terminal\n");
#endif /* CMVC 13302 */
                perror("setpgrp");
                exit(1);
            }
#endif

            /* redirect stdin, stdout, & stderr so pwd_strengthd can be started remotely */
            if ((fd = open("/dev/null", O_RDWR,
                       S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != -1)
            {
                (void)dup2(fd,0);
                (void)dup2(fd,1);
                (void)dup2(fd,2);
                (void)close(fd);
            }
        }
    }
}
#endif /* not IBMOS2 */

/*
 * Main routine
 */
int main (
    int argc,
    char *argv[]
)
{
    error_status_t          status, lst;
    /* Use define constant in IBM DCE for easier support of multiple servers
        from the same source code. */
    unsigned_char_p_t       annotation = (unsigned_char_p_t)
                                   SERVER_PNAME " - rsec_pwd_mgmt interface";
    /* need log buffer so appropriate program name appears in messages */
    char logbuf[120];       /* plenty */
    boolean32               reset_passwd;
    sec_login_auth_src_t    auth_src;
    pthread_t               refresh_thread,
#ifdef IBMOS2
                            rpclisten_thread,
#else
                            sig_thread,
    error_status_t          st;
#endif
                            manage_key_thread,
                            cleanup_cache_thread;
    sec_passwd_rec_t        *keydata;


#ifdef OS2_EXAMPLE
    char                    *os2dcepath;
#endif

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
    char                    ibs_msg_buf[MSGSIZE];
#endif /* CMVC 16603, msg separation */

#ifdef AIX_PROD         /* CMVC 13028 */
    sigset_t                mask;

    /* Set the process signal mask in the main thread. */
    sigemptyset(&mask);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);
#ifdef  _INCLUDE_POSIX_SOURCE
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
#endif  /* _INCLUDE_POSIX_SOURCE */
    sigprocmask(SIG_BLOCK, &mask, NULL);
#endif  /* AIX_PROD, CMVC 13028 */

#ifdef IBMOS2
    /* This must come right after the declarations */
    /* CMVC 11253: removed copyright print statements */
    pthread_inst_exception_handler();
    if (getenv("PWD_PORT_TEST")) {
       port_test = true;
    }
    /* install signal handler */
    signal(SIGINT, os2_sigcatch);
    signal(SIGBREAK, os2_sigcatch);
    signal(SIGTERM, os2_sigcatch);
    signal(SIGABRT, os2_sigcatch);
    signal(SIGSEGV, os2_sigcatch);
#endif /* IBMOS2 */

    setlocale(LC_ALL, "");

#ifndef OS2_EXAMPLE
    dce_svc_set_progname(PWD_PROGNAME, &st);
    dce_msg_define_msg_table(sad__table,
        sizeof sad__table / sizeof sad__table[0], &st);
    dce_msg_define_msg_table(ibs__table,
        sizeof ibs__table / sizeof ibs__table[0], &st);
#endif

    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
#ifdef DCESTARTSTOP
    dce_printf(msg_s_dceos2_copyright);       /* CMVC 18710  */
    /* PRINT_DCE_OS2_COPYRIGHT(stdout);        */
#endif

#ifndef OS2_EXAMPLE
    /* Must be run by privileged user */
    if (setuid(0) == -1) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 13302, msg separation */
        dce_fprintf(stderr, ibm_msg_422); /* CMVC 14567 */
#else /* CMVC 13302, msg separation */
        fprintf(stderr, "Must be the privileged user\n");
#endif /* CMVC 13302 */
        exit(1);
    }
#endif  /* OS2_EXAMPLE */

    /* Construct program name variable, accessible from signal handlers too */
    strcpy(PWD_PROGNAME, argv[0]);

#ifdef IBMOS2
    /* Construct appropriate DCELOCAL-based names for keytable and log file,
       for both primary and customer-built servers.  Done differently, but
       result should be the same.   */
#ifdef OS2_FATMAP
    /* Primary server has access to ACPP and mapname() */

    /* Obtain the proper OS/2 pathname for this server's key table. */
    if ((mappedname = mapname(mapoutput, inkeytabnm)) == NULL) {
        dce_fprintf(stderr, ibm_msg_424, inkeytabnm); /* CMVC 14567 & 13302 */
        exit(1);
    }
    /* Prepend with "type" prefix, to build the string expected by receivers. */
    /* CMVC 18549: use string functions, not sprintf, as this not a message */
    strcpy(fullkeytabnm, "FILE:");
    strcat(fullkeytabnm, mappedname);
    if (port_test)
       dce_fprintf(stderr, ibm_msg_637, fullkeytabnm); /* CMVC 14567 & 13302 */

    /* Now build the OS/2 pathname for this server's log file. */
    mappedname = NULL;
    if ((mappedname = mapname(mapoutput, PWD_STRENGTHD_LOG)) == NULL) {
        dce_fprintf(stderr, ibm_msg_424, PWD_STRENGTHD_LOG); /* CMVC 14567 & 13302 */
        exit(1);
    }
    /* CMVC 18549: use string functions, not sprintf, as this not a message */
    strcpy(pwdlognm, mappedname);
    if (port_test)
       dce_fprintf(stderr, ibm_msg_639, pwdlognm); /* CMVC 14567 & 13302 */

#else   /* (OS2_EXAMPLE) */    /* CMVC 18549 */
     /* Example code must read same environment variable mapname() uses */
    os2dcepath = (char *)getenv("DCELOCAL");
    strcpy(fullkeytabnm, "FILE:");
    strcat(fullkeytabnm, os2dcepath);
    strcat(fullkeytabnm, KEYTABNAME);
    strcpy(pwdlognm, os2dcepath);
    strcat(pwdlognm, PWD_STRENGTHD_LOG);

#endif  /* OS2_FATMAP */
#endif  /* IBMOS2 */

    /* Process command line arguments */
    if (!process_args(argc, argv)) {
        exit(1);
    }

    /* Open log file:  ALWAYS...since IBM DCE now outputs results of error()
        to both screen and to log. */
#ifdef OS2_EXAMPLE
        /* CMVC 18549: use DCELOCAL-based name for OS/2 example, too. */
        pd_fd = sopen(pwdlognm, (O_WRONLY | O_APPEND |
                     O_CREAT), SH_DENYNO, S_IREAD | S_IWRITE);
#else
#ifdef OS2_FATMAP
        pd_fd = open(pwdlognm, (O_WRONLY | O_APPEND |
                     O_CREAT), (mode_t) (S_IRUSR | S_IWUSR));
#else
        pd_fd = open((char *) PWD_STRENGTHD_LOG, (O_WRONLY | O_APPEND |
                     O_CREAT), (mode_t) (S_IRUSR | S_IWUSR));
#endif  /* OS2_FATMAP */
#endif  /* OS2_EXAMPLE */
        if (pd_fd == -1) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 13302, msg separation */
            dce_fprintf(stderr, ibm_msg_433); /* CMVC 14567 */
#else /* CMVC 13302, msg separation */
            fprintf(stderr, "Cannot open log file\n");
#endif /* CMVC 13302 */
            perror("open");
            exit(1);
        }
        pd_fp = fdopen(pd_fd, "a");
        if (pd_fp == (FILE *) NULL) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 13302, msg separation */
            dce_fprintf(stderr, ibm_msg_433); /* CMVC 14567 */
#else /* CMVC 13302, msg separation */
            fprintf(stderr, "Cannot open log file\n");
#endif /* CMVC 13302 */
            perror("fdopen");
            exit(1);
        }
        setbuf(pd_fp, NULL);
#ifdef IBMOS2
        if (port_test)
           dce_fprintf(stderr, ibm_msg_642); /* CMVC 14567 & 13302 */
#endif /* IBMOS2 */

#ifdef IBMOS2
/*  No fork in OS/2, so stay in foreground, as do sclientd and secd. */
#else
    /* become server, disconnect from tty, etc ... */
    (void) become_server();
#endif /* IBMOS2 */

    /* establish server principal identity early in the game, since it is
     * needed for at least one rpc_ setup call: rpc_ns_binding_export
     */
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
    if (verbose) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1077);
        message(ibs_msg_buf);
    }

    /* setup login context */
    sec_login_setup_identity((unsigned_char_p_t) SERVER_PNAME,
        sec_login_no_flags, &login_context, &status);
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1078);
        error(&status, ibs_msg_buf);
        exit(1);
    }
#else /* CMVC 16603 & 17768, msg separation */
    if (verbose) {
        message("Setting up server context");
    }

    /* setup login context */
    sec_login_setup_identity((unsigned_char_p_t) SERVER_PNAME,
        sec_login_no_flags, &login_context, &status);
    if (status != error_status_ok) {
        error(&status, "Unable to setup identity");
        exit(1);
    }
#endif /* CMVC 16603 & 17768 */

    /* get key  -- key must have been created via rgy_edit ktadd */
#ifdef IBMOS2
    /* CMVC 18549: Use DCELOCAL-based name for OS/2 example, too. */
    sec_key_mgmt_get_key(rpc_c_authn_dce_secret, fullkeytabnm,
        (idl_char *) SERVER_PNAME, 0, (void **) &keydata, &status);
#else
    sec_key_mgmt_get_key(rpc_c_authn_dce_secret, KEYTABNAME,
        (idl_char *) SERVER_PNAME, 0, (void **) &keydata, &status);
#endif /* IBMOS2 */
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1074);
        error(&status, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    /* validate login context */
    sec_login_validate_identity(login_context, keydata, &reset_passwd,
        &auth_src, &status);
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1075);
        error(&status, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    sec_key_mgmt_free_key(keydata, &lst);

    /* certify login context */
    sec_login_certify_identity(login_context, &status);
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1076);
        error(&status, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
    }

    /* set context */
    sec_login_set_context(login_context, &status);
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1082);
        error(&status, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
#else /* CMVC 16603 & 17768, msg separation */
    if (status != error_status_ok) {
        error(&status, "Unable to get server key");
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    /* validate login context */
    sec_login_validate_identity(login_context, keydata, &reset_passwd,
        &auth_src, &status);
    if (status != error_status_ok) {
        error(&status, "Unable to validate identity");
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    sec_key_mgmt_free_key(keydata, &lst);

    /* certify login context */
    sec_login_certify_identity(login_context, &status);
    if (status != error_status_ok) {
        error(&status, "Unable to certify identity");
        sec_login_purge_context(&login_context, &lst);
    }

    /* set context */
    sec_login_set_context(login_context, &status);
    if (status != error_status_ok) {
        error(&status, "Unable to set server context");
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
#endif /* CMVC 16603 & 17768 */

#ifdef IBMOS2
    if (port_test)
       dce_fprintf(stderr, ibm_msg_643, SERVER_PNAME); /* CMVC 14567 & 13302 */
#endif /* IBMOS2 */

    /* use all protocol sequences */
    rpc_server_use_all_protseqs(rpc_c_protseq_max_reqs_default, &status);
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1083);
        error(&status, ibs_msg_buf);
        exit(1);
    }

    /* obtain vector of server binding handles */
    rpc_server_inq_bindings(&bindings, &status);
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1084);
        error(&status, ibs_msg_buf);
        exit(1);
    }

    if (verbose) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1085);
        message(ibs_msg_buf);
    }

    /* register rsec_pwd_mgmt interface with RPC runtime */
    rpc_server_register_if(rsec_pwd_mgmt_v1_0_s_ifspec, (uuid_t *) NULL,
        (rpc_mgr_epv_t) NULL, &status);
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1086);
        error(&status, ibs_msg_buf);
        exit(1);
    }

    /* export binding information to name service */

    /* -- the object uuid is what distinguishes the rsec_pwd_mgmt interface
     * -- exported by this strength/gen server instance from the same
     * -- interface exported by others
     */
    uuid_from_string((unsigned_char_p_t)obj_id_str, &obj_id, &status);
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1087);
        error(&status, ibs_msg_buf);
        exit(1);
    }
    obj_uuids.count = 1;
    obj_uuids.uuid[0] = &obj_id;

    /* first try an unexport to get rid of any potential information that
        might indicate a different pwd_strength location. */
    if (verbose) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1088);
        message(ibs_msg_buf);
    }
    rpc_ns_binding_unexport(rpc_c_ns_syntax_default,
                            (unsigned_char_p_t) cds_path,
                            rsec_pwd_mgmt_v1_0_s_ifspec,
                            &obj_uuids, &lst);
    if (lst != rpc_s_interface_not_found) {
        /* Normal case is to find nothing, hence the "interface not found"
                error code.  Only other plausible return code is "success" */
        if (lst == error_status_ok) {
            /* Namespace info existed prior to the unexport. */
            ibs_get_msg(ibs_msg_buf,ibm_msg_1089);
            error(NULL, ibs_msg_buf);
            ibs_get_msg(ibs_msg_buf,ibm_msg_1090);
            sprintf(logbuf, ibs_msg_buf, PWD_PROGNAME);
            error(NULL, logbuf);
            ibs_get_msg(ibs_msg_buf,ibm_msg_1091);
            sprintf(logbuf, ibs_msg_buf, PWD_PROGNAME);
            error(NULL, logbuf);
            ibs_get_msg(ibs_msg_buf,ibm_msg_1092);
            error(NULL, ibs_msg_buf);
        }
    }
    if (verbose) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1093);
        message(ibs_msg_buf);
    }
    rpc_ns_binding_export(rpc_c_ns_syntax_default,
                          (unsigned_char_p_t) cds_path,
                          rsec_pwd_mgmt_v1_0_s_ifspec,
                          bindings,
                          &obj_uuids, &status);

    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1094);
        error(&status, ibs_msg_buf);
        exit(1);
    }

    /* register endpoints in local host's endpoint map */

    if (verbose) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1095);
        message(ibs_msg_buf);
    }
    rpc_ep_register(rsec_pwd_mgmt_v1_0_s_ifspec, bindings,
        &obj_uuids, annotation, &status);
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1096);
        error(&status, ibs_msg_buf);
        exit(1);
    }
#else /* CMVC 16603 & 17768, msg separation */
    if (status != error_status_ok) {
        error(&status, "Unable to use all protseqs");
        exit(1);
    }

    /* obtain vector of server binding handles */
    rpc_server_inq_bindings(&bindings, &status);
    if (status != error_status_ok) {
        error(&status, "Unable to obtain bindings");
        exit(1);
    }

    if (verbose) {
        message("Registering rsec_pwd_mgmt interface");
    }

    /* register rsec_pwd_mgmt interface with RPC runtime */
    rpc_server_register_if(rsec_pwd_mgmt_v1_0_s_ifspec, (uuid_t *) NULL,
        (rpc_mgr_epv_t) NULL, &status);
    if (status != error_status_ok) {
        error(&status, "Unable to register rsec_pwd_mgmt interface");
        exit(1);
    }

    /* export binding information to name service */

    /* -- the object uuid is what distinguishes the rsec_pwd_mgmt interface
     * -- exported by this strength/gen server instance from the same
     * -- interface exported by others
     */
    uuid_from_string((unsigned_char_p_t)obj_id_str, &obj_id, &status);
    if (status != error_status_ok) {
        error(&status, "Unable to obtain object uuid for binding export");
        exit(1);
    }
    obj_uuids.count = 1;
    obj_uuids.uuid[0] = &obj_id;

    /* first try an unexport to get rid of any potential information that
        might indicate a different pwd_strength location. */
    if (verbose) {
        message("unexporting old rsec_pwd_mgmt info from CDS namespace");
    }
    rpc_ns_binding_unexport(rpc_c_ns_syntax_default,
                            (unsigned_char_p_t) cds_path,
                            rsec_pwd_mgmt_v1_0_s_ifspec,
                            &obj_uuids, &lst);
    if (lst != rpc_s_interface_not_found) {
        /* Normal case is to find nothing, hence the "interface not found"
                error code.  Only other plausible return code is "success" */
        if (lst == error_status_ok) {
            /* Namespace info existed prior to the unexport. */
            error(NULL, "Warning: found rsec_pwd_mgmt info in namespace.");
            sprintf(logbuf, "         Previous %s run might have aborted;",
                                                        PWD_PROGNAME);
            error(NULL, logbuf);
            sprintf(logbuf, "         or, %s running elsewhere and is now",
                                                        PWD_PROGNAME);
            error(NULL, logbuf);
            error(NULL, "         disabled since namespace now contains new info.");
        }
    }
    if (verbose) {
        message("exporting new rsec_pwd_mgmt info to CDS namespace");
    }
    rpc_ns_binding_export(rpc_c_ns_syntax_default,
                          (unsigned_char_p_t) cds_path,
                          rsec_pwd_mgmt_v1_0_s_ifspec,
                          bindings,
                          &obj_uuids, &status);

    if (status != error_status_ok) {
        error(&status, "Unable to export binding information");
        exit(1);
    }

    /* register endpoints in local host's endpoint map */

    if (verbose) {
        message("registering endpoints for rsec_pwd_mgmt interface");
    }
    rpc_ep_register(rsec_pwd_mgmt_v1_0_s_ifspec, bindings,
        &obj_uuids, annotation, &status);
    if (status != error_status_ok) {
        error(&status, "Unable to register endpoints");
        exit(1);
    }
#endif /* CMVC 16603 & 17768 */

    /*
     * Register authentication info
     *
     * NOTE : the client encrypts any password arguments before
     * sending them to this server. (see rs_pwd_mgmt.c)
     * So there is no need for a get_key function.
     */
#ifdef IBMOS2
    /* CMVC 18549: Use DCELOCAL-based name for OS/2 example, too. */
    rpc_server_register_auth_info((unsigned_char_p_t) SERVER_PNAME,
        rpc_c_authn_dce_secret, (rpc_auth_key_retrieval_fn_t) NULL,
        fullkeytabnm, &status);
#else
    /* Altered OSF original to use define constant for keyfile name */
    rpc_server_register_auth_info((unsigned_char_p_t) SERVER_PNAME,
        rpc_c_authn_dce_secret, (rpc_auth_key_retrieval_fn_t) NULL,
        (void *) KEYTABNAME, &status);
#endif /* IBMOS2 */

#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1097);
        error(&status, ibs_msg_buf);
        exit(1);
    }

#ifdef IBMOS2
    if (port_test)
       dce_fprintf(stderr, ibm_msg_644); /* CMVC 14567 & 13302 */
#endif /* IBMOS2 */

    /* create mutex for writing to cache */
    if (pthread_mutex_init(&cache_mutex, pthread_mutexattr_default) < 0) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1098);
        error(NULL, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
#else /* CMVC 16603 & 17768, msg separation */
    if (status != error_status_ok) {
        error(&status, "Unable to register authentication info");
        exit(1);
    }

    /* create mutex for writing to cache */
    if (pthread_mutex_init(&cache_mutex, pthread_mutexattr_default) < 0) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1098);
        error(NULL, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
#endif /* CMVC 16603 & 17768 */

#if defined(IBMOS2) && defined(TERMINATE_CDS_STYLE)
    /*  Establish mutex and condition variabled used to wakeup main thread
        to do cleanup.    */
    if (pthread_mutex_init(&strength_sig_mutex,
                pthread_mutexattr_default) == -1) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1099);  /* CMVC 16603 & 17768 */
        error(NULL, ibs_msg_buf);               /* CMVC 16603 & 17768 */
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
    if (pthread_cond_init(&strength_sig_cond,
                pthread_condattr_default) == -1) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1100);  /* CMVC 16603 & 17768 */
        error(NULL, ibs_msg_buf);               /* CMVC 16603 & 17768 */
        error(NULL, "Unable to initialize mutex condition variable");
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
#endif

    /* create cache table */
    create_table(&status);
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
    if (status != error_status_ok) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1101);
        error(&status, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    /* start a thread to manage key */
    if (pthread_create(&manage_key_thread, pthread_attr_default,
                       manage_key, 0) < 0) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1102);
        error(NULL, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    /* start a thread to refresh identity */
    if (pthread_create(&refresh_thread, pthread_attr_default,
                       refresh_context, 0) < 0) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1103);
        error(NULL, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    /* start a thread to cleanup cache */
    if (pthread_create(&cleanup_cache_thread, pthread_attr_default,
                       cleanup_cache, 0) < 0) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1104);
        error(NULL, ibs_msg_buf);
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
#else /* CMVC 16603 & 17768, msg separation */
    if (status != error_status_ok) {
        error(&status, "Unable to initialize password cache");
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    /* start a thread to manage key */
    if (pthread_create(&manage_key_thread, pthread_attr_default,
                       manage_key, 0) < 0) {
        error(NULL, "Unable to start the manage key thread");
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    /* start a thread to refresh identity */
    if (pthread_create(&refresh_thread, pthread_attr_default,
                       refresh_context, 0) < 0) {
        error(NULL, "Unable to start the refresh context thread");
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }

    /* start a thread to cleanup cache */
    if (pthread_create(&cleanup_cache_thread, pthread_attr_default,
                       cleanup_cache, 0) < 0) {
        error(NULL, "Unable to start the cleanup cache thread");
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
#endif /* CMVC 16603 & 17768 */


#ifndef IBMOS2
    /* start a thread to catch asynchronous signals */
    if (pthread_create(&sig_thread, pthread_attr_default,
                       sigcatch, 0) < 0) {
#if defined(AIX_PROD) /* CMVC 16603 & 17768, msg separation */
        ibs_get_msg(ibs_msg_buf,ibm_msg_1105);
        error(NULL, ibs_msg_buf);
#else /* CMVC 16603 & 17768, msg separation */
        error(NULL, "Unable to start the signal catching thread");
#endif /* CMVC 16603 & 17768 */
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
#endif  /* not IBMOS2 */

#if defined(IBMOS2) && defined(TERMINATE_CDS_STYLE)
    /* start a thread to listen for RPCs */
    if (pthread_create(&rpclisten_thread, pthread_attr_default,
                       manage_rpclisten, 0) < 0) {
        ibs_get_msg(ibs_msg_buf,ibm_msg_1106);  /* CMVC 16603 & 17768 */
        error(NULL, ibs_msg_buf);               /* CMVC 16603 & 17768 */
        sec_login_purge_context(&login_context, &lst);
        exit(1);
    }
#endif

#ifdef IBMOS2
    if (port_test)
       dce_fprintf(stderr, ibm_msg_645); /* CMVC 14567 & 13302 */
#endif /* IBMOS2 */

#if defined(IBMOS2) && defined(DCESTARTSTOP)
    /* OS/2 DCE-delivered strength server puts out "server started" message
        via the interfaces that supports dcestart.  */
    dce_svc_printf(DCE_SVC(ibs_svc_handle, ""),
                   ibs_s_pwdstr,
                   svc_c_sev_notice | svc_c_route_stderr,
                   ibs_pwd_strength_started);           /* CMVC 18710 */
    /* PRINT_DCE_OS2_MSG(DCEOS2_STRENGTH_STARTED, stdout);   */
    dceos2_set_process_state(DCEOS2_STRENGTH_NAME, getpid(),
                             DCEOS2_PROCESS_INITED);
    if (!debug_mode) {
        /* If not debug mode, it's nice to see this in the program log also. */
        ibs_get_msg(ibs_msg_buf,ibm_msg_1107);          /* CMVC 16603 & 17768 */
        sprintf(logbuf, ibs_msg_buf, PWD_PROGNAME);     /* CMVC 16603 & 17768 */
        message(logbuf);
    }
#else
    /* Let's have other versions print out the 'init complete' message */
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
    ibs_get_msg(ibs_msg_buf,ibm_msg_1107);
    sprintf(logbuf, ibs_msg_buf, PWD_PROGNAME);
    message(logbuf);
    if (!debug_mode) {
        /* message() put the message in the log.  Put it on screen also */
        dce_printf(ibm_msg_1109, PWD_PROGNAME);
    }
#else /* CMVC 16603 & 17768, msg separation */
    sprintf(logbuf, "%s INITIALIZATION COMPLETE", PWD_PROGNAME);
    message(logbuf);
    if (!debug_mode) {
        /* message() put the message in the log.  Put it on screen also */
        printf("%s INITIALIZATION COMPLETE\n", PWD_PROGNAME);
    }
#endif /* CMVC 16603 & 17768 */
#endif

#if defined(IBMOS2) && defined(TERMINATE_CDS_STYLE)

    sigemptyset(&strength_sig_block);
    sigaddset(&strength_sig_block, SIGINT);
    sigaddset(&strength_sig_block, SIGTERM);
    sigaddset(&strength_sig_block, SIGBREAK);
    pthread_mutex_lock(&strength_sig_mutex);
    strength_sig_init = 1;
    while (strength_sig_init == 1) {
        pthread_cond_wait(&strength_sig_cond, &strength_sig_mutex);
    }
    if (strength_sig_init == 2) {
        strength_sig_init = 3;
        sigprocmask(SIG_BLOCK, &strength_sig_block, NULL);
    }
    pthread_mutex_unlock(&strength_sig_mutex);

    rpc_mgmt_stop_server_listening(NULL, &status);
    if (status != rpc_s_ok) {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603, msg separation */
        ibs_get_msg(ibs_msg_buf,ibm_msg_1057);
        error(&status, ibs_msg_buf);
#else /* CMVC 16603, msg separation */
        error(&status, "Unable to stop server listening");
#endif /* CMVC 16603 */
    }
    strength_unregister();
#ifdef DCESTARTSTOP
    dceos2_set_process_state(DCEOS2_STRENGTH_NAME, getpid(),
                                  DCEOS2_PROCESS_ENDED);
    dce_svc_printf(DCE_SVC(ibs_svc_handle, ""),
                   ibs_s_pwdstr,
                   svc_c_sev_notice | svc_c_route_stderr,
                   ibs_pwd_strength_ended);           /* CMVC 18710 */
    /* PRINT_DCE_OS2_MSG(DCEOS2_STRENGTH_ENDED, stdout);    */
#endif

#else  /* ! OS/2 DCE using CDS style termination */

    TRY {
#if defined(AIX_PROD) || defined(IBMOS2) /* CMVC 16603 & 17768, msg separation */
        if (verbose) {
            ibs_get_msg(ibs_msg_buf,ibm_msg_1111);
            message(ibs_msg_buf);
        }

        /* listen for requests */
        rpc_server_listen(rpc_c_listen_max_calls_default, &status);

        ibs_get_msg(ibs_msg_buf,ibm_msg_1064);
        error(&status, ibs_msg_buf);
#else /* CMVC 16603 & 17768, msg separation */
        if (verbose) {
            message("Listening on rsec_pwd_mgmt interface");
        }

        /* listen for requests */
        rpc_server_listen(rpc_c_listen_max_calls_default, &status);

        error(&status, "Unable to listen for requests");
#endif /* CMVC 16603 & 17768 */

    } FINALLY {

        if (verbose) {
            dce_fprintf(stderr, ibm_msg_646, PWD_PROGNAME, status); /* CMVC 14567 & 13302 */
        }
        strength_unregister();

    } ENDTRY;

#endif  /* OS/2 DCE using CDS style termination */

    return(0);
}
