gchar * mdm_verify_user (MdmDisplay *d, const char *username, gboolean allow_retry) { gint pamerr = 0; struct passwd *pwent = NULL; char *login, *passreq; char *pam_stack = NULL; MDM_PAM_QUAL void *p; int null_tok = 0; gboolean credentials_set = FALSE; gboolean error_msg_given = FALSE; gboolean started_timer = FALSE; verify_user_again: pamerr = 0; login = NULL; error_msg_given = FALSE; credentials_set = FALSE; started_timer = FALSE; null_tok = 0; /* Don't start a timed login if we've already entered a username */ if (username != NULL) { login = g_strdup (username); mdm_slave_greeter_ctl_no_ret (MDM_SETLOGIN, login); } else { /* start the timer for timed logins */ if ( ! ve_string_empty (mdm_daemon_config_get_value_string (MDM_KEY_TIMED_LOGIN)) && d->timed_login_ok && (d->attached)) { mdm_slave_greeter_ctl_no_ret (MDM_STARTTIMER, ""); started_timer = TRUE; } } cur_mdm_disp = d; authenticate_again: if (prev_user && !login) { login = g_strdup (prev_user); } else if (login && !prev_user) { prev_user = g_strdup (login); auth_retries = 0; } else if (login && prev_user && strcmp (login, prev_user)) { g_free (prev_user); prev_user = g_strdup (login); auth_retries = 0; } /* * Initialize a PAM session for the user... * Get value per-display so different displays can use different * PAM Stacks, in case one display should use a different * authentication mechanism than another display. */ pam_stack = mdm_daemon_config_get_value_string_per_display (MDM_KEY_PAM_STACK, (char *)d->name); if ( ! create_pamh (d, pam_stack, login, &pamc, d->name, &pamerr)) { if (started_timer) mdm_slave_greeter_ctl_no_ret (MDM_STOPTIMER, ""); g_free (pam_stack); goto pamerr; } g_free (pam_stack); /* * have to unset login otherwise there is no chance to ever enter * a different user */ g_free (login); login = NULL; pam_set_item (pamh, PAM_USER_PROMPT, _("Username:"******"PASSREQ="); if (mdm_daemon_config_get_value_bool (MDM_KEY_PASSWORD_REQUIRED) || ((passreq != NULL) && g_ascii_strcasecmp (passreq, "YES") == 0)) null_tok |= PAM_DISALLOW_NULL_AUTHTOK; mdm_verify_select_user (NULL); /* Start authentication session */ did_we_ask_for_password = FALSE; if ((pamerr = pam_authenticate (pamh, null_tok)) != PAM_SUCCESS) { if ( ! ve_string_empty (selected_user)) { pam_handle_t *tmp_pamh; /* Face browser was used to select a user, just completely rewhack everything since it seems various PAM implementations are having goats with just setting PAM_USER and trying to pam_authenticate again */ g_free (login); login = selected_user; selected_user = NULL; mdm_sigterm_block_push (); mdm_sigchld_block_push (); tmp_pamh = pamh; pamh = NULL; mdm_sigchld_block_pop (); mdm_sigterm_block_pop (); /* FIXME: what about errors */ /* really this has been a sucess, not a failure */ pam_end (tmp_pamh, pamerr); g_free (prev_user); prev_user = NULL; auth_retries = 0; mdm_slave_greeter_ctl_no_ret (MDM_SETLOGIN, login); goto authenticate_again; } if (started_timer) mdm_slave_greeter_ctl_no_ret (MDM_STOPTIMER, ""); if (mdm_slave_action_pending ()) { /* FIXME: see note above about PAM_FAIL_DELAY */ /* #ifndef PAM_FAIL_DELAY */ mdm_sleep_no_signal (mdm_daemon_config_get_value_int (MDM_KEY_RETRY_DELAY)); /* wait up to 100ms randomly */ usleep (g_random_int_range (0, 100000)); /* #endif */ /* PAM_FAIL_DELAY */ mdm_error ("Couldn't authenticate user"); if (prev_user) { unsigned max_auth_retries = 3; char *val = mdm_read_default ("LOGIN_RETRIES="); if (val) { max_auth_retries = atoi (val); g_free (val); } if (allow_retry == FALSE || pamerr == PAM_MAXTRIES || ++auth_retries >= max_auth_retries) { g_free (prev_user); prev_user = NULL; auth_retries = 0; } } } else { /* cancel, configurator etc pressed */ g_free (prev_user); prev_user = NULL; auth_retries = 0; } goto pamerr; } /* stop the timer for timed logins */ if (started_timer) mdm_slave_greeter_ctl_no_ret (MDM_STOPTIMER, ""); g_free (login); login = NULL; g_free (prev_user); prev_user = NULL; if ((pamerr = pam_get_item (pamh, PAM_USER, &p)) != PAM_SUCCESS) { login = NULL; /* is not really an auth problem, but it will pretty much look as such, it shouldn't really happen */ if (mdm_slave_action_pending ()) mdm_error ("Couldn't authenticate user"); goto pamerr; } login = g_strdup ((const char *)p); /* kind of anal, the greeter likely already knows, but it could have been changed */ mdm_slave_greeter_ctl_no_ret (MDM_SETLOGIN, login); if ( ! mdm_slave_check_user_wants_to_log_in (login)) { /* cleanup stuff */ mdm_slave_greeter_ctl_no_ret (MDM_SETLOGIN, ""); g_free (login); login = NULL; mdm_slave_greeter_ctl_no_ret (MDM_RESETOK, ""); mdm_verify_cleanup (d); goto verify_user_again; } /* Check if user is root and is allowed to log in */ pwent = getpwnam (login); if (( ! mdm_daemon_config_get_value_bool (MDM_KEY_ALLOW_ROOT) || ( ! d->attached )) && (pwent != NULL && pwent->pw_uid == 0)) { mdm_error ("Root login disallowed on display '%s'", d->name); mdm_slave_greeter_ctl_no_ret (MDM_ERRBOX, _("\nThe system administrator " "is not allowed to login " "from this screen")); /*mdm_slave_greeter_ctl_no_ret (MDM_ERRDLG, _("Root login disallowed"));*/ error_msg_given = TRUE; goto pamerr; } if (mdm_daemon_config_get_value_bool (MDM_KEY_DISPLAY_LAST_LOGIN)) { char *info = mdm_get_last_info (login); mdm_slave_greeter_ctl_no_ret (MDM_MSG, info); g_free (info); } /* Check if the user's account is healthy. */ pamerr = pam_acct_mgmt (pamh, null_tok); switch (pamerr) { case PAM_SUCCESS : break; case PAM_NEW_AUTHTOK_REQD : if ((pamerr = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK)) != PAM_SUCCESS) { mdm_error ("Authentication token change failed for user %s", login); mdm_slave_greeter_ctl_no_ret (MDM_ERRBOX, _("\nThe change of the authentication token failed. " "Please try again later or contact the system administrator.")); error_msg_given = TRUE; goto pamerr; } break; case PAM_ACCT_EXPIRED : mdm_error ("User %s no longer permitted to access the system", login); mdm_slave_greeter_ctl_no_ret (MDM_ERRBOX, _("\nThe system administrator has disabled your account.")); error_msg_given = TRUE; goto pamerr; case PAM_PERM_DENIED : mdm_error ("User %s not permitted to gain access at this time", login); mdm_slave_greeter_ctl_no_ret (MDM_ERRBOX, _("\nThe system administrator has disabled access to the system temporarily.")); error_msg_given = TRUE; goto pamerr; default : if (mdm_slave_action_pending ()) mdm_error ("Couldn't set acct. mgmt for %s", login); goto pamerr; } pwent = getpwnam (login); if (/* paranoia */ pwent == NULL || ! mdm_setup_gids (login, pwent->pw_gid)) { mdm_error ("Cannot set user group for %s", login); mdm_slave_greeter_ctl_no_ret (MDM_ERRBOX, _("\nCannot set your user group; " "you will not be able to log in. " "Please contact your system administrator.")); goto pamerr; } did_setcred = TRUE; /* Set credentials */ pamerr = pam_setcred (pamh, PAM_ESTABLISH_CRED); if (pamerr != PAM_SUCCESS) { did_setcred = FALSE; if (mdm_slave_action_pending ()) mdm_error ("Couldn't set credentials for %s", login); goto pamerr; } credentials_set = TRUE; opened_session = TRUE; /* Register the session */ pamerr = pam_open_session (pamh, 0); if (pamerr != PAM_SUCCESS) { opened_session = FALSE; /* we handle this above */ did_setcred = FALSE; if (mdm_slave_action_pending ()) mdm_error ("Couldn't open session for %s", login); goto pamerr; } /* Workaround to avoid mdm messages being logged as PAM_pwdb */ mdm_log_shutdown (); mdm_log_init (); cur_mdm_disp = NULL; /* * Login succeeded. * This function is a no-op if libaudit is not present. */ log_to_audit_system(login, d->hostname, d->name, AU_SUCCESS); return login; pamerr: /* * Take care of situation where we get here before setting pwent. * Since login can be passed in as NULL, get the actual value if * possible. */ if ((pam_get_item (pamh, PAM_USER, &p)) == PAM_SUCCESS) { g_free (login); login = g_strdup ((const char *)p); } if (pwent == NULL && login != NULL) { pwent = getpwnam (login); } /* * Log the failed login attempt. * This function is a no-op if libaudit is not present. */ log_to_audit_system(login, d->hostname, d->name, AU_FAILED); /* The verbose authentication is turned on, output the error * message from the PAM subsystem */ if ( ! error_msg_given && mdm_slave_action_pending ()) { mdm_slave_write_utmp_wtmp_record (d, MDM_SESSION_RECORD_TYPE_FAILED_ATTEMPT, login, getpid ()); /* * I'm not sure yet if I should display this message for any * other issues - heeten * Adding AUTHINFO_UNAVAIL to the list - its what an unknown * user is. */ if (pamerr == PAM_AUTH_ERR || pamerr == PAM_USER_UNKNOWN || pamerr == PAM_AUTHINFO_UNAVAIL) { gboolean is_capslock = FALSE; const char *basemsg; char *msg; char *ret; ret = mdm_slave_greeter_ctl (MDM_QUERY_CAPSLOCK, ""); if ( ! ve_string_empty (ret)) is_capslock = TRUE; g_free (ret); /* Only give this message if we actually asked for password, otherwise it would be silly to say that the password may have been wrong */ if (did_we_ask_for_password) { basemsg = _("\nIncorrect username or password. " "Letters must be typed in the correct " "case."); } else { basemsg = _("\nAuthentication failed. " "Letters must be typed in the correct " "case."); } if (is_capslock) { msg = g_strconcat (basemsg, " ", _("Caps Lock is on."), NULL); } else { msg = g_strdup (basemsg); } mdm_slave_greeter_ctl_no_ret (MDM_ERRBOX, msg); g_free (msg); } else { mdm_slave_greeter_ctl_no_ret (MDM_ERRDLG, _("Authentication failed")); } } did_setcred = FALSE; opened_session = FALSE; if (pamh != NULL) { pam_handle_t *tmp_pamh; mdm_sigterm_block_push (); mdm_sigchld_block_push (); tmp_pamh = pamh; pamh = NULL; mdm_sigchld_block_pop (); mdm_sigterm_block_pop (); /* Throw away the credentials */ if (credentials_set) pam_setcred (tmp_pamh, PAM_DELETE_CRED); pam_end (tmp_pamh, pamerr); } pamh = NULL; /* Workaround to avoid mdm messages being logged as PAM_pwdb */ mdm_log_shutdown (); mdm_log_init (); g_free (login); cur_mdm_disp = NULL; return NULL; }
gchar * mdm_verify_user (MdmDisplay *d, const char *username, gboolean allow_retry) { 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 (d->attached && d->timed_login_ok) mdm_slave_greeter_ctl_no_ret (MDM_STARTTIMER, ""); if (username == NULL) { authenticate_again: /* Ask for the user's login */ mdm_verify_select_user (NULL); mdm_slave_greeter_ctl_no_ret (MDM_MSG, _("Please enter your username")); login = mdm_slave_greeter_ctl (MDM_PROMPT, _("Username:"******""); g_free (login); return NULL; } } mdm_slave_greeter_ctl_no_ret (MDM_MSG, ""); if (mdm_daemon_config_get_value_bool (MDM_KEY_DISPLAY_LAST_LOGIN)) { char *info = mdm_get_last_info (login); mdm_slave_greeter_ctl_no_ret (MDM_ERRBOX, info); g_free (info); } } else { login = g_strdup (username); } mdm_slave_greeter_ctl_no_ret (MDM_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 = mdm_slave_greeter_ctl (MDM_NOECHO, _("Password:"******""); if (mdm_slave_greeter_check_interruption ()) { if (d->attached) mdm_slave_greeter_ctl_no_ret (MDM_STOPTIMER, ""); g_free (login); g_free (passwd); g_free (ppasswd); return NULL; } } if (d->attached) mdm_slave_greeter_ctl_no_ret (MDM_STOPTIMER, ""); if (pwent == NULL) { mdm_sleep_no_signal (mdm_daemon_config_get_value_int (MDM_KEY_RETRY_DELAY)); mdm_debug ("Couldn't authenticate user"); 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)) { mdm_sleep_no_signal (mdm_daemon_config_get_value_int (MDM_KEY_RETRY_DELAY)); mdm_debug ("Couldn't authenticate user"); print_cant_auth_errbox (); g_free (login); g_free (passwd); g_free (ppasswd); return NULL; } if (( ! mdm_daemon_config_get_value_bool (MDM_KEY_ALLOW_ROOT) || ( ! mdm_daemon_config_get_value_bool (MDM_KEY_ALLOW_REMOTE_ROOT) && ! d->attached)) && pwent->pw_uid == 0) { mdm_debug ("Root login disallowed on display '%s'", d->name); mdm_slave_greeter_ctl_no_ret (MDM_ERRBOX, _("The system administrator " "is not allowed to login " "from this screen")); /*mdm_slave_greeter_ctl_no_ret (MDM_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) { mdm_debug ("User not allowed to log in"); mdm_slave_greeter_ctl_no_ret (MDM_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, NOLOGIN) == 0 || strcmp (pwent->pw_shell, "/bin/true") == 0 || strcmp (pwent->pw_shell, "/bin/false") == 0)) { mdm_debug ("User not allowed to log in"); mdm_slave_greeter_ctl_no_ret (MDM_ERRBOX, _("\nThe system administrator " "has disabled your " "account.")); /*mdm_slave_greeter_ctl_no_ret (MDM_ERRDLG, _("Login disabled"));*/ g_free (login); g_free (passwd); g_free (ppasswd); return NULL; } #endif /* HAVE_LOGINRESTRICTIONS */ g_free (passwd); g_free (ppasswd); if ( ! mdm_slave_check_user_wants_to_log_in (login)) { g_free (login); login = NULL; goto authenticate_again; } if ( ! mdm_setup_gids (login, pwent->pw_gid)) { mdm_debug ("Cannot set user group"); mdm_slave_greeter_ctl_no_ret (MDM_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 : mdm_debug ("User password has expired"); mdm_errorgui_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) { mdm_slave_greeter_ctl_no_ret (MDM_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 = mdm_slave_greeter_ctl (MDM_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) { mdm_errorgui_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 */ mdm_errorgui_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 : mdm_debug ("User password has expired"); mdm_errorgui_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 : mdm_debug ("Internal error on passwdexpired"); mdm_errorgui_error_box (d, GTK_MESSAGE_ERROR, _("An internal error occurred. 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; }