text: /*
   * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  
  /*
   * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
   *
   *	Openvision retains the copyright to derivative works of
   *	this source code.  Do *NOT* create a derivative of this
   *	source code before consulting with your legal department.
   *	Do *NOT* integrate *ANY* of this source code into another
   *	product before consulting with your legal department.
   *
   *	For further information, read the top-level Openvision
   *	copyright which is contained in the top-level MIT Kerberos
   *	copyright.
   *
   * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
   *
   */
  
  
  /*
   * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
   */
  
  
  #include <stdio.h>
  #ifdef HAVE_MEMORY_H
  #include <memory.h>
  #endif
  #include <time.h>
  #include <locale.h>
  
  #include <kadm5/admin.h>
  #include "admin_internal.h"
  
  #include <krb5.h>
  #include <strings.h>
  
  #define string_text error_message
  
  const char *chpw_error_message(kadm5_ret_t code);
  
  /*
   * Function: kadm5_chpass_principal_util
   *
   * Purpose: Wrapper around chpass_principal. We can read new pw, change pw and return useful messages
   *
   * Arguments:
   *
   *      princ          (input) a krb5b_principal structure for the 
   *                     principal whose password we should change.
   *
   *      new_password   (input) NULL or a null terminated string with the 
   *                     the principal's desired new password.  If new_password
   *                     is NULL then this routine will read a new password.
   *    
   *	pw_ret		(output) if non-NULL, points to a static buffer
   *			containing the new password (if password is prompted
   *			internally), or to the new_password argument (if
   *			that is non-NULL).  If the former, then the buffer
   *			is only valid until the next call to the function,
   *			and the caller should be sure to zero it when
   *			it is no longer needed.
   *
   *      msg_ret         (output) a useful message is copied here.
   *
   *      <return value>  exit status of 0 for success, else the com err code 
   *                      for the last significant routine called.
   *      
   * Requires:
   *      
   *      A msg_ret should point to a buffer large enough for the messasge.
   *
   * Effects:
   *      
   * Modifies:
   *
   *
   */
  
  kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle,
  					 void *lhandle,
  					 krb5_principal princ,
  					 char *new_pw, 
  					 char **ret_pw,
  					 char *msg_ret,
  					 unsigned int msg_len)
  {
    int code, code2;
    unsigned int pwsize;
    static char buffer[255];
    char *new_password;
    kadm5_principal_ent_rec princ_ent;
    kadm5_policy_ent_rec policy_ent;
    krb5_chgpwd_prot passwd_protocol;
  
    _KADM5_CHECK_HANDLE(server_handle);
  
    if (ret_pw)
      *ret_pw = NULL;
  
    if (new_pw != NULL) {
      new_password = new_pw;
    } else { /* read the password */
      krb5_context context;
  
      if ((code = (int) kadm5_init_krb5_context(&context)) == 0) {
        pwsize = sizeof(buffer);
        code = krb5_read_password(context, KADM5_PW_FIRST_PROMPT,
  				KADM5_PW_SECOND_PROMPT,
  				buffer, &pwsize);
        krb5_free_context(context);
      }
  
      if (code == 0) 
        new_password = buffer;
      else {
  #ifdef ZEROPASSWD    
        memset(buffer, 0, sizeof(buffer));
  #endif      
        if (code == KRB5_LIBOS_BADPWDMATCH) {
  	(void) strncpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH),
  		msg_len - 1);
  	msg_ret[msg_len - 1] = '\0';
  	return(code);
        } else {
          (void) strncpy(msg_ret, error_message(code), msg_len - 1);
          (void) strncat(msg_ret, " ", msg_len - 1);
          (void) strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_READING_PASSWORD),
  		msg_len - 1);
          (void) strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
  		msg_len - 1);
  	msg_ret[msg_len - 1] = '\0';
  	return(code);
        }
      }
      if (pwsize == 0) {
  #ifdef ZEROPASSWD    
        memset(buffer, 0, sizeof(buffer));
  #endif      
        strncpy(msg_ret, string_text(CHPASS_UTIL_NO_PASSWORD_READ), msg_len - 1);
        msg_ret[msg_len - 1] = '\0';
        return(KRB5_LIBOS_CANTREADPWD); /* could do better */
      }
    }
  
    if (ret_pw)
      *ret_pw = new_password;
  
  	passwd_protocol = _kadm5_get_kpasswd_protocol(server_handle);
  	if (passwd_protocol == KRB5_CHGPWD_CHANGEPW_V2) {
  		kadm5_ret_t srvr_rsp_code;
  		krb5_data   srvr_msg;
  
  		srvr_msg.length = 0;
  		srvr_msg.data = NULL;
  
  		code = kadm5_chpass_principal_v2(server_handle, princ,
  						new_password,
  						&srvr_rsp_code,
  						&srvr_msg);
  		if (srvr_rsp_code) {
  			sprintf(msg_ret, "%s%s%.*s\n",
  				chpw_error_message(srvr_rsp_code),
  				srvr_msg.length? ": " : "",
  				srvr_msg.length,
  				srvr_msg.data ? srvr_msg.data : "");
  
  			return (srvr_rsp_code);
  		}
  		return (code);
  
  	} else if (passwd_protocol == KRB5_CHGPWD_RPCSEC) {
  		code = kadm5_chpass_principal(server_handle, princ,
  					    new_password);
  
  #ifdef ZEROPASSWD
    if (!ret_pw)
      memset(buffer, 0, sizeof(buffer)); /* in case we read a new password */
  #endif    
  
    if (code == KADM5_OK) {
      strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_CHANGED), msg_len - 1);
      msg_ret[msg_len - 1] = '\0';
      return(0);
    }
  
    if ((code != KADM5_PASS_Q_TOOSHORT) && 
        (code != KADM5_PASS_REUSE) &&(code != KADM5_PASS_Q_CLASS) && 
        (code != KADM5_PASS_Q_DICT) && (code != KADM5_PASS_TOOSOON)) {
      /* Can't get more info for other errors */
      sprintf(buffer, "%s %s", error_message(code), 
  	    string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE));
      sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 
  	    buffer);
      return(code);
    }
  
    /* Ok, we have a password quality error. Return a good message */
  
    if (code == KADM5_PASS_REUSE) {
      strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_REUSE), msg_len - 1);
      msg_ret[msg_len - 1] = '\0';
      return(code);
    }
  
    if (code == KADM5_PASS_Q_DICT) {
      strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_IN_DICTIONARY),
  	    msg_len - 1);
      msg_ret[msg_len - 1] = '\0';
      return(code);
    }
    
    /* Look up policy for the remaining messages */
  
    code2 = kadm5_get_principal (lhandle, princ, &princ_ent,
  			       KADM5_PRINCIPAL_NORMAL_MASK);
    if (code2 != 0) {
      strncpy(msg_ret, error_message(code2), msg_len - 1);
      strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, string_text(CHPASS_UTIL_GET_PRINC_INFO), msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE),
  	    msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
  	    msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret));
      msg_ret[msg_len - 1] = '\0';
      return(code);
    }
    
    if ((princ_ent.aux_attributes & KADM5_POLICY) == 0) {
      strncpy(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret));
      strncpy(msg_ret, string_text(CHPASS_UTIL_NO_POLICY_YET_Q_ERROR),
  	    msg_len - 1 - strlen(msg_ret));
      strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret));
      strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
  	    msg_len - 1 - strlen(msg_ret));
      msg_ret[msg_len - 1] = '\0';
  
      (void) kadm5_free_principal_ent(lhandle, &princ_ent);
      return(code);
    }
  
    code2 = kadm5_get_policy(lhandle, princ_ent.policy,
  			   &policy_ent);
    if (code2 != 0) {
      sprintf(msg_ret, "%s %s\n%s %s\n\n%s\n ", error_message(code2), 
  	    string_text(CHPASS_UTIL_GET_POLICY_INFO),
  	    error_message(code),
  	    string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE),
  	    string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED));
      (void) kadm5_free_principal_ent(lhandle, &princ_ent);
      return(code);
    }
    
    if (code == KADM5_PASS_Q_TOOSHORT) {
      sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SHORT), 
  	    policy_ent.pw_min_length);
      (void) kadm5_free_principal_ent(lhandle, &princ_ent);
      (void) kadm5_free_policy_ent(lhandle, &policy_ent);
      return(code);
    }
  
  
    if (code == KADM5_PASS_Q_CLASS) {
      sprintf(msg_ret, string_text(CHPASS_UTIL_TOO_FEW_CLASSES), 
  	    policy_ent.pw_min_classes);
      (void) kadm5_free_principal_ent(lhandle, &princ_ent);
      (void) kadm5_free_policy_ent(lhandle, &policy_ent);
      return(code);
    }
  
    if (code == KADM5_PASS_TOOSOON) {
      time_t until;
      char *time_string, *ptr;
  
      until = princ_ent.last_pwd_change + policy_ent.pw_min_life;
  
      time_string = ctime(&until);
      if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
        *ptr = '\0';
  
      sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SOON), 
  	    time_string);
      (void) kadm5_free_principal_ent(lhandle, &princ_ent);
      (void) kadm5_free_policy_ent(lhandle, &policy_ent);
      return(code);
    } else {
  
    /* We should never get here, but just in case ... */
    sprintf(buffer, "%s %s", error_message(code), 
  	  string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE));
    sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED), 
  	  buffer);
    (void) kadm5_free_principal_ent(lhandle, &princ_ent);
    (void) kadm5_free_policy_ent(lhandle, &policy_ent);
    return(code);
  		}
  	} else {
  		sprintf(msg_ret, "%s\n%s\n",
  			string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
  			"Password protocol in krb5.conf is not supported\n");
  		return (-1);
  	}
  }
  
  /*
   * krb5_chpw_result_code_string
   *
   * convert the return code received from the password server
   * to a human-readable string.
   */
  const char *
  chpw_error_message(kadm5_ret_t result_code)
  {
  	switch (result_code) {
  	case KRB5_KPASSWD_MALFORMED:
  		return (dgettext(TEXT_DOMAIN, "Malformed request error"));
  	case KRB5_KPASSWD_HARDERROR:
  		return (dgettext(TEXT_DOMAIN, "Server error"));
  	case KRB5_KPASSWD_AUTHERROR:
  		return (dgettext(TEXT_DOMAIN, "Authentication error"));
  	case KRB5_KPASSWD_SOFTERROR:
  		return (dgettext(TEXT_DOMAIN, "Password change rejected"));
  	case KRB5_KPASSWD_ACCESSDENIED:
  		return (dgettext(TEXT_DOMAIN,
  				"Not authorized to change password"));
  	case KRB5_KPASSWD_BAD_VERSION:
  		return (dgettext(TEXT_DOMAIN, "Protocol version unsupported"));
  	case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
  		return (dgettext(TEXT_DOMAIN,
  				"initial flag required in changepw request"));
  	case KRB5_KPASSWD_POLICY_REJECT:
  		return (dgettext(TEXT_DOMAIN, "new password fails policy"));
  	case KRB5_KPASSWD_BAD_PRINCIPAL:
  		return (dgettext(TEXT_DOMAIN,
  		    "target principal does not exist for "
  		    "changepw request"));
  	case KRB5_KPASSWD_ETYPE_NOSUPP:
  		return (dgettext(TEXT_DOMAIN,
  		    "changepw request key sequence has an "
  		    "unsupported Etype"));
  	default:
  		return (dgettext(TEXT_DOMAIN, "Password change failed"));
  	}
  }
  