summaryrefslogtreecommitdiffstats
path: root/src/kadmin/passwd
diff options
context:
space:
mode:
authorMarc Horowitz <marc@mit.edu>1996-07-22 20:49:46 +0000
committerMarc Horowitz <marc@mit.edu>1996-07-22 20:49:46 +0000
commitedf8b4d8a6a665c2aa150993cd813ea6c5cf12e1 (patch)
tree6c2974a97b448c040fa4a31708ec5e02f187526c /src/kadmin/passwd
parent013bb1391582ed9e653ae706e398ddb8d08cfcc9 (diff)
downloadkrb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.tar.gz
krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.tar.xz
krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.zip
this commit includes all the changes on the OV_9510_INTEGRATION and
OV_MERGE branches. This includes, but is not limited to, the new openvision admin system, and major changes to gssapi to add functionality, and bring the implementation in line with rfc1964. before committing, the code was built and tested for netbsd and solaris. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8774 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/kadmin/passwd')
-rw-r--r--src/kadmin/passwd/ChangeLog20
-rw-r--r--src/kadmin/passwd/Kpasswd46
-rw-r--r--src/kadmin/passwd/Makefile.in23
-rw-r--r--src/kadmin/passwd/Makefile.ov34
-rw-r--r--src/kadmin/passwd/configure.in13
-rw-r--r--src/kadmin/passwd/kpasswd.c280
-rw-r--r--src/kadmin/passwd/kpasswd_strings.et76
-rw-r--r--src/kadmin/passwd/tty_kpasswd.c80
-rw-r--r--src/kadmin/passwd/unit-test/Makefile.ov23
-rw-r--r--src/kadmin/passwd/unit-test/config/unix.exp36
-rw-r--r--src/kadmin/passwd/unit-test/helpers.exp217
-rw-r--r--src/kadmin/passwd/unit-test/kpasswd.0/changing.exp102
-rw-r--r--src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp29
-rw-r--r--src/kadmin/passwd/unit-test/kpasswd.0/principal.exp55
-rw-r--r--src/kadmin/passwd/unit-test/kpasswd.0/usage.exp26
-rw-r--r--src/kadmin/passwd/xm_kpasswd.c450
16 files changed, 1510 insertions, 0 deletions
diff --git a/src/kadmin/passwd/ChangeLog b/src/kadmin/passwd/ChangeLog
new file mode 100644
index 000000000..d0f4cbafc
--- /dev/null
+++ b/src/kadmin/passwd/ChangeLog
@@ -0,0 +1,20 @@
+Mon Jul 22 04:07:02 1996 Marc Horowitz <marc@mit.edu>
+
+ * tty_kpasswd.c: main returns int, not void
+
+Thu Jul 18 19:46:24 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed ET_RULES, replaced with AC_PROG_AWK
+
+Wed Jul 10 01:28:12 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.in, configure.in: added autoconf support
+
+Tue Jul 9 15:03:13 1996 Marc Horowitz <marc@mit.edu>
+
+ * kpasswd.c, tty_kpasswd.c, xm_kpasswd.c: renamed
+ <ovsec_admin/foo.h> to <kadm5/foo.h>
+
+ * configure.in (CONFIG_DIRS): build the subdirs for the new admin
+ system, not the old one.
+
diff --git a/src/kadmin/passwd/Kpasswd b/src/kadmin/passwd/Kpasswd
new file mode 100644
index 000000000..a7ec03161
--- /dev/null
+++ b/src/kadmin/passwd/Kpasswd
@@ -0,0 +1,46 @@
+*xm_ovpasswd.title: PW-CHG-GUI
+*form.shadowThickness: 2
+
+*foreground: black
+*background: grey80
+*topShadowColor: grey95
+*bottomShadowColor: grey20
+*fontList: -*-helvetica-medium-r-*-*-14-*
+*main_lbl.fontList: -*-helvetica-bold-r-*-*-14-*
+*XmForm.Spacing: 5
+
+*main_lbl.labelString: Changing password.
+*old_lbl.labelString: Old password:
+*new_lbl.labelString: New password:
+*again_lbl.labelString: New password (again):
+*sep.leftOffset: 0
+*sep.rightOffset: 0
+*Quit.labelString: Quit
+*Help.labelString: Help
+
+*main_lbl.alignment: ALIGNMENT_CENTER
+*lbl_form*alignment: ALIGNMENT_END
+*scroll_win.shadowThickness: 0
+
+*scroll_text.value: \
+Enter your old password below, and press return. You will not be able to see what you\n\
+are typing. After correctly entering your old password, you will be prompted twice for\n\
+your new password. Other messages and directions will appear in this space as necessary.
+*scroll_text.rows: 5
+*scroll_text.columns: 66
+*scroll_text.scrollHorizontal: FALSE
+*scroll_text.cursorPositionVisible: FALSE
+
+*help_dlg_popup.title: PW-CHG-GUI Help
+*help_dlg.messageString: \
+Welcome to the Kerberos password changing GUI.\n\
+\n\
+In the main window, enter your old password when prompted. After verifying\n\
+your old password, the policy governing your password will be displayed, and\n\
+you will be prompted for a new password. You will then be asked to enter it\n\
+a second time, to make sure you have not made any typos. Assuming that\n\
+your new password complies with your password policy, you should receive\n\
+an acknowledgement that your password has been changed.\n\
+\n\
+If an error occurs, the process will start over from the beginning. You may\n\
+exit the application at any time by pressing the "Quit" button.
diff --git a/src/kadmin/passwd/Makefile.in b/src/kadmin/passwd/Makefile.in
new file mode 100644
index 000000000..46e962070
--- /dev/null
+++ b/src/kadmin/passwd/Makefile.in
@@ -0,0 +1,23 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) -I. -DUSE_KADM5_API_VERSION=1
+
+PROG = kpasswd
+OBJS = tty_kpasswd.o kpasswd.o kpasswd_strings.o
+
+all:: $(PROG).local $(PROG)
+
+kpasswd_strings.c kpasswd_strings.h: $(srcdir)/kpasswd_strings.et
+
+$(OBJS): kpasswd_strings.h
+
+$(PROG).local: $(OBJS) $(SRVDEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS)
+
+$(PROG): $(OBJS) $(CLNTDEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS)
+
+install::
+ $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+
+clean::
+ $(RM) kpasswd_strings.c kpasswd_strings.h $(PROG).local $(PROG) $(OBJS)
diff --git a/src/kadmin/passwd/Makefile.ov b/src/kadmin/passwd/Makefile.ov
new file mode 100644
index 000000000..8841a4762
--- /dev/null
+++ b/src/kadmin/passwd/Makefile.ov
@@ -0,0 +1,34 @@
+TOP = ..
+include $(TOP)/config.mk/template
+
+CFLAGS := $(CFLAGS) -DUSE_KADM5_API_VERSION=1
+
+# This used as a string table, not an error table
+ETABLES = kpasswd_strings.et
+expand ErrorTables
+
+kpasswd.o: kpasswd_strings.h
+depend:: kpasswd_strings.h
+
+PROG = kpasswd
+SRCS = tty_kpasswd.c kpasswd.c kpasswd_strings.c
+OBJS = tty_kpasswd.o kpasswd.o kpasswd_strings.o
+LIBS = $(LIBADMCLNT) $(LIBCOM_ERR) $(LIBGSSAPI_KRB5) $(LIBRPCLIB) \
+ $(LIBDYN) $(LIBDB) $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \
+ $(LIBISODE) $(NDBMLIB) $(BSDLIB) $(NETLIB)
+
+expand NormalProgram
+
+ifndef OMIT_XM_KPASSWD
+PROG = xm_kpasswd
+CFLAGS := -I$(XM_INC) -I$(XT_INC) -I$(X_INC) $(CFLAGS)
+SRCS := xm_kpasswd.c kpasswd.c kpasswd_strings.c
+OBJS := xm_kpasswd.o kpasswd.o kpasswd_strings.o
+LIBS := $(LIBS) $(XM_LIB) $(XT_LIB) $(X_LIB) $(REGEXLIB)
+
+expand NormalProgram
+endif
+
+SUBDIRS = unit-test
+
+expand SubdirTarget
diff --git a/src/kadmin/passwd/configure.in b/src/kadmin/passwd/configure.in
new file mode 100644
index 000000000..fe1389a83
--- /dev/null
+++ b/src/kadmin/passwd/configure.in
@@ -0,0 +1,13 @@
+AC_INIT(kpasswd.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+AC_PROG_AWK
+USE_KADMCLNT_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_DYN_LIBRARY
+USE_KDB5_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/passwd/kpasswd.c b/src/kadmin/passwd/kpasswd.c
new file mode 100644
index 000000000..f87ad1cb0
--- /dev/null
+++ b/src/kadmin/passwd/kpasswd.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ *
+ *
+ */
+
+static char rcsid[] = "$Id$";
+
+#include <kadm5/admin.h>
+#include <krb5.h>
+
+#include "kpasswd_strings.h"
+#define string_text error_message
+#define initialize_kpasswd_strings initialize_kpws_error_table
+
+#include <stdio.h>
+#include <pwd.h>
+#include <string.h>
+
+extern char *whoami;
+
+extern void display_intro_message();
+extern long read_old_password();
+extern long read_new_password();
+
+#define MISC_EXIT_STATUS 6
+
+/*
+ * Function: kpasswd
+ *
+ * Purpose: Initialize and call lower level routines to change a password
+ *
+ * Arguments:
+ *
+ * context (r) krb5_context to use
+ * argc/argv (r) principal name to use, optional
+ * read_old_password (f) function to read old password
+ * read_new_password (f) function to read new and change password
+ * display_intro_message (f) function to display intro message
+ * whoami (extern) argv[0]
+ *
+ * Returns:
+ * exit status of 0 for success
+ * 1 principal unknown
+ * 2 old password wrong
+ * 3 cannot initialize admin server session
+ * 4 new passwd mismatch or error trying to change pw
+ * 5 password not typed
+ * 6 misc error
+ * 7 incorrect usage
+ *
+ * Requires:
+ * Passwords cannot be more than 255 characters long.
+ *
+ * Effects:
+ *
+ * If argc is 2, the password for the principal specified in argv[1]
+ * is changed; otherwise, the principal of the default credential
+ * cache or username is used. display_intro_message is called with
+ * the arguments KPW_STR_CHANGING_PW_FOR and the principal name.
+ * read_old_password is then called to prompt for the old password.
+ * The admin system is then initialized, the principal's policy
+ * retrieved and explained, if appropriate, and finally
+ * read_new_password is called to read the new password and change the
+ * principal's password (presumably ovsec_kadm_chpass_principal).
+ * admin system is de-initialized before the function returns.
+ *
+ * Modifies:
+ *
+ * Changes the principal's password.
+ *
+ */
+int
+kpasswd(context, argc, argv)
+ krb5_context context;
+ int argc;
+ char *argv[];
+{
+ int code;
+ krb5_ccache ccache = NULL;
+ krb5_principal princ = 0;
+ char *princ_str;
+ struct passwd *pw = 0;
+ int pwsize;
+ char password[255]; /* I don't really like 255 but that's what kinit uses */
+ char msg_ret[1024], admin_realm[1024];
+ ovsec_kadm_principal_ent_t principal_entry = NULL;
+ ovsec_kadm_policy_ent_t policy_entry = NULL;
+ void *server_handle;
+
+ if (argc > 2) {
+ com_err(whoami, KPW_STR_USAGE, 0);
+ return(7);
+ /*NOTREACHED*/
+ }
+
+ krb5_init_ets(context);
+
+ /************************************
+ * Get principal name to change *
+ ************************************/
+
+ /* Look on the command line first, followed by the default credential
+ cache, followed by defaulting to the Unix user name */
+
+ if (argc == 2)
+ princ_str = strdup(argv[1]);
+ else {
+ code = krb5_cc_default(context, &ccache);
+ /* If we succeed, find who is in the credential cache */
+ if (code == 0) {
+ /* Get default principal from cache if one exists */
+ code = krb5_cc_get_principal(context, ccache, &princ);
+ /* if we got a principal, unparse it, otherwise get out of the if
+ with an error code */
+ (void) krb5_cc_close(context, ccache);
+ if (code == 0) {
+ code = krb5_unparse_name(context, princ, &princ_str);
+ if (code != 0) {
+ com_err(whoami, code, string_text(KPW_STR_UNPARSE_NAME));
+ return(MISC_EXIT_STATUS);
+ }
+ }
+ }
+
+ /* this is a crock.. we want to compare against */
+ /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */
+ /* both the file and stdio types return FCC_NOFILE. If there is */
+ /* ever another ccache type (or if the error codes are ever */
+ /* fixed), this code will have to be updated. */
+ if (code && code != KRB5_FCC_NOFILE) {
+ com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC));
+ return(MISC_EXIT_STATUS);
+ }
+
+ /* if either krb5_cc failed check the passwd file */
+ if (code != 0) {
+ pw = getpwuid((int) getuid());
+ if (pw == NULL) {
+ com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE));
+ return(MISC_EXIT_STATUS);
+ }
+ princ_str = strdup(pw->pw_name);
+ }
+ }
+
+ display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str);
+
+ /* Need to get a krb5_principal, unless we started from with one from
+ the credential cache */
+
+ if (! princ) {
+ code = krb5_parse_name (context, princ_str, &princ);
+ if (code != 0) {
+ com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str);
+ free(princ_str);
+ return(MISC_EXIT_STATUS);
+ }
+ }
+
+ pwsize = sizeof(password);
+ code = read_old_password(context, password, &pwsize);
+
+ if (code != 0) {
+ memset(password, 0, sizeof(password));
+ com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD));
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ return(MISC_EXIT_STATUS);
+ }
+ if (pwsize == 0) {
+ memset(password, 0, sizeof(password));
+ com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ));
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ return(5);
+ }
+
+ admin_realm[0] = '\0';
+ strncat(admin_realm, krb5_princ_realm(context, princ)->data,
+ krb5_princ_realm(context, princ)->length);
+
+ code = ovsec_kadm_init(princ_str, password, OVSEC_KADM_CHANGEPW_SERVICE,
+ admin_realm /* we probably should take a -r */
+ /* someday */,
+ OVSEC_KADM_STRUCT_VERSION,
+ OVSEC_KADM_API_VERSION_1,
+ &server_handle);
+ if (code != 0) {
+ if (code == OVSEC_KADM_BAD_PASSWORD)
+ com_err(whoami, 0, string_text(KPW_STR_OLD_PASSWORD_INCORRECT));
+ else
+ com_err(whoami, 0, string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER), admin_realm,
+ error_message(code));
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ return((code == OVSEC_KADM_BAD_PASSWORD)?2:3);
+ }
+
+ /* Explain policy restrictions on new password if any. */
+ /* Note: copy of this exists in login (kverify.c/get_verified_in_tkt). */
+
+ code = ovsec_kadm_get_principal(server_handle, princ, &principal_entry);
+ if (code != 0) {
+ com_err(whoami, 0,
+ string_text((code == OVSEC_KADM_UNK_PRINC)
+ ? KPW_STR_PRIN_UNKNOWN : KPW_STR_CANT_GET_POLICY_INFO),
+ princ_str);
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ (void) ovsec_kadm_destroy(server_handle);
+ return((code == OVSEC_KADM_UNK_PRINC) ? 1 : MISC_EXIT_STATUS);
+ }
+ if ((principal_entry->aux_attributes & OVSEC_KADM_POLICY) != 0) {
+ code = ovsec_kadm_get_policy(server_handle,
+ principal_entry->policy, &policy_entry);
+ if (code != 0) {
+ /* doesn't matter which error comes back, there's no nice recovery
+ or need to differentiate to the user */
+ com_err(whoami, 0,
+ string_text(KPW_STR_CANT_GET_POLICY_INFO), princ_str);
+ (void) ovsec_kadm_free_principal_ent(server_handle, principal_entry);
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ (void) ovsec_kadm_destroy(server_handle);
+ return(MISC_EXIT_STATUS);
+ }
+ com_err(whoami, 0, string_text(KPW_STR_POLICY_EXPLANATION),
+ princ_str, principal_entry->policy,
+ policy_entry->pw_min_length, policy_entry->pw_min_classes);
+ if (code = ovsec_kadm_free_principal_ent(server_handle, principal_entry)) {
+ (void) ovsec_kadm_free_policy_ent(server_handle, policy_entry);
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
+ (void) ovsec_kadm_destroy(server_handle);
+ return(MISC_EXIT_STATUS);
+ }
+ if (code = ovsec_kadm_free_policy_ent(server_handle, policy_entry)) {
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_POLICY));
+ (void) ovsec_kadm_destroy(server_handle);
+ return(MISC_EXIT_STATUS);
+ }
+ }
+ else {
+ /* kpasswd *COULD* output something here to encourage the choice
+ of good passwords, in the absence of an enforced policy. */
+ if (code = ovsec_kadm_free_principal_ent(server_handle,
+ principal_entry)) {
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
+ (void) ovsec_kadm_destroy(server_handle);
+ return(MISC_EXIT_STATUS);
+ }
+ }
+
+ pwsize = sizeof(password);
+ code = read_new_password(server_handle, password, &pwsize, msg_ret, princ);
+ memset(password, 0, sizeof(password));
+
+ if (code)
+ com_err(whoami, 0, msg_ret);
+
+ krb5_free_principal(context, princ);
+ free(princ_str);
+
+ (void) ovsec_kadm_destroy(server_handle);
+
+ if (code == KRB5_LIBOS_CANTREADPWD)
+ return(5);
+ else if (code)
+ return(4);
+ else
+ return(0);
+}
diff --git a/src/kadmin/passwd/kpasswd_strings.et b/src/kadmin/passwd/kpasswd_strings.et
new file mode 100644
index 000000000..b78aa9d6f
--- /dev/null
+++ b/src/kadmin/passwd/kpasswd_strings.et
@@ -0,0 +1,76 @@
+#
+# Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
+#
+# String table of messages for kpasswd
+
+
+error_table kpws
+
+# /* M1 */
+error_code KPW_STR_USAGE, "Usage: kpasswd [principal_name]."
+
+error_code KPW_STR_PRIN_UNKNOWN,
+ "Kerberos principal name %s is not recognized."
+# /* <name> */
+
+# /* M2 */
+error_code KPW_STR_WHILE_LOOKING_AT_CC,
+ "while reading principal name from credential cache."
+
+# /* M4 */
+error_code KPW_STR_OLD_PASSWORD_INCORRECT,
+ "Old Kerberos password is incorrect. Please try again."
+
+# /* M5 */
+error_code KPW_STR_CANT_OPEN_ADMIN_SERVER,
+"Cannot establish a session with the Kerberos administrative server for\n\
+realm %s. %s."
+# /* <realm-name>, <Specific error message from admin server library>. */
+
+# /* M6 */
+error_code KPW_STR_NEW_PASSWORD_MISMATCH,
+ "New passwords do not match - password not changed.\n"
+
+# /* M7 */
+error_code KPW_STR_PASSWORD_CHANGED, "Kerberos password changed.\n"
+
+# /* M13 */
+error_code KPW_STR_PASSWORD_NOT_CHANGED, "Password not changed."
+
+error_code KPW_STR_PARSE_NAME, "when parsing name %s."
+error_code KPW_STR_UNPARSE_NAME, "when unparsing name."
+error_code KPW_STR_NOT_IN_PASSWD_FILE, "Unable to identify user from password file."
+
+# /* M3 */
+error_code KPW_STR_CHANGING_PW_FOR, "Changing password for %s."
+# /* principal@realm */
+
+error_code KPW_STR_OLD_PASSWORD_PROMPT, "Old password:"
+error_code KPW_STR_WHILE_READING_PASSWORD, "while reading new password."
+
+# /* M4 */
+error_code KPW_STR_NO_PASSWORD_READ,
+"You must type a password. Passwords must be at least one character long."
+
+# /* M14 */
+error_code KPW_STR_WHILE_TRYING_TO_CHANGE, "while trying to change password."
+
+error_code KPW_STR_WHILE_DESTROYING_ADMIN_SESSION,
+"while closing session with admin server and destroying tickets."
+
+error_code KPW_STR_WHILE_FREEING_PRINCIPAL,
+"while freeing admin principal entry"
+
+error_code KPW_STR_WHILE_FREEING_POLICY,
+"while freeing admin policy entry"
+
+error_code KPW_STR_CANT_GET_POLICY_INFO,
+"Could not get password policy information for principal %s."
+# /* principal@realm */
+
+error_code KPW_STR_POLICY_EXPLANATION,
+"%s's password is controlled by the policy %s, which\nrequires a minimum of %u characters from at least %u classes (the five classes\nare lowercase, uppercase, numbers, punctuation, and all other characters)."
+# /* principal_name policy_name min_length min_classes */
+
+end
+
diff --git a/src/kadmin/passwd/tty_kpasswd.c b/src/kadmin/passwd/tty_kpasswd.c
new file mode 100644
index 000000000..6d865a020
--- /dev/null
+++ b/src/kadmin/passwd/tty_kpasswd.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ *
+ *
+ */
+
+static char rcsid[] = "$Id$";
+
+#include <kadm5/admin.h>
+#include <krb5.h>
+
+#include "kpasswd_strings.h"
+#define string_text error_message
+#define initialize_kpasswd_strings initialize_kpws_error_table
+
+#include <stdio.h>
+#include <pwd.h>
+#include <string.h>
+
+char *whoami;
+
+void display_intro_message(fmt_string, arg_string)
+ char *fmt_string;
+ char *arg_string;
+{
+ com_err(whoami, 0, fmt_string, arg_string);
+}
+
+long read_old_password(context, password, pwsize)
+ krb5_context context;
+ char *password;
+ int *pwsize;
+{
+ long code = krb5_read_password(context,
+ (char *)string_text(KPW_STR_OLD_PASSWORD_PROMPT),
+ 0, password, pwsize);
+ return code;
+}
+
+long read_new_password(server_handle, password, pwsize, msg_ret, princ)
+ void *server_handle;
+ char *password;
+ int *pwsize;
+ char *msg_ret;
+ krb5_principal princ;
+{
+ return (ovsec_kadm_chpass_principal_util(server_handle, princ, NULL,
+ NULL /* don't need new pw back */,
+ msg_ret));
+}
+
+
+/*
+ * main() for tty version of kpasswd.c
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_context context;
+ int retval;
+
+ initialize_kpasswd_strings();
+
+ whoami = (whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0];
+
+ if (retval = krb5_init_context(&context)) {
+ com_err(whoami, retval, "initializing krb5 context");
+ exit(retval);
+ }
+ retval = kpasswd(context, argc, argv);
+
+ if (!retval)
+ printf(string_text(KPW_STR_PASSWORD_CHANGED));
+
+ exit(retval);
+}
diff --git a/src/kadmin/passwd/unit-test/Makefile.ov b/src/kadmin/passwd/unit-test/Makefile.ov
new file mode 100644
index 000000000..db042720b
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/Makefile.ov
@@ -0,0 +1,23 @@
+#
+# $Id$
+#
+
+TOP = ../..
+include $(TOP)/config.mk/template
+
+USER = root
+
+unit-test:: unit-test-setup unit-test-body unit-test-cleanup
+
+unit-test-body::
+ $(RUNTEST) KPASSWD=../kpasswd \
+ KINIT=$(TOP)/../clients/kinit/kinit \
+ KDESTROY=$(TOP)/../clients/kdestroy/kdestroy \
+ USER=$(USER) --tool kpasswd
+
+unit-test-setup::
+ $(START_SERVERS)
+ echo "source $(TCLUTIL); catch {ovsec_kadm_init admin admin \$$OVSEC_KADM_ADMIN_SERVICE null \$$OVSEC_KADM_STRUCT_VERSION \$$OVSEC_KADM_API_VERSION_1 server_handle; ovsec_kadm_create_principal \$$server_handle [simple_principal $(USER)] {OVSEC_KADM_PRINCIPAL} $(USER); ovsec_kadm_destroy \$$server_handle;}; if {[info exists errorInfo]} { puts stderr \$$errorInfo; exit 1; }" | $(CLNTTCL)
+
+unit-test-cleanup::
+ $(STOP_SERVERS)
diff --git a/src/kadmin/passwd/unit-test/config/unix.exp b/src/kadmin/passwd/unit-test/config/unix.exp
new file mode 100644
index 000000000..c77aa016a
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/config/unix.exp
@@ -0,0 +1,36 @@
+#
+# kpasswd_version -- extract and print the version number of kpasswd
+#
+
+proc kpasswd_version {} {
+ global KPASSWD
+ catch "exec ident $KPASSWD" tmp
+ if [regexp {Id: kpasswd.c,v ([0-9]+\.[0-9]+)} $tmp \
+ dummy version] then {
+ clone_output "$KPASSWD version $version\n"
+ } else {
+ clone_output "$KPASSWD version <unknown>\n"
+ }
+}
+#
+# kpasswd_load -- loads the program
+#
+proc kpasswd_load {} {
+ #
+}
+
+# kpasswd_exit -- clean up and exit
+proc kpasswd_exit {} {
+ #
+}
+
+#
+# kpasswd_start -- start kpasswd running
+#
+proc kpasswd_start { args } {
+ global KPASSWD
+ global spawn_id
+
+ verbose "% $KPASSWD $args" 1
+ eval spawn $KPASSWD $args
+}
diff --git a/src/kadmin/passwd/unit-test/helpers.exp b/src/kadmin/passwd/unit-test/helpers.exp
new file mode 100644
index 000000000..9dcfbcf01
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/helpers.exp
@@ -0,0 +1,217 @@
+#
+# $Id$
+#
+
+global s
+set s "\[\r\n\t\ \]"
+
+if {[info commands exp_version] != {}} {
+ set exp_version_4 [regexp {^4} [exp_version]]
+} else {
+ set exp_version_4 [regexp {^4} [expect_version]]
+}
+
+# Backward compatibility until we're using expect 5 everywhere
+if {$exp_version_4} {
+ global wait_error_index wait_errno_index wait_status_index
+ set wait_error_index 0
+ set wait_errno_index 1
+ set wait_status_index 1
+} else {
+ set wait_error_index 2
+ set wait_errno_index 3
+ set wait_status_index 3
+}
+
+proc myfail { comment } {
+ global mytest_name
+ global mytest_status
+ wait
+ fail "$mytest_name: $comment"
+ set mytest_status 1
+}
+
+proc mypass {} {
+}
+
+##
+## When you expect on an id, and eof is detected, the spawn_id is closed.
+## It may be waited for, but calling expect or close on this id is an ERROR!
+##
+
+proc mytest { name kpargs status args } {
+ global spawn_id
+ global timeout
+ global mytest_name
+ global mytest_status
+ global wait_error_index wait_errno_index wait_status_index
+
+ verbose "starting test: $name"
+
+ set mytest_name "$name"
+
+ eval kpasswd_start $kpargs
+
+ # at the end, eof is success
+
+ lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } }
+
+ # for each test argument....
+ # rep invariant: when this foreach ends, the id is close'd, but
+ # not wait'ed.
+
+ foreach test $args {
+ set mytest_status 0
+
+ # treat the arg as an expect parameter
+ # if failure, the process will be closed and waited.
+
+ uplevel 1 "expect {
+ $test
+ timeout { close; myfail \"timeout\"}
+ eof { myfail \"eof read before expected message string\" }
+ }"
+
+ if {$mytest_status == 1} { return }
+ }
+
+ # at this point, the id is closed and we can wait on it.
+
+ set ret [wait]
+ verbose "% Exit $ret" 1
+ if {[lindex $ret $wait_error_index] == -1} {
+ fail "$name: wait returned error [lindex $ret $wait_errno_index]"
+ } else {
+ if { [lindex $ret $wait_status_index] == $status ||
+ (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } {
+ pass "$name"
+ } else {
+ fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status"
+ }
+ }
+}
+
+proc kinit { princ pass } {
+ global env;
+ global KINIT
+ spawn -noecho $KINIT $princ;
+
+ expect {
+ -re {Password for .*: $}
+ {send "$pass\n"}
+ timeout {puts "Timeout waiting for prompt" ; close }
+ }
+
+ # this necessary so close(1) in the child will not sleep waiting for
+ # the parent, which is us, to read pending data.
+
+ expect {
+ eof {}
+ }
+ wait
+}
+
+proc kdestroy {} {
+ global KDESTROY
+ global errorCode errorInfo
+ global env
+
+ if {[info exists errorCode]} {
+ set saveErrorCode $errorCode
+ }
+ if {[info exists errorInfo]} {
+ set saveErrorInfo $errorInfo
+ }
+ catch "system $KDESTROY 2>/dev/null"
+ if {[info exists saveErrorCode]} {
+ set errorCode $saveErrorCode
+ } elseif {[info exists errorCode]} {
+ unset errorCode
+ }
+ if {[info exists saveErrorInfo]} {
+ set errorInfo $saveErrorInfo
+ } elseif {[info exists errorInfo]} {
+ unset errorInfo
+ }
+}
+
+global initerr_str
+global initerr_regexp
+set initerr_str "Cannot establish a session with the Kerberos administrative server for realm \[^\r\n\]*\\. "
+set initerr_regexp "Cannot establish a session with the Kerberos administrative server for$s+realm \[^\r\n\]*\\.$s+"
+
+proc test_win { args name princ pass1 { pass2 "\001\001" } } {
+ global s
+ global initerr_regexp
+
+ if { $pass2 == "\001\001" } { set pass2 "$pass1" }
+
+ mytest "$name" $args 0 {
+ -re "Changing password for $princ.*\\.$s+Old password:"
+ { send "$pass1\n" }
+ } {
+ -re "Old Kerberos password is incorrect. Please try again."
+ { close; myfail "Old password incorrect" }
+ -re "${initerr_regexp}(.+\[^\r\n\t\ \])\r\n"
+ { close; myfail "init error: $expect_out(1,string)" }
+ -re "^$s+New password:"
+ { send "$pass2\n" }
+ -re "^$s+.*$s+.*$s+.*$s+New password:"
+ { send "$pass2\n" }
+ } {
+ -re "^$s+New password \\(again\\):"
+ { send "$pass2\n" }
+ } {
+ -re "^$s+Kerberos password changed."
+ { mypass }
+ -re "^$s+Password changed."
+ { close; myfail "Wrong message on success." }
+ }
+}
+
+proc test_initerr { args name princ pass status err } {
+ global s
+ global initerr_regexp
+
+ regsub -all "$s+" $err "$s+" err2
+
+ mytest "$name" $args $status {
+ -re "Changing password for $princ.*\\.$s+Old password:"
+ { send "$pass\n" }
+ } {
+ -re "$err2"
+ { mypass }
+ -re "Old Kerberos password is incorrect. Please try again."
+ { close; myfail "Old password incorrect" }
+ -re "${initerr_regexp}(.+)\r\n"
+ { close; myfail "init error: $expect_out(1,string)" }
+ }
+}
+
+proc test_3pass { args name princ pass1 pass2 pass3 status err } {
+ global s
+ global initerr_regexp
+
+ regsub -all "$s+" $err "$s+" err2
+
+ mytest "$name" $args $status {
+ -re "Changing password for $princ.*\\.$s+Old password:"
+ { send "$pass1\n" }
+ } {
+ -re "Old Kerberos password is incorrect. Please try again."
+ { close; myfail "Old password incorrect" }
+ -re "${initerr_regexp}(.+)\r\n"
+ { close; myfail "init error: $expect_out(1,string)" }
+ -re "^$s+New password:"
+ { send "$pass2\n" }
+ -re "^$s+.*$s+.*$s+.*$s+New password:"
+ { send "$pass2\n" }
+ } {
+ -re "^$s+New password \\(again\\):"
+ { send "$pass3\n" }
+ } {
+ -re "$s+$err2"
+ { mypass }
+ }
+}
+
diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp b/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp
new file mode 100644
index 000000000..4f0354c63
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp
@@ -0,0 +1,102 @@
+#
+# $Id$
+#
+
+set timeout 15
+
+load_lib "helpers.exp"
+
+if [info exist env(DEBUG)] { debug 1 }
+
+#
+# Here are the tests
+#
+
+test_3pass {test2} {D.5: different new passwords} test2 test2 test2 foobar \
+ 4 {New passwords do not match - password not changed.}
+
+test_3pass {test2} {D.7.5: empty/empty} test2 test2 {} {} \
+ 5 {You must type a password. Passwords must be at least one character long.}
+
+test_3pass {test2} {D.6: empty/non-empty} test2 test2 {} test2 \
+ 4 {New passwords do not match - password not changed.}
+
+test_3pass {test2} {D.7: non-empty/empty} test2 test2 test2 {} \
+ 4 {New passwords do not match - password not changed.}
+
+
+test_win {test1} {D.8: change password} test1 test1 newpass
+
+test_win {test1} {D.9: test changed password} test1 newpass test1
+
+mytest "D.22: No policy description was shown" test1 4 {
+ -re "Changing password for test1.*\\.$s+Old password:"
+ { send "test1\n" }
+} {
+ -re "$s+.*$s+.*$s+.*char.*classes.*"
+ { myfail "policy description displayed" }
+ timeout { mypass }
+} {
+ -re "^$s+New password:"
+ { send "newpass\n" }
+} {
+ -re "^$s+New password \\(again\\):"
+ { send "ssapwen\n" }
+} {
+ -re "$s+New passwords do not match - password not changed."
+ { mypass }
+}
+
+test_3pass {pol1} {D.10: new password too short} pol1 pol111111 que que \
+ 4 {New password is too short. Please choose a password which is at least [0-9]+ characters long.}
+
+test_3pass {pol1} {D.13: too few char classes in new password} pol1 \
+ pol111111 123456789 123456789 \
+ 4 {New password does not have enough character classes. The character classes are: - lower-case letters, - upper-case letters, - digits, - punctuation, and - all other characters \(e.g., control characters\). Please choose a password with at least [0-9]+ character classes.}
+
+test_3pass {pol1} {D.14: new password in dictionary} pol1 \
+ pol111111 Discordianism Discordianism \
+ 4 {New password was found in a dictionary of possible passwords and therefore may be easily guessed. Please choose another password. See the ovpasswd man page for help in choosing a good password.}
+
+test_win {pol1} {successful change} pol1 pol111111 polAAAAAA
+# fail "successful change: XXXX password history is majorly broken"
+
+test_3pass {pol1} {D.11: new password same as old} pol1 \
+ polAAAAAA polAAAAAA polAAAAAA \
+ 4 {New password was used previously. Please choose a different password.}
+
+test_3pass {pol1} {D.12: new password in history} pol1 \
+ polAAAAAA pol111111 pol111111 \
+ 4 {New password was used previously. Please choose a different password.}
+
+mytest "D.18: Policy description was shown" pol1 4 {
+ -re "Changing password for pol1.*\\.$s+Old password:"
+ { send "polAAAAAA\n" }
+} {
+ -re "$s+.*$s+.*$s+.*8 char.*2 classes.*$s+New password:"
+ { send "newpass1234\n" }
+} {
+ -re "^$s+New password \\(again\\):"
+ { send "newpass4321\n" }
+} {
+ -re "$s+New passwords do not match - password not changed."
+ { mypass }
+}
+
+# restore pol1's password to its initial value; see discussion in
+# secure-kpasswd/2204 about secure-releng/2191 if you are confused
+test_win {pol1} {successful change} pol1 polAAAAAA polBBBBBB
+test_win {pol1} {successful change} pol1 polBBBBBB polCCCCCC
+test_win {pol1} {successful change} pol1 polCCCCCC pol111111
+
+test_win {pol2} {successful change} pol2 pol222222 polbbbbbb
+
+test_3pass {pol2} {D.15: too soon to change password} pol2 \
+ polbbbbbb pol222222 pol222222 \
+ 4 {Password cannot be changed because it was changed too recently. Please wait until .*199[0-9] before you change it. If you need to change your password before then, contact your system security administrator.}
+
+verbose "(sleeping 30 seconds)"
+catch "exec sleep 30"
+
+test_win {pol2} {password min life passed} pol2 polbbbbbb pol222222
+
diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp b/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp
new file mode 100644
index 000000000..2cda17a6a
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp
@@ -0,0 +1,29 @@
+#
+# $Id$
+#
+
+set timeout 15
+
+load_lib "helpers.exp"
+
+if [info exist env(DEBUG)] { debug 1 }
+
+#
+# Here are the tests
+#
+
+test_initerr {test2} {C.4: empty old password (XXXX)} test2 {} \
+ 5 {You must type a password. Passwords must be at least one character long.}
+
+test_initerr {test2} {C.5: incorrect old password} test2 foobar \
+ 2 "Old Kerberos password is incorrect. Please try again."
+
+# set timeout 60
+#
+#test_initerr {test2@SECURE-TEST-DEAD.OV.COM} {C.8: server up, daemon down} \
+# test2 test2 \
+# 3 ""
+#
+#test_initerr {test2@SECURE-TEST-DOWN.OV.COM} {C.8.5: server down} \
+# test2 test2 \
+# 3 "${initerr_str}Cannot contact any KDC for requested realm"
diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp b/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp
new file mode 100644
index 000000000..e2bc20569
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp
@@ -0,0 +1,55 @@
+#
+# $Id$
+#
+
+set timeout 15
+
+load_lib "helpers.exp"
+
+if [info exist env(DEBUG)] { debug 1 }
+
+#
+# Here are the tests
+#
+
+if {[info exists env(KRB5CCNAME)]} {
+ unset env(KRB5CCNAME)
+}
+kdestroy
+
+#### no principal specified
+
+set whoami $USER
+test_win {} {B.7: default nonexisting ccache(1)} $whoami $whoami newpass
+test_win {} {B.7: default nonexisting ccache(2)} $whoami newpass $whoami
+
+kinit test2 test2
+test_win {} {B.4: default existing cache containing existing principal} \
+ test2 test2 newpass
+kdestroy
+
+set env(KRB5CCNAME) FILE:/tmp/ovsec_adm_test_ccache
+kinit test2 newpass
+test_win {} {B.3: specified existing cache containing existing principal} \
+ test2 newpass test2
+kdestroy
+unset env(KRB5CCNAME)
+
+#### principal on command line
+
+#
+test_win {test2} {B.14: existing principal, no realm} test2 test2 newpass
+
+#
+test_initerr {bogus} {B.15, C.6: non-existent principal, no realm} bogus bogus \
+ 3 "${initerr_str}Client not found in Kerberos database"
+
+#
+test_win {test2@SECURE-TEST.OV.COM} {B.16: existing principal, with realm} \
+ test2 newpass test2
+
+#
+test_initerr {bogus@SECURE-TEST.OV.COM} \
+ {B.17: non-existent principal, with realm} \
+ bogus bogus \
+ 3 "${initerr_str}Client not found in Kerberos database"
diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp b/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp
new file mode 100644
index 000000000..e132bab2f
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp
@@ -0,0 +1,26 @@
+#
+# $Id$
+#
+
+set timeout 15
+
+load_lib "helpers.exp"
+
+#
+# Here are the tests
+#
+
+mytest {A.1: two args} {foo bar} 7 {
+ -re {[a-z./]+passwd: Usage: [a-z./]+passwd \[principal_name\]} { mypass }
+}
+
+mytest {A.2: three args} {foo bar baz} 7 {
+ -re {[a-z./]+passwd: Usage: [a-z./]+passwd \[principal_name\]} { mypass }
+}
+
+set env(KRB5CCNAME) bogus_type:bogus_ccname
+mytest {B.5: malformed ccache name} {} 6 {
+ -re {[a-z./]+passwd: Unknown credential cache type while reading principal name from credential cache} { mypass }
+}
+unset env(KRB5CCNAME)
+
diff --git a/src/kadmin/passwd/xm_kpasswd.c b/src/kadmin/passwd/xm_kpasswd.c
new file mode 100644
index 000000000..0db1111c6
--- /dev/null
+++ b/src/kadmin/passwd/xm_kpasswd.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ *
+ *
+ */
+
+static char rcsid_2[] = "$Id$";
+
+#include <kadm5/admin.h>
+#include <krb5.h>
+
+#include "kpasswd_strings.h"
+#define string_text error_message
+#define initialize_kpasswd_strings initialize_kpws_error_table
+
+#include <stdio.h>
+#include <pwd.h>
+#include <string.h>
+
+char *whoami;
+
+#include <Xm/Xm.h>
+#include <Xm/MessageB.h>
+#include <Xm/ScrolledW.h>
+#include <Xm/Form.h>
+#include <Xm/Text.h>
+#include <Xm/PushB.h>
+#include <Xm/Label.h>
+#include <Xm/Separator.h>
+#include <X11/cursorfont.h>
+#include <X11/Shell.h>
+
+Widget toplevel, scroll_text, prompt_text;
+Widget quit_btn, help_btn, old_lbl, new_lbl, again_lbl, main_lbl;
+XtAppContext app_con;
+int looping;
+int retval=0;
+
+
+/***************************************************************************
+ *
+ * A few utility functions for setting/unsetting the busy cursor
+ * (i.e. the watch cursor).
+ */
+static void
+SetCursor(w,c)
+ Widget w;
+ Cursor c;
+{
+ while (XtIsSubclass(w, shellWidgetClass) != True)
+ w = XtParent(w);
+
+ XDefineCursor(XtDisplay(w), XtWindow(w), c);
+ XFlush(XtDisplay(w));
+}
+
+
+static void
+SetStandardCursor()
+{
+ static Cursor ArrowCursor = (Cursor)NULL;
+
+ if (ArrowCursor == (Cursor)NULL)
+ ArrowCursor = XCreateFontCursor(XtDisplay(toplevel), XC_top_left_arrow);
+ SetCursor(toplevel, ArrowCursor);
+}
+
+
+static void
+SetWatchCursor()
+{
+ static Cursor WatchCursor = (Cursor)NULL;
+
+ if (WatchCursor == (Cursor)NULL)
+ WatchCursor = XCreateFontCursor(XtDisplay(toplevel), XC_watch);
+ SetCursor(toplevel, WatchCursor);
+}
+
+
+/***************************************************************************
+ *
+ * Set up a com_err hook, for displaying to a motif scrolling widget.
+ */
+
+#if __STDC__
+# include <stdarg.h>
+#else /* varargs: not STDC or no <stdarg> */
+ /* Non-ANSI, always take <varargs.h> path. */
+# undef VARARGS
+# define VARARGS 1
+# include <varargs.h>
+#endif /* varargs */
+
+static void
+#ifdef __STDC__
+motif_com_err (const char *whoami, long code, const char *fmt, va_list args)
+#else
+motif_com_err (whoami, code, fmt, args)
+ const char *whoami;
+ long code;
+ const char *fmt;
+ va_list args;
+#endif
+{
+ XEvent event;
+ char buf[2048];
+
+ buf[0] = '\0';
+
+ if (whoami)
+ {
+ strcpy(buf, whoami);
+ strcat(buf, ": ");
+ }
+ if (code)
+ {
+ strcat(buf, error_message(code));
+ strcat(buf, " ");
+ }
+ if (fmt)
+ {
+ vsprintf(buf + strlen(buf), fmt, args);
+ }
+
+ XtVaSetValues(scroll_text, XmNvalue, buf, NULL);
+
+ for (; XtAppPending(app_con); )
+ {
+ XtAppNextEvent(app_con, &event);
+ XtDispatchEvent(&event);
+ }
+}
+
+
+/***************************************************************************
+ *
+ * Function to display help widget.
+ */
+static void
+help()
+{
+ static Widget help_dlg = NULL;
+
+ if (!help_dlg)
+ {
+ help_dlg = XmCreateInformationDialog(toplevel, "help_dlg", NULL,
+ 0);
+ XtUnmanageChild(XmMessageBoxGetChild(help_dlg, XmDIALOG_CANCEL_BUTTON));
+ XtUnmanageChild(XmMessageBoxGetChild(help_dlg, XmDIALOG_HELP_BUTTON));
+ }
+ XtManageChild(help_dlg);
+}
+
+
+/***************************************************************************
+ *
+ * Unset the global "looping" when we want to get out of reading a
+ * password.
+ */
+static void
+unset_looping()
+{
+ looping = 0;
+}
+
+
+/***************************************************************************
+ *
+ * Function to exit the gui. Callback on the "Exit" button.
+ */
+static void
+quit()
+{
+ exit(retval);
+}
+
+
+/***************************************************************************
+ *
+ * Set up motif widgets, callbacks, etc.
+ */
+static void
+create_widgets(argc, argv)
+ int *argc;
+ char *argv[];
+{
+ Widget form, lbl_form,
+ sep,
+ scroll_win;
+ Pixel bg;
+
+ toplevel = XtAppInitialize(&app_con, "Kpasswd", NULL, 0, argc, argv,
+ NULL, NULL, 0);
+ form = XtCreateManagedWidget("form", xmFormWidgetClass, toplevel, NULL, 0);
+ quit_btn = XtVaCreateManagedWidget("Quit", xmPushButtonWidgetClass,
+ form,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ NULL);
+ XtAddCallback(quit_btn, XmNactivateCallback, quit, 0);
+ help_btn = XtVaCreateManagedWidget("Help", xmPushButtonWidgetClass,
+ form,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ /* XmNshowAsDefault, TRUE, */
+ NULL);
+ XtAddCallback(help_btn, XmNactivateCallback, help, 0);
+ sep = XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass,
+ form,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_WIDGET,
+ XmNbottomWidget, quit_btn,
+ NULL);
+ lbl_form = XtVaCreateManagedWidget("lbl_form", xmFormWidgetClass,
+ form,
+ XmNspacing, 0,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_WIDGET,
+ XmNbottomWidget, sep,
+ NULL);
+ old_lbl = XtVaCreateManagedWidget("old_lbl", xmLabelWidgetClass,
+ lbl_form,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ NULL);
+ new_lbl = XtVaCreateManagedWidget("new_lbl", xmLabelWidgetClass,
+ lbl_form,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ NULL);
+ again_lbl = XtVaCreateManagedWidget("again_lbl", xmLabelWidgetClass,
+ lbl_form,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ NULL);
+ prompt_text = XtVaCreateManagedWidget("prompt_text", xmTextWidgetClass,
+ form,
+ XmNeditMode, XmSINGLE_LINE_EDIT,
+ XmNleftAttachment, XmATTACH_WIDGET,
+ XmNleftWidget, lbl_form,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_WIDGET,
+ XmNbottomWidget, sep,
+ NULL);
+ XtAddCallback(prompt_text, XmNactivateCallback, unset_looping, 0);
+ XtVaGetValues(prompt_text, XmNbackground, &bg, NULL);
+ XtVaSetValues(prompt_text, XmNforeground, bg, NULL);
+
+ main_lbl = XtVaCreateWidget("main_lbl", xmLabelWidgetClass,
+ form,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ NULL);
+ scroll_win = XtVaCreateManagedWidget("scroll_win",
+ xmScrolledWindowWidgetClass,
+ form,
+ XmNscrollingPolicy, XmAPPLICATION_DEFINED,
+ XmNscrollBarDisplayPolicy, XmSTATIC,
+ XmNtopAttachment, XmATTACH_WIDGET,
+ XmNtopWidget, main_lbl,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_WIDGET,
+ XmNbottomWidget, prompt_text,
+ NULL);
+ scroll_text = XtVaCreateManagedWidget("scroll_text", xmTextWidgetClass,
+ scroll_win,
+ XmNeditMode, XmMULTI_LINE_EDIT,
+ XmNeditable, FALSE,
+ NULL);
+ XtRealizeWidget(toplevel);
+}
+
+
+/***************************************************************************
+ *
+ *
+ */
+static long
+read_password(password, pwsize)
+ char *password;
+ int *pwsize;
+{
+ XEvent event;
+ char *text_val;
+
+ /* OK, this next part is gross... but this is due to the fact that */
+ /* this is not your traditional X program, which would be event */
+ /* driven. Instead, this program is more 'CLI' in nature, so we */
+ /* handle the dialogs synchronously... */
+
+ XtVaSetValues(prompt_text, XmNmaxLength, *pwsize, XmNvalue, "", NULL);
+ for (looping=1; looping; )
+ {
+ XtAppNextEvent(app_con, &event);
+ XtDispatchEvent(&event);
+ }
+ XtVaGetValues(prompt_text, XmNvalue, &text_val, NULL);
+ *pwsize = strlen(text_val);
+ strcpy(password, text_val);
+ memset(text_val, 0, *pwsize);
+ XtVaSetValues(prompt_text, XmNvalue, text_val, NULL);
+ return(0);
+}
+
+
+/***************************************************************************
+ *
+ *
+ */
+void
+display_intro_message(fmt_string, arg_string)
+ char *fmt_string;
+ char *arg_string;
+{
+ XmString xmstr;
+ char buf[1024];
+
+ sprintf(buf, fmt_string, arg_string);
+
+ xmstr = XmStringCreateLtoR(buf, XmSTRING_DEFAULT_CHARSET);
+ XtVaSetValues(main_lbl, XmNlabelString, xmstr, NULL);
+ XmStringFree(xmstr);
+ XtManageChild(main_lbl);
+}
+
+
+long
+read_old_password(context, password, pwsize)
+ krb5_context context;
+ char *password;
+ int *pwsize;
+{
+ long code;
+
+ XtManageChild(old_lbl);
+ code = read_password(password, pwsize);
+ SetWatchCursor();
+ return code;
+}
+
+long
+read_new_password(server_handle, password, pwsize, msg_ret, princ)
+ void *server_handle;
+ char *password;
+ int *pwsize;
+ char *msg_ret;
+ krb5_principal princ;
+{
+ char *password2 = (char *) malloc(*pwsize * sizeof(char));
+ int pwsize2 = *pwsize;
+
+ SetStandardCursor();
+
+ if (password2 == NULL)
+ {
+ strcpy(msg_ret, error_message(ENOMEM));
+ SetWatchCursor();
+ return(ENOMEM);
+ }
+
+ XtManageChild(new_lbl); XtUnmanageChild(old_lbl);
+ read_password(password, pwsize);
+ XtManageChild(again_lbl); XtUnmanageChild(new_lbl);
+ read_password(password2, &pwsize2);
+
+ if (strcmp(password, password2))
+ {
+ memset(password, 0, *pwsize);
+
+ memset(password2, 0, pwsize2);
+ free(password2);
+
+ strcpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH));
+ SetWatchCursor();
+ return(KRB5_LIBOS_BADPWDMATCH);
+ }
+
+ memset(password2, 0, pwsize2);
+ free(password2);
+
+ SetWatchCursor();
+ return (ovsec_kadm_chpass_principal_util(server_handle, princ, password,
+ NULL /* don't need new pw back */,
+ msg_ret));
+}
+
+
+/***************************************************************************
+ *
+ *
+ */
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_context context;
+ int code;
+
+ initialize_kpasswd_strings();
+
+ whoami = (whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0];
+
+ (void) set_com_err_hook(motif_com_err);
+
+ create_widgets(&argc, argv);
+ XmProcessTraversal(prompt_text, XmTRAVERSE_CURRENT);
+
+ if (retval = krb5_init_context(&context)) {
+ com_err(whoami, retval, "initializing krb5 context");
+ exit(retval);
+ }
+
+ while (1)
+ {
+ retval = kpasswd(context, argc, argv);
+ SetStandardCursor();
+
+ if (!retval)
+ com_err(0, 0, string_text(KPW_STR_PASSWORD_CHANGED));
+
+ if (retval == 0) /* 0 is success, so presumably the user */
+ /* is done. */
+ XmProcessTraversal(quit_btn, XmTRAVERSE_CURRENT);
+
+ if ((retval == 1) || /* the rest are "fatal", so we should */
+ (retval == 3) || /* "force" the user to quit... */
+ (retval == 6) ||
+ (retval == 7))
+ {
+ XtSetSensitive(prompt_text, FALSE);
+ XmProcessTraversal(quit_btn, XmTRAVERSE_CURRENT);
+ XtAppMainLoop(app_con);
+ }
+ }
+
+ /* NOTREACHED */
+ exit(retval);
+}