text: /* GDM - The Gnome Display Manager
   * Copyright (C) 1999, 2000 Martin K. Petersen <mkp@mkp.net>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   */
  
  #include <config.h>
  #include <libgnome/libgnome.h>
  #include <gtk/gtkmessagedialog.h>
  #include <syslog.h>
  #include <pwd.h>
  #include <shadow.h>
  #include <grp.h>
  #include <sys/types.h>
  #include <unistd.h>
  
  #if defined(CAN_CLEAR_ADMCHG) && defined(HAVE_USERSEC_H)
  #  include <usersec.h>
  #endif /* CAN_CLEAR_ADMCHG && HAVE_USERSEC_H */
  
  #ifdef HAVE_CRYPT
  #  include <crypt.h>
  #endif /* HAVE_CRYPT */
  
  #include <vicious.h>
  
  #include "gdm.h"
  #include "misc.h"
  #include "slave.h"
  #include "verify.h"
  #include "errorgui.h"
  
  /* Configuration option variables */
  extern gboolean GdmAllowRoot;
  extern gboolean GdmAllowRemoteRoot;
  extern gint GdmRetryDelay;
  extern gboolean GdmDisplayLastLogin;
  
  static char *selected_user = NULL;
  
  void
  gdm_verify_select_user (const char *user)
  {
  	g_free (selected_user);
  	if (ve_string_empty (user))
  		selected_user = NULL;
  	else
  		selected_user = g_strdup (user);
  }
  
  static void
  print_cant_auth_errbox (void)
  {
  	gboolean is_capslock = FALSE;
  	const char *basemsg;
  	char *msg;
  	char *ret;
  
  	ret = gdm_slave_greeter_ctl (GDM_QUERY_CAPSLOCK, "");
  	if ( ! ve_string_empty (ret))
  		is_capslock = TRUE;
  	g_free (ret);
  
  	basemsg = _("\nIncorrect username or password.  "
  		    "Letters must be typed in the correct "
  		    "case.");
  	if (is_capslock) {
  		msg = g_strconcat (basemsg, "  ",
  				   _("Please make sure the "
  				     "Caps Lock key is not "
  				     "enabled."),
  				   NULL);
  	} else {
  		msg = g_strdup (basemsg);
  	}
  	gdm_slave_greeter_ctl_no_ret (GDM_ERRBOX, msg);
  	g_free (msg);
  }
  
  /**
   * gdm_verify_user:
   * @username: Name of user or NULL if we should ask
   * @display: Name of display to register with the authentication system
   * @local: boolean if local
   *
   * Provides a communication layer between the operating system's
   * authentication functions and the gdmgreeter. 
   *
   * Returns the user's login on success and NULL on failure.
   */
  
  gchar *
  gdm_verify_user (GdmDisplay *d, const char *username, const gchar *display, gboolean local) 
  {
      gchar *login, *passwd, *ppasswd;
      struct passwd *pwent;
      struct spwd *sp;
  #if defined(HAVE_PASSWDEXPIRED) && defined(HAVE_CHPASS) \
      || defined(HAVE_LOGINRESTRICTIONS)
      gchar *message = NULL;
  #endif
  #if defined(HAVE_PASSWDEXPIRED) && defined(HAVE_CHPASS)
      gchar *info_msg = NULL, *response = NULL;
      gint reEnter, ret;
  #endif
  
      if (local)
  	    gdm_slave_greeter_ctl_no_ret (GDM_STARTTIMER, "");
  
      if (username == NULL) {
  authenticate_again:
  	    /* Ask for the user's login */
  	    gdm_verify_select_user (NULL);
  	    login = gdm_slave_greeter_ctl (GDM_PROMPT, _("Username:"));
  	    if (login == NULL ||
  		gdm_slave_greeter_check_interruption ()) {
  		    if ( ! ve_string_empty (selected_user)) {
  			    /* user selected */
  			    g_free (login);
  			    login = selected_user;
  			    selected_user = NULL;
  		    } else {
  			    /* some other interruption */
  			    if (local)
  				    gdm_slave_greeter_ctl_no_ret (GDM_STOPTIMER, "");
  			    g_free (login);
  			    return NULL;
  		    }
  	    }
  	    gdm_slave_greeter_ctl_no_ret (GDM_MSG, "");
  
  	    if (GdmDisplayLastLogin) {
  		    char *info = gdm_get_last_info (login);
  		    gdm_slave_greeter_ctl_no_ret (GDM_ERRBOX, info);
  		    g_free (info);
  	    }
      } else {
  	    login = g_strdup (username);
      }
      gdm_slave_greeter_ctl_no_ret (GDM_SETLOGIN, login);
  
      pwent = getpwnam (login);
  
      setspent ();
  
      /* Lookup shadow password */
      sp = getspnam (login);
      
      /* Use shadow password when available */
      if (sp != NULL) {
  	    ppasswd = g_strdup (sp->sp_pwdp);
      } else {
  	    /* In case shadow password cannot be retrieved (when using NIS
  	       authentication for example), use standard passwd */
  	    if (pwent != NULL &&
  		pwent->pw_passwd != NULL)
  		    ppasswd = g_strdup (pwent->pw_passwd);
  	    else
  		    /* If no password can be retrieved, set it to NULL */
  		    ppasswd = NULL;
      }
      
      endspent ();
  
      /* Request the user's password */
      if (pwent != NULL &&
          ve_string_empty (ppasswd)) {
  	    /* eeek a passwordless account */
  	    passwd = g_strdup ("");
      } else {
  	    passwd = gdm_slave_greeter_ctl (GDM_NOECHO, _("Password:"));
  	    if (passwd == NULL)
  		    passwd = g_strdup ("");
  	    if (gdm_slave_greeter_check_interruption ()) {
  		    if (local)
  			    gdm_slave_greeter_ctl_no_ret (GDM_STOPTIMER, "");
  		    g_free (login);
  		    g_free (passwd);
  		    g_free (ppasswd);
  		    return NULL;
  	    }
      }
  
      if (local)
  	    gdm_slave_greeter_ctl_no_ret (GDM_STOPTIMER, "");
  
      if (pwent == NULL) {
  	    gdm_sleep_no_signal (GdmRetryDelay);
  	    gdm_error (_("Couldn't authenticate user \"%s\""), login);
  
  	    print_cant_auth_errbox ();
  
  	    g_free (login);
  	    g_free (passwd);
  	    g_free (ppasswd);
  	    return NULL;
      }
  
      /* Check whether password is valid */
      if (ppasswd == NULL || (ppasswd[0] != '\0' &&
  			    strcmp (crypt (passwd, ppasswd), ppasswd) != 0)) {
  	    gdm_sleep_no_signal (GdmRetryDelay);
  	    gdm_error (_("Couldn't authenticate user \"%s\""), login);
  
  	    print_cant_auth_errbox ();
  
  	    g_free (login);
  	    g_free (passwd);
  	    g_free (ppasswd);
  	    return NULL;
      }
  
      if ( ( ! GdmAllowRoot ||
  	  ( ! GdmAllowRemoteRoot && ! local) ) &&
  	pwent->pw_uid == 0) {
  	    gdm_error (_("Root login disallowed on display '%s'"), display);
  	    gdm_slave_greeter_ctl_no_ret (GDM_ERRBOX,
  					  _("The system administrator "
  					    "is not allowed to login "
  					    "from this screen"));
  	    /*gdm_slave_greeter_ctl_no_ret (GDM_ERRDLG,
  	      _("Root login disallowed"));*/
  	    g_free (login);
  	    g_free (passwd);
  	    g_free (ppasswd);
  	    return NULL;
      }
  
  #ifdef HAVE_LOGINRESTRICTIONS
  
      /* Check with the 'loginrestrictions' function
         if the user has been disallowed */
      if (loginrestrictions (login, 0, NULL, &message) != 0) {
  	    gdm_error (_("User %s not allowed to log in"), login);
  	    gdm_slave_greeter_ctl_no_ret (GDM_ERRBOX,
  					  _("\nThe system administrator "
  					    "has disabled your "
  					    "account."));
  	    g_free (login);
  	    g_free (passwd);
  	    g_free (ppasswd);
  	    if (message != NULL)
  		    free (message);
  	    return NULL;
      }
      
      if (message != NULL)
  	    free (message);
      message = NULL;
  
  #else /* ! HAVE_LOGINRESTRICTIONS */
  
      /* check for the standard method of disallowing users */
      if (pwent->pw_shell != NULL &&
  	(strcmp (pwent->pw_shell, "/sbin/nologin") == 0 ||
  	 strcmp (pwent->pw_shell, "/bin/true") == 0 ||
  	 strcmp (pwent->pw_shell, "/bin/false") == 0)) {
  	    gdm_error (_("User %s not allowed to log in"), login);
  	    gdm_slave_greeter_ctl_no_ret (GDM_ERRBOX,
  					  _("\nThe system administrator "
  					    "has disabled your "
  					    "account."));
  	    /*gdm_slave_greeter_ctl_no_ret (GDM_ERRDLG,
  	      _("Login disabled"));*/
  	    g_free (login);
  	    g_free (passwd);
  	    g_free (ppasswd);
  	    return NULL;
      }	
  
  #endif /* HAVE_LOGINRESTRICTIONS */
  
      g_free (passwd);
      g_free (ppasswd);
  
      if ( ! gdm_slave_check_user_wants_to_log_in (login)) {
  	    g_free (login);
  	    login = NULL;
  	    goto authenticate_again;
      }
  
      if ( ! gdm_setup_gids (login, pwent->pw_gid)) {
  	    gdm_error (_("Cannot set user group for %s"), login);
  	    gdm_slave_greeter_ctl_no_ret (GDM_ERRBOX,
  					  _("\nCannot set your user group, "
  					    "you will not be able to log in, "
  					    "please contact your system administrator."));
  	    g_free (login);
  	    return NULL;
      }
  
  #if defined(HAVE_PASSWDEXPIRED) && defined (HAVE_CHPASS)
  
      switch (passwdexpired (login, &info_msg)) {
      case 1 :
  	    gdm_error (_("Password of %s has expired"), login);
  	    gdm_error_box (d, GTK_MESSAGE_ERROR,
  			   _("You are required to change your password.\n"
  			     "Please choose a new one."));
  	    g_free (info_msg);
  
  	    do {
  		    ret = chpass (login, response, &reEnter, &message);
  		    g_free (response);
  
  		    if (ret != 1) {
  			    if (ret != 0) {
  				    gdm_slave_greeter_ctl_no_ret (GDM_ERRBOX,
  								  _("\nCannot change your password, "
  								    "you will not be able to log in, "
  								    "please try again later or contact "
  								    "your system administrator."));
  			    } else if ((reEnter != 0) && (message)) {
  				    response = gdm_slave_greeter_ctl (GDM_NOECHO, message);
  				    if (response == NULL)
  					    response = g_strdup ("");
  			    }
  		    }
  
  		    g_free(message);
  		    message = NULL;
  
  	    } while ( ((reEnter != 0) && (ret == 0))
  		      || (ret ==1) );
  
  	    g_free (response);
  	    g_free (message);
  
  	    if ((ret != 0) || (reEnter != 0)) {
  		    return NULL;
  	    }
  
  #if defined(CAN_CLEAR_ADMCHG)
  	    /* The password is changed by root, clear the ADM_CHG
  	       flag in the passwd file */
  	    ret = setpwdb (S_READ | S_WRITE);
  	    if (!ret) {
  		    upwd = getuserpw (login);
  		    if (upwd == NULL) {
  			    ret = -1;
  		    }
  		    else {
  			    upwd->upw_flags &= ~PW_ADMCHG;
  			    ret = putuserpw (upwd);
  			    if (!ret) {
  				    ret = endpwdb ();
  			    }
  		    }
  	    }
  
  	    if (ret) {
  		    gdm_error_box (d, GTK_MESSAGE_WARNING,
  				   _("Your password has been changed but "
  				     "you may have to change it again, "
  				     "please try again later or contact "
  				     "your system administrator."));
  	    }
  
  #else /* !CAN_CLEAR_ADMCHG */
  	    gdm_error_box (d, GTK_MESSAGE_WARNING,
  			   _("Your password has been changed but you "
  			     "may have to change it again, please try again "
  			     "later or contact your system administrator."));
  
  #endif /* CAN_CLEAR_ADMCHG */
  
  	    break;
  
      case 2 :
  	    gdm_error (_("Password of %s has expired"), login);
  	    gdm_error_box (d, GTK_MESSAGE_ERROR,
  			   _("Your password has expired.\n"
  			     "Only a system administrator can now change it"));
  	    g_free (info_msg);
  	    return NULL;
  	    break;    
  
      case -1 :
  	    gdm_error (_("Internal error on passwdexpired"));
  	    gdm_error_box (d, GTK_MESSAGE_ERROR,
  			   _("An internal error occured, you will not be able to log in.\n"
  			     "Please try again later or contact your system administrator."));
  	    g_free (info_msg);
  	    return NULL;
  	    break;    
  
      default :
  	    g_free (info_msg);
  	    break;
      }
  
  #endif /* HAVE_PASSWDEXPIRED && HAVE_CHPASS */
  
      return login;
  }
  
  /**
   * gdm_verify_setup_user:
   * @login: The name of the user
   * @display: The name of the display
   *
   * This is used for auto loging in.  This just sets up the login
   * session for this user
   */
  
  gboolean
  gdm_verify_setup_user (GdmDisplay *d, const gchar *login, const gchar *display,
  		       char **new_login) 
  {
  	struct passwd *pwent;
  
  	*new_login = NULL;
  
  	pwent = getpwnam (login);
  	if (pwent == NULL) {
  		gdm_error (_("Cannot get passwd structure for %s"), login);
  		return FALSE;
  	}
  
  	if ( ! gdm_setup_gids (login, pwent->pw_gid)) {
  		gdm_error (_("Cannot set user group for %s"), login);
  		gdm_error_box (d,
  			       GTK_MESSAGE_ERROR,
  			       _("\nCannot set your user group, "
  				 "you will not be able to log in, "
  				 "please contact your system administrator."));
  		return FALSE;
  	}
  
  	return TRUE;
  }
  
  
  /**
   * gdm_verify_cleanup:
   *
   * Unregister the user's session */
  
  void
  gdm_verify_cleanup (GdmDisplay *d)
  {
  	gid_t groups[1] = { 0 };
  
  	/* Clear the group setup */
  	setgid (0);
  	/* this will get rid of any suplementary groups etc... */
  	setgroups (1, groups);
  }
  
  
  /**
   * gdm_verify_check:
   *
   * Check that the authentication system is correctly configured.
   *
   * Aborts daemon on error 
   */
  
  void
  gdm_verify_check (void)
  {
  }
  
  /* used in pam */
  gboolean
  gdm_verify_setup_env (GdmDisplay *d)
  {
  	return TRUE;
  }
  
  /* EOF */
  