/* -*-c-*- */
/* $Id: pam_krb4_passwd.-c,v 1.1 1997/07/18 16:38:14 warlord Exp $ */

/*
 * $Log: pam_krb4_passwd.-c,v $
 * Revision 1.1  1997/07/18 16:38:14  warlord
 * Initial revision based on Derrick Brashear's Krb4 PAM Module
 *
 * Revision 1.1  1996/11/05 13:21:27  shadow
 * Initial revision
 *
 */

static const char rcsid_pass[] =
"$Id: pam_krb4_passwd.-c,v 1.1 1997/07/18 16:38:14 warlord Exp $\n"
" - pam_krb4 password module <shadow@dementia.org>"
;

/* data tokens */

#define _KRB4_OLD_AUTHTOK  "-KRB4-OLD-PASS"
#define _KRB4_NEW_AUTHTOK  "-KRB4-NEW-PASS"

#include <netinet/in.h>
#include <kadm.h>
#include <kadm_err.h>

/* Implementation */

/*
 * FUNCTION: _krb4_updatedb()
 *
 * This function is responsible for updating the passwd (or shadow)
 * database, as specified by the arguments it is passed.
 */

static int _krb4_updatedb(const char *user, const des_cblock *new_key
			  , const char *pass_new, const unsigned int ctrl
			  , const char *realm)
{
     int retval;
     char *ret_st;

     if ((retval = kadm_init_link((void *)PWSERV_NAME, (void *)KRB_MASTER, 
				  (void *)realm)) != KADM_SUCCESS) {
       D(("kadm_init_link returned %d\n", retval));
       pass_new = NULL;                                
       return PAM_AUTHTOK_ERR;
     } else {
       retval = kadm_change_pw2((void *)new_key, (char *)pass_new, (u_char **)&ret_st);
       D(("kadm_change_pw2 returned %d\n", retval));
       if (ret_st) {
	 free(ret_st);
       }
       if (retval == KADM_INSECURE_PW) {
	 pass_new = NULL;                                
	 retval = PAM_AUTHTOK_ERR;
       } else
       if (retval != KADM_SUCCESS) {
	 pass_new = NULL;                                
	 retval = PAM_CRED_ERR;
       } else {
	 retval = PAM_SUCCESS;
       }
     }
     
     pass_new = NULL;

     dest_tkt();

     return retval;
}

/*
 * FUNCTION: _pam_krb4_chauthtok() 
 *
 * this function works in two passes. The first, when KRB4__PRELIM is
 * set, obtains the previous password. It sets the PAM_OLDAUTHTOK item
 * or stores it as a data item. The second function obtains a new
 * password (verifying if necessary, that the user types it the same a
 * second time.) depending on the 'ctrl' flags this new password may
 * be stored in the PAM_AUTHTOK item or a private data item.
 */

static int _krb4_chauthtok(pam_handle_t *pamh, unsigned int ctrl)
{
     int retval;
     unsigned int lctrl;
     char lrealm[REALM_SZ];       /* realm of user */
     des_cblock new_key;

     /* <DO NOT free() THESE> */
     const char *user;
     const char *pass_old, *pass_new;
     /* </DO NOT free() THESE> */

     /*
      * First get the name of a user
      */

     if (krb_get_lrealm(lrealm, 1) != KSUCCESS) {
       return PAM_AUTHINFO_UNAVAIL;
     }

     retval = _krb4_get_user( pamh, ctrl, "Username: ", &user );
     if ( retval != PAM_SUCCESS ) {
	  if ( on(KRB4_DEBUG,ctrl) ) {
	       _log_err(LOG_DEBUG, "password - could not identify user\n");
	  }
	  return retval;
     }

     if ( on(KRB4__PRELIM, ctrl) ) {
	  /*
	   * obtain and verify the current password (OLDAUTHTOK) for
	   * the user.
	   */

	  char *Announce;

	  D(("prelim check\n"));

	  /* instruct user what is happening */
#define greeting "Changing password for "
	  Announce = (char *) malloc(sizeof(greeting)+strlen(user));
	  if (Announce == NULL) {
	    _log_err(LOG_CRIT, "password - out of memory");
	    return PAM_BUF_ERR;
	  }
	  (void) strcpy(Announce, greeting);
	  (void) strcpy(Announce+sizeof(greeting)-1, user);
#undef greeting
	  
	  lctrl = ctrl;
	  set(KRB4__OLD_PASSWD, lctrl);
	  retval = _krb4_read_password( pamh, lctrl
					, Announce
					, "(current) KRB4 password: "
					, NULL
					, _KRB4_OLD_AUTHTOK
					, &pass_old );
	  free(Announce);
	  
	  if ( retval != PAM_SUCCESS ) {
	    _log_err(LOG_NOTICE
		     , "password - (old) token not obtained");
	    return retval;
	  }
	  
	  /* verify that this is the password for this user */
	  
	  retval = _krb4_verify_password(pamh, user, pass_old, ctrl);
	  
	  if ( retval != PAM_SUCCESS ) {
	       D(("Authentication failed"));
	       pass_old = NULL;
	       return retval;
	  }

	  retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *) pass_old);
	  pass_old = NULL;
	  if ( retval != PAM_SUCCESS ) {
	       _log_err(LOG_CRIT, "failed to set PAM_OLDAUTHTOK");
	  }

     } else if ( on( KRB4__UPDATE, ctrl ) ) {

	  /*
	   * obtain the proposed password
	   */

	  D(("do update\n"));

	  /*
	   * get the old token back. NULL was ok only if root [at this
	   * point we assume that this has already been enforced on a
	   * previous call to this function].
	   */

	  if ( off(KRB4_NOT_SET_PASS, ctrl) ) {
	       retval = pam_get_item(pamh, PAM_OLDAUTHTOK
				     , (const void **)&pass_old);
	  } else {
	       retval = pam_get_data(pamh, _KRB4_OLD_AUTHTOK
				     , (const void **)&pass_old);
	       if (retval == PAM_NO_MODULE_DATA) {
		    retval = PAM_SUCCESS;
		    pass_old = NULL;
	       }
	  }

	  if (retval != PAM_SUCCESS) {
	       _log_err(LOG_NOTICE, "user not authenticated");
	       return retval;
	  }

	  D(("get new password now\n"));

	  lctrl = ctrl;

	  /*
	   * use_authtok is to force the use of a previously entered
	   * password -- needed for pluggable password strength checking
	   */

	  if ( on(KRB4_USE_AUTHTOK, lctrl) ) {
	       set(KRB4_USE_FIRST_PASS, lctrl);
	  }

	  retval = _krb4_read_password( pamh, lctrl
					, NULL
					, "Enter new KRB4 password: "
					, "Retype new KRB4 password: "
					, _KRB4_NEW_AUTHTOK
					, &pass_new );

	  if ( retval != PAM_SUCCESS ) {
	       if ( on(KRB4_DEBUG,ctrl) ) {
		    _log_err(LOG_ALERT
			     , "password - new password not obtained\n");
	       }
	       pass_old = NULL;                               /* tidy up */
	       return retval;
	  }

	  D(("returned to _krb4_chauthtok\n"));

	  /*
	   * At this point we know who the user is and what they
	   * propose as their new password. Verify that the new
	   * password is acceptable.
	   */

	  retval = _pam_krb4_approve_pass(pamh, ctrl
					  , pass_old, pass_new);

	  D(("password approved\n"));

	  pass_old = NULL;               /* drop pointer to old password */

	  if (retval != PAM_SUCCESS) {
	       _log_err(LOG_NOTICE
			, "pam_krb4 - new password not acceptable\n");
	       pass_new = NULL;	                              /* tidy up */
	       return retval;
	  }

	  /*
	   * By reaching here we have approved the passwords and must now
	   * change it in the database.
	   */

#ifdef AFS_SUPPORT
	  if ( on(KRB4_USE_AFS_STK,ctrl) ) {
	    (void) afs_string_to_key ((void *)pass_new, &new_key, lrealm);
	  } else
#endif
	    (void) des_string_to_key ((void *)pass_new, new_key);

	  /* update the password database */

	  retval = _krb4_updatedb(user, &new_key, pass_new, ctrl, lrealm);

	  if ( retval != PAM_SUCCESS ) {
	       _log_err(LOG_ALERT, "password problem [user %s by %s(uid=%d)]"
			, user, getlogin(), getuid());
	  } else {
	       _log_err(LOG_INFO, "password for user %s changed by %s(uid=%d)"
			, user, getlogin(), getuid());
	  }

     } else {            /* something has broken with the module */

	  _log_err(LOG_ALERT, "password received unknown request");
	  retval = PAM_ABORT;

     }

     return retval;
}

/* ******************************************************************
 * Copyright (c) Alexander O. Yuriev (alex@bach.cis.temple.edu), 1996.
 * Copyright (c) Andrew Morgan <morgan@physics.ucla.edu>, 1996
 * Copyright (c) Cristian Gafton, <gafton@sorosis.ro> 1996.
 * Copyright (c) Derrick J Brashear, <shadow@dementia.org> 1996.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, and the entire permission notice in its entirety,
 *    including the disclaimer of warranties.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.
 * 
 * ALTERNATIVELY, this product may be distributed under the terms of
 * the GNU Public License, in which case the provisions of the GPL are
 * required INSTEAD OF the above restrictions.  (This clause is
 * necessary due to a potential bad interaction between the GPL and
 * the restrictions contained in a BSD-style copyright.)
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
