Esempio n. 1
0
void Authenticator::fail_delay(const unsigned int micro_sec) {
    switch((last_result=pam_fail_delay(pam_handle, micro_sec))) {
    default:
        _end();
        throw Exception(pam_handle, "fail_delay()", last_result);

    case PAM_SUCCESS:
        break;
    }
    return;
}
Esempio n. 2
0
File: pam.c Progetto: LucentW/ngircd
/**
 * Authenticate a connecting client using PAM.
 * @param Client The client to authenticate.
 * @return true when authentication succeeded, false otherwise.
 */
GLOBAL bool
PAM_Authenticate(CLIENT *Client) {
	pam_handle_t *pam;
	int retval = PAM_SUCCESS;

	LogDebug("PAM: Authenticate \"%s\" (%s) ...",
		 Client_OrigUser(Client), Client_Mask(Client));

	/* Set supplied client password */
	if (password)
		free(password);
	password = strdup(Conn_Password(Client_Conn(Client)));
	conv.appdata_ptr = Conn_Password(Client_Conn(Client));

	/* Initialize PAM */
	retval = pam_start(Conf_PAMServiceName, Client_OrigUser(Client), &conv, &pam);
	if (retval != PAM_SUCCESS) {
		Log(LOG_ERR, "PAM: Failed to create authenticator! (%d)", retval);
		return false;
	}

	pam_set_item(pam, PAM_RUSER, Client_User(Client));
	pam_set_item(pam, PAM_RHOST, Client_Hostname(Client));
#if defined(HAVE_PAM_FAIL_DELAY) && !defined(NO_PAM_FAIL_DELAY)
	pam_fail_delay(pam, 0);
#endif

	/* PAM authentication ... */
	retval = pam_authenticate(pam, 0);

	/* Success? */
	if (retval == PAM_SUCCESS)
		Log(LOG_INFO, "PAM: Authenticated \"%s\" (%s).",
		    Client_OrigUser(Client), Client_Mask(Client));
	else
		Log(LOG_ERR, "PAM: Error on \"%s\" (%s): %s",
		    Client_OrigUser(Client), Client_Mask(Client),
		    pam_strerror(pam, retval));

	/* Free PAM structures */
	if (pam_end(pam, retval) != PAM_SUCCESS)
		Log(LOG_ERR, "PAM: Failed to release authenticator!");

	return (retval == PAM_SUCCESS);
}
Esempio n. 3
0
File: ulock.c Progetto: jwilk/ulock
void check_password(void)
{
  static struct pam_conv conv =
  {
    ulock_conv,
    NULL
  };
  pam_handle_t *pamh;
  char username[1 << 8];
  username[0] = '\0';
  strncat(username, getpwuid(getuid())->pw_name, (sizeof username) - 1);
  pam_start("ulock", username, &conv, &pamh);
  writes(STDOUT_FILENO, "The terminal is now locked. Please enter the password to unlock it.\n");
  char *username2 = username;
  for (int i = 0; ; i++)
  {
    int pam_error;
    writes(STDOUT_FILENO, username2);
    writes(STDOUT_FILENO, "'s password: "******"Erm, one minute penalty...\n");
      sleep(60);
    }
    username2 = (username2 == username) ? "root" : username;
  }
  pam_end(pamh, PAM_SUCCESS);
  fatal("Something went *SERIOUSLY* wrong\n");
}
Esempio n. 4
0
int checkpass_pam(char *password)
{
	struct pam_conv conv = { conv_func, password };
	int retval = pam_start("bftpd", user, (struct pam_conv *) &conv,
						   (pam_handle_t **) & pamh);
	if (retval != PAM_SUCCESS) {
		printf("Error while initializing PAM: %s\n",
			   pam_strerror(pamh, retval));
		return 1;
	}
	pam_fail_delay(pamh, 0);
	retval = pam_authenticate(pamh, 0);
	if (retval == PAM_SUCCESS)
		retval = pam_acct_mgmt(pamh, 0);
	if (retval == PAM_SUCCESS)
		pam_open_session(pamh, 0);
	if (retval != PAM_SUCCESS)
		return 1;
	else
		return 0;
}
Esempio n. 5
0
int main(int argc, char **argv) {
  hardened_shadow_openlog("su");

  if (!hardened_shadow_get_current_username(&current_username))
    errx(EXIT_FAILURE, "Cannot determine your user name.");

  parse_args(argc, argv);

  uid_t my_uid = getuid();
  bool is_root = (my_uid == 0);

  if (!is_root && (!isatty(STDIN_FILENO) || !ttyname(STDIN_FILENO)))
    errx(EXIT_FAILURE, "must be run from a terminal");

  const struct pam_conv pam_conversation = {
    misc_conv,
    NULL
  };
  pam_handle_t *pam_handle = NULL;
  int pam_rv = pam_start("su", target_username, &pam_conversation, &pam_handle);
  if (pam_rv != PAM_SUCCESS) {
    hardened_shadow_syslog(LOG_ERR, "pam_start: error %d", pam_rv);
    su_fatal();
  }

  pam_rv = pam_set_item(pam_handle, PAM_TTY, ttyname(STDIN_FILENO));
  if (pam_rv != PAM_SUCCESS) {
    hardened_shadow_syslog(LOG_ERR, "pam_set_item: %s",
                           pam_strerror(pam_handle, pam_rv));
    goto pam_cleanup;
  }

  pam_rv = pam_set_item(pam_handle, PAM_RUSER, current_username);
  if (pam_rv != PAM_SUCCESS) {
    hardened_shadow_syslog(LOG_ERR, "pam_set_item: %s",
                           pam_strerror(pam_handle, pam_rv));
    goto pam_cleanup;
  }

  pam_rv = pam_fail_delay(pam_handle, 1000000);
  if (pam_rv != PAM_SUCCESS) {
    hardened_shadow_syslog(LOG_ERR, "pam_fail_delay: %s",
                           pam_strerror(pam_handle, pam_rv));
    goto pam_cleanup;
  }

  pam_rv = pam_authenticate(pam_handle, 0);
  if (pam_rv != PAM_SUCCESS) {
    hardened_shadow_syslog(LOG_ERR, "pam_authenticate: %s",
                           pam_strerror(pam_handle, pam_rv));
    goto pam_cleanup;
  }

  pam_rv = pam_acct_mgmt(pam_handle, 0);
  if (pam_rv != PAM_SUCCESS) {
    if (is_root) {
      warnx("%s (ignored)", pam_strerror(pam_handle, pam_rv));
    } else if (pam_rv == PAM_NEW_AUTHTOK_REQD) {
      pam_rv = pam_chauthtok(pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
      if (pam_rv != PAM_SUCCESS) {
	hardened_shadow_syslog(LOG_ERR, "pam_chauthtok: %s",
                               pam_strerror(pam_handle, pam_rv));
        goto pam_cleanup;
      }
    } else {
      hardened_shadow_syslog(LOG_ERR, "pam_acct_mgmt: %s",
                             pam_strerror(pam_handle, pam_rv));
      goto pam_cleanup;
    }
  }

  if (setgid(target_gid) != 0) {
    hardened_shadow_syslog(LOG_ERR, "bad group ID `%d' for user `%s': %s",
                           target_gid, target_username, strerror(errno));
    pam_rv = PAM_ABORT;
    goto pam_cleanup;
  }
  if (initgroups(target_username, target_gid) != 0) {
    hardened_shadow_syslog(LOG_ERR, "initgroups failed for user `%s': %s",
                           target_username, strerror(errno));
    pam_rv = PAM_ABORT;
    goto pam_cleanup;
  }

  pam_rv = pam_setcred(pam_handle, PAM_ESTABLISH_CRED);
  if (pam_rv != PAM_SUCCESS) {
    hardened_shadow_syslog(LOG_ERR, "pam_setcred: %s",
                           pam_strerror(pam_handle, pam_rv));
    goto pam_cleanup;
  }

  pam_rv = pam_open_session(pam_handle, 0);
  if (pam_rv != PAM_SUCCESS) {
    hardened_shadow_syslog(LOG_ERR, "pam_open_session: %s",
                           pam_strerror(pam_handle, pam_rv));
    goto pam_cred_cleanup;
  }

  char **pam_env = pam_getenvlist(pam_handle);
  if (!pam_env)
    errx(EXIT_FAILURE, "pam_getenvlist returned NULL");

  struct environment_options environment_options = {
    .pam_environment = pam_env,
    .preserve_environment = preserve_environment,
    .login_shell = login_shell,
    .target_username = target_username,
    .target_homedir = target_homedir,
    .target_shell = shell,
  };
  if (!hardened_shadow_prepare_environment(&environment_options)) {
    pam_rv = PAM_ABORT;
    goto pam_session_cleanup;
  }

  if (setuid(target_uid) != 0) {
    hardened_shadow_syslog(LOG_ERR, "bad user ID `%d' for user `%s': %s",
                           target_uid, target_username, strerror(errno));
    goto pam_session_cleanup;
  }

  int shell_argc = command ? 4 : 2;
  char **shell_argv = calloc(shell_argc, sizeof(*shell_argv));
  if (!shell_argv) {
    hardened_shadow_syslog(LOG_ERR, "memory allocation failure");
    goto pam_session_cleanup;
  }
  /* When argv[0] starts with a dash ("-"), bash will recognize
   * it as a login shell. This is what shadow-utils does. */
  shell_argv[0] = login_shell ? "-su" : shell;
  if (command) {
    shell_argv[1] = "-c";
    shell_argv[2] = command;
  }
  shell_argv[shell_argc - 1] = NULL;
  int status;
  if (!run_shell(shell, shell_argv, &status)) {
    pam_rv = PAM_ABORT;
    goto pam_session_cleanup;
  }
  free(shell_argv);

  pam_rv = pam_setcred(pam_handle, PAM_DELETE_CRED);
  if (pam_rv != PAM_SUCCESS) {
    hardened_shadow_syslog(LOG_ERR, "pam_setcred: %s",
                           pam_strerror(pam_handle, pam_rv));
    pam_close_session(pam_handle, 0);
    pam_end(pam_handle, pam_rv);
    errx(EXIT_FAILURE, "pam_setcred");
  }

  pam_rv = pam_close_session(pam_handle, 0);
  if (pam_rv != PAM_SUCCESS) {
    hardened_shadow_syslog(LOG_ERR, "pam_close_session: %s",
                           pam_strerror(pam_handle, pam_rv));
    pam_end(pam_handle, pam_rv);
    errx(EXIT_FAILURE, "pam_close_session");
  }

  pam_end(pam_handle, pam_rv);

  free(shell);
  free(current_username);
  free(target_username);
  free(target_homedir);

  hardened_shadow_closelog();

  if (WIFEXITED(status))
    return WEXITSTATUS(status);

  return WTERMSIG(status) + 128;
pam_session_cleanup:
  pam_close_session(pam_handle, 0);
pam_cred_cleanup:
  pam_setcred(pam_handle, PAM_DELETE_CRED);
pam_cleanup:
  pam_end(pam_handle, pam_rv);
  su_fatal();
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
/*
 * login - create a new login session for a user
 *
 *	login is typically called by getty as the second step of a
 *	new user session. getty is responsible for setting the line
 *	characteristics to a reasonable set of values and getting
 *	the name of the user to be logged in. login may also be
 *	called to create a new user session on a pty for a variety
 *	of reasons, such as X servers or network logins.
 *
 *	the flags which login supports are
 *	
 *	-p - preserve the environment
 *	-r - perform autologin protocol for rlogin
 *	-f - do not perform authentication, user is preauthenticated
 *	-h - the name of the remote host
 */
int main (int argc, char **argv)
{
	const char *tmptty;
	char tty[BUFSIZ];

#ifdef RLOGIN
	char term[128] = "";
#endif				/* RLOGIN */
#if defined(HAVE_STRFTIME) && !defined(USE_PAM)
	char ptime[80];
#endif
	unsigned int delay;
	unsigned int retries;
	bool failed;
	bool subroot = false;
#ifndef USE_PAM
	bool is_console;
#endif
	int err;
	const char *cp;
	char *tmp;
	char fromhost[512];
	struct passwd *pwd = NULL;
	char **envp = environ;
	const char *failent_user;
	/*@null@*/struct utmp *utent;

#ifdef USE_PAM
	int retcode;
	pid_t child;
	char *pam_user = NULL;
#else
	struct spwd *spwd = NULL;
#endif
	/*
	 * Some quick initialization.
	 */

	sanitize_env ();

	(void) setlocale (LC_ALL, "");
	(void) bindtextdomain (PACKAGE, LOCALEDIR);
	(void) textdomain (PACKAGE);

	initenv ();

	amroot = (getuid () == 0);
	Prog = Basename (argv[0]);

	if (geteuid() != 0) {
		fprintf (stderr, _("%s: Cannot possibly work without effective root\n"), Prog);
		exit (1);
	}

	process_flags (argc, argv);

	if ((isatty (0) == 0) || (isatty (1) == 0) || (isatty (2) == 0)) {
		exit (1);	/* must be a terminal */
	}

	utent = get_current_utmp ();
	/*
	 * Be picky if run by normal users (possible if installed setuid
	 * root), but not if run by root. This way it still allows logins
	 * even if your getty is broken, or if something corrupts utmp,
	 * but users must "exec login" which will use the existing utmp
	 * entry (will not overwrite remote hostname).  --marekm
	 */
	if (!amroot && (NULL == utent)) {
		(void) puts (_("No utmp entry.  You must exec \"login\" from the lowest level \"sh\""));
		exit (1);
	}
	/* NOTE: utent might be NULL afterwards */

	tmptty = ttyname (0);
	if (NULL == tmptty) {
		tmptty = "UNKNOWN";
	}
	STRFCPY (tty, tmptty);

#ifndef USE_PAM
	is_console = console (tty);
#endif

	if (rflg || hflg) {
		/*
		 * Add remote hostname to the environment. I think
		 * (not sure) I saw it once on Irix.  --marekm
		 */
		addenv ("REMOTEHOST", hostname);
	}
	if (fflg) {
		preauth_flag = true;
	}
	if (hflg) {
		reason = PW_RLOGIN;
	}
#ifdef RLOGIN
	if (rflg) {
		assert (NULL == username);
		username = xmalloc (USER_NAME_MAX_LENGTH + 1);
		username[USER_NAME_MAX_LENGTH] = '\0';
		if (do_rlogin (hostname, username, USER_NAME_MAX_LENGTH, term, sizeof term)) {
			preauth_flag = true;
		} else {
			free (username);
			username = NULL;
		}
	}
#endif				/* RLOGIN */

	OPENLOG ("login");

	setup_tty ();

#ifndef USE_PAM
	(void) umask (getdef_num ("UMASK", GETDEF_DEFAULT_UMASK));

	{
		/* 
		 * Use the ULIMIT in the login.defs file, and if
		 * there isn't one, use the default value. The
		 * user may have one for themselves, but otherwise,
		 * just take what you get.
		 */
		long limit = getdef_long ("ULIMIT", -1L);

		if (limit != -1) {
			set_filesize_limit (limit);
		}
	}

#endif
	/*
	 * The entire environment will be preserved if the -p flag
	 * is used.
	 */
	if (pflg) {
		while (NULL != *envp) {	/* add inherited environment, */
			addenv (*envp, NULL); /* some variables change later */
			envp++;
		}
	}

#ifdef RLOGIN
	if (term[0] != '\0') {
		addenv ("TERM", term);
	} else
#endif				/* RLOGIN */
	{
		/* preserve TERM from getty */
		if (!pflg) {
			tmp = getenv ("TERM");
			if (NULL != tmp) {
				addenv ("TERM", tmp);
			}
		}
	}

	init_env ();

	if (optind < argc) {	/* now set command line variables */
		set_env (argc - optind, &argv[optind]);
	}

	if (rflg || hflg) {
		cp = hostname;
#ifdef	HAVE_STRUCT_UTMP_UT_HOST
	} else if ((NULL != utent) && ('\0' != utent->ut_host[0])) {
		cp = utent->ut_host;
#endif				/* HAVE_STRUCT_UTMP_UT_HOST */
	} else {
		cp = "";
	}

	if ('\0' != *cp) {
		snprintf (fromhost, sizeof fromhost,
		          " on '%.100s' from '%.200s'", tty, cp);
	} else {
		snprintf (fromhost, sizeof fromhost,
		          " on '%.100s'", tty);
	}

      top:
	/* only allow ALARM sec. for login */
	(void) signal (SIGALRM, alarm_handler);
	timeout = getdef_unum ("LOGIN_TIMEOUT", ALARM);
	if (timeout > 0) {
		(void) alarm (timeout);
	}

	environ = newenvp;	/* make new environment active */
	delay   = getdef_unum ("FAIL_DELAY", 1);
	retries = getdef_unum ("LOGIN_RETRIES", RETRIES);

#ifdef USE_PAM
	retcode = pam_start ("login", username, &conv, &pamh);
	if (retcode != PAM_SUCCESS) {
		fprintf (stderr,
		         _("login: PAM Failure, aborting: %s\n"),
		         pam_strerror (pamh, retcode));
		SYSLOG ((LOG_ERR, "Couldn't initialize PAM: %s",
		         pam_strerror (pamh, retcode)));
		exit (99);
	}

	/*
	 * hostname & tty are either set to NULL or their correct values,
	 * depending on how much we know. We also set PAM's fail delay to
	 * ours.
	 *
	 * PAM_RHOST and PAM_TTY are used for authentication, only use
	 * information coming from login or from the caller (e.g. no utmp)
	 */
	retcode = pam_set_item (pamh, PAM_RHOST, hostname);
	PAM_FAIL_CHECK;
	retcode = pam_set_item (pamh, PAM_TTY, tty);
	PAM_FAIL_CHECK;
#ifdef HAS_PAM_FAIL_DELAY
	retcode = pam_fail_delay (pamh, 1000000 * delay);
	PAM_FAIL_CHECK;
#endif
	/* if fflg, then the user has already been authenticated */
	if (!fflg) {
		unsigned int failcount = 0;
		char hostn[256];
		char loginprompt[256];	/* That's one hell of a prompt :) */

		/* Make the login prompt look like we want it */
		if (gethostname (hostn, sizeof (hostn)) == 0) {
			snprintf (loginprompt,
			          sizeof (loginprompt),
			          _("%s login: "******"login: "******"TOO MANY LOGIN TRIES (%u)%s FOR '%s'",
				         failcount, fromhost, failent_user));
				fprintf(stderr,
				        _("Maximum number of tries exceeded (%u)\n"),
				        failcount);
				PAM_END;
				exit(0);
			} else if (retcode == PAM_ABORT) {
				/* Serious problems, quit now */
				(void) fputs (_("login: abort requested by PAM\n"), stderr);
				SYSLOG ((LOG_ERR,"PAM_ABORT returned from pam_authenticate()"));
				PAM_END;
				exit(99);
			} else if (retcode != PAM_SUCCESS) {
				SYSLOG ((LOG_NOTICE,"FAILED LOGIN (%u)%s FOR '%s', %s",
				         failcount, fromhost, failent_user,
				         pam_strerror (pamh, retcode)));
				failed = true;
			}

			if (!failed) {
				break;
			}

#ifdef WITH_AUDIT
			audit_fd = audit_open ();
			audit_log_acct_message (audit_fd,
			                        AUDIT_USER_LOGIN,
			                        NULL,    /* Prog. name */
			                        "login",
			                        failent_user,
			                        AUDIT_NO_ID,
			                        hostname,
			                        NULL,    /* addr */
			                        tty,
			                        0);      /* result */
			close (audit_fd);
#endif				/* WITH_AUDIT */

			(void) puts ("");
			(void) puts (_("Login incorrect"));

			if (failcount >= retries) {
				SYSLOG ((LOG_NOTICE,
				         "TOO MANY LOGIN TRIES (%u)%s FOR '%s'",
				         failcount, fromhost, failent_user));
				fprintf(stderr,
				        _("Maximum number of tries exceeded (%u)\n"),
				        failcount);
				PAM_END;
				exit(0);
			}

			/*
			 * Let's give it another go around.
			 * Even if a username was given on the command
			 * line, prompt again for the username.
			 */
			retcode = pam_set_item (pamh, PAM_USER, NULL);
			PAM_FAIL_CHECK;
		}

		/* We don't get here unless they were authenticated above */
		(void) alarm (0);
	}

	/* Check the account validity */
	retcode = pam_acct_mgmt (pamh, 0);
	if (retcode == PAM_NEW_AUTHTOK_REQD) {
		retcode = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
	}
	PAM_FAIL_CHECK;

	/* Open the PAM session */
	get_pam_user (&pam_user);
	retcode = pam_open_session (pamh, hushed (pam_user) ? PAM_SILENT : 0);
	PAM_FAIL_CHECK;

	/* Grab the user information out of the password file for future usage
	 * First get the username that we are actually using, though.
	 *
	 * From now on, we will discard changes of the user (PAM_USER) by
	 * PAM APIs.
	 */
	get_pam_user (&pam_user);
	if (NULL != username) {
		free (username);
	}
	username = pam_user;
	failent_user = get_failent_user (username);

	pwd = xgetpwnam (username);
	if (NULL == pwd) {
		SYSLOG ((LOG_ERR, "cannot find user %s", failent_user));
		exit (1);
	}

	/* This set up the process credential (group) and initialize the
	 * supplementary group access list.
	 * This has to be done before pam_setcred
	 */
	if (setup_groups (pwd) != 0) {
		exit (1);
	}

	retcode = pam_setcred (pamh, PAM_ESTABLISH_CRED);
	PAM_FAIL_CHECK;
	/* NOTE: If pam_setcred changes PAM_USER, this will not be taken
	 * into account.
	 */

#else				/* ! USE_PAM */
	while (true) {	/* repeatedly get login/password pairs */
		/* user_passwd is always a pointer to this constant string
		 * or a passwd or shadow password that will be memzero by
		 * pw_free / spw_free.
		 * Do not free() user_passwd. */
		const char *user_passwd = "!";

		/* Do some cleanup to avoid keeping entries we do not need
		 * anymore. */
		if (NULL != pwd) {
			pw_free (pwd);
			pwd = NULL;
		}
		if (NULL != spwd) {
			spw_free (spwd);
			spwd = NULL;
		}

		failed = false;	/* haven't failed authentication yet */
		if (NULL == username) {	/* need to get a login id */
			if (subroot) {
				closelog ();
				exit (1);
			}
			preauth_flag = false;
			username = xmalloc (USER_NAME_MAX_LENGTH + 1);
			username[USER_NAME_MAX_LENGTH] = '\0';
			login_prompt (_("\n%s login: "******"!",
			 * the account is locked and the user cannot
			 * login, even if they have been
			 * "pre-authenticated."
			 */
			if (   ('!' == user_passwd[0])
			    || ('*' == user_passwd[0])) {
				failed = true;
			}
		}

		if (strcmp (user_passwd, SHADOW_PASSWD_STRING) == 0) {
			spwd = xgetspnam (username);
			if (NULL != spwd) {
				user_passwd = spwd->sp_pwdp;
			} else {
				/* The user exists in passwd, but not in
				 * shadow. SHADOW_PASSWD_STRING indicates
				 * that the password shall be in shadow.
				 */
				SYSLOG ((LOG_WARN,
				         "no shadow password for '%s'%s",
				         username, fromhost));
			}
		}

		/*
		 * The -r and -f flags provide a name which has already
		 * been authenticated by some server.
		 */
		if (preauth_flag) {
			goto auth_ok;
		}

		if (pw_auth (user_passwd, username, reason, (char *) 0) == 0) {
			goto auth_ok;
		}

		SYSLOG ((LOG_WARN, "invalid password for '%s' %s",
		         failent_user, fromhost));
		failed = true;

	      auth_ok:
		/*
		 * This is the point where all authenticated users wind up.
		 * If you reach this far, your password has been
		 * authenticated and so on.
		 */
		if (   !failed
		    && (NULL != pwd)
		    && (0 == pwd->pw_uid)
		    && !is_console) {
			SYSLOG ((LOG_CRIT, "ILLEGAL ROOT LOGIN %s", fromhost));
			failed = true;
		}
		if (   !failed
		    && !login_access (username, ('\0' != *hostname) ? hostname : tty)) {
			SYSLOG ((LOG_WARN, "LOGIN '%s' REFUSED %s",
			         username, fromhost));
			failed = true;
		}
		if (   (NULL != pwd)
		    && getdef_bool ("FAILLOG_ENAB")
		    && !failcheck (pwd->pw_uid, &faillog, failed)) {
			SYSLOG ((LOG_CRIT,
			         "exceeded failure limit for '%s' %s",
			         username, fromhost));
			failed = true;
		}
		if (!failed) {
			break;
		}

		/* don't log non-existent users */
		if ((NULL != pwd) && getdef_bool ("FAILLOG_ENAB")) {
			failure (pwd->pw_uid, tty, &faillog);
		}
		if (getdef_str ("FTMP_FILE") != NULL) {
#ifdef USE_UTMPX
			struct utmpx *failent =
				prepare_utmpx (failent_user,
				               tty,
			/* FIXME: or fromhost? */hostname,
				               utent);
#else				/* !USE_UTMPX */
			struct utmp *failent =
				prepare_utmp (failent_user,
				              tty,
				              hostname,
				              utent);
#endif				/* !USE_UTMPX */
			failtmp (failent_user, failent);
			free (failent);
		}

		retries--;
		if (retries <= 0) {
			SYSLOG ((LOG_CRIT, "REPEATED login failures%s",
			         fromhost));
		}

		/*
		 * If this was a passwordless account and we get here, login
		 * was denied (securetty, faillog, etc.). There was no
		 * password prompt, so do it now (will always fail - the bad
		 * guys won't see that the passwordless account exists at
		 * all).  --marekm
		 */
		if (user_passwd[0] == '\0') {
			pw_auth ("!", username, reason, (char *) 0);
		}

		/*
		 * Authentication of this user failed.
		 * The username must be confirmed in the next try.
		 */
		free (username);
		username = NULL;

		/*
		 * Wait a while (a la SVR4 /usr/bin/login) before attempting
		 * to login the user again. If the earlier alarm occurs
		 * before the sleep() below completes, login will exit.
		 */
		if (delay > 0) {
			(void) sleep (delay);
		}

		(void) puts (_("Login incorrect"));

		/* allow only one attempt with -r or -f */
		if (rflg || fflg || (retries <= 0)) {
			closelog ();
			exit (1);
		}
	}			/* while (true) */
#endif				/* ! USE_PAM */
	assert (NULL != username);
	assert (NULL != pwd);

	(void) alarm (0);		/* turn off alarm clock */

#ifndef USE_PAM			/* PAM does this */
	/*
	 * porttime checks moved here, after the user has been
	 * authenticated. now prints a message, as suggested
	 * by Ivan Nejgebauer <*****@*****.**>.  --marekm
	 */
	if (   getdef_bool ("PORTTIME_CHECKS_ENAB")
	    && !isttytime (username, tty, time ((time_t *) 0))) {
		SYSLOG ((LOG_WARN, "invalid login time for '%s'%s",
		         username, fromhost));
		closelog ();
		bad_time_notify ();
		exit (1);
	}

	check_nologin (pwd->pw_uid == 0);
#endif

	if (getenv ("IFS")) {	/* don't export user IFS ... */
		addenv ("IFS= \t\n", NULL);	/* ... instead, set a safe IFS */
	}

	if (pwd->pw_shell[0] == '*') {	/* subsystem root */
		pwd->pw_shell++;	/* skip the '*' */
		subsystem (pwd);	/* figure out what to execute */
		subroot = true;	/* say I was here again */
		endpwent ();	/* close all of the file which were */
		endgrent ();	/* open in the original rooted file */
		endspent ();	/* system. they will be re-opened */
#ifdef	SHADOWGRP
		endsgent ();	/* in the new rooted file system */
#endif
		goto top;	/* go do all this all over again */
	}

#ifdef WITH_AUDIT
	audit_fd = audit_open ();
	audit_log_acct_message (audit_fd,
	                        AUDIT_USER_LOGIN,
	                        NULL,    /* Prog. name */
	                        "login",
	                        username,
	                        AUDIT_NO_ID,
	                        hostname,
	                        NULL,    /* addr */
	                        tty,
	                        1);      /* result */
	close (audit_fd);
#endif				/* WITH_AUDIT */

#ifndef USE_PAM			/* pam_lastlog handles this */
	if (getdef_bool ("LASTLOG_ENAB")) {	/* give last login and log this one */
		dolastlog (&ll, pwd, tty, hostname);
	}
#endif

#ifndef USE_PAM			/* PAM handles this as well */
	/*
	 * Have to do this while we still have root privileges, otherwise we
	 * don't have access to /etc/shadow.
	 */
	if (NULL != spwd) {		/* check for age of password */
		if (expire (pwd, spwd)) {
			/* The user updated her password, get the new
			 * entries.
			 * Use the x variants because we need to keep the
			 * entry for a long time, and there might be other
			 * getxxyy in between.
			 */
			pw_free (pwd);
			pwd = xgetpwnam (username);
			if (NULL == pwd) {
				SYSLOG ((LOG_ERR,
				         "cannot find user %s after update of expired password",
				         username));
				exit (1);
			}
			spw_free (spwd);
			spwd = xgetspnam (username);
		}
	}
	setup_limits (pwd);	/* nice, ulimit etc. */
#endif				/* ! USE_PAM */
	chown_tty (pwd);

#ifdef USE_PAM
	/*
	 * We must fork before setuid() because we need to call
	 * pam_close_session() as root.
	 */
	(void) signal (SIGINT, SIG_IGN);
	child = fork ();
	if (child < 0) {
		/* error in fork() */
		fprintf (stderr, _("%s: failure forking: %s"),
		         Prog, strerror (errno));
		PAM_END;
		exit (0);
	} else if (child != 0) {
		/*
		 * parent - wait for child to finish, then cleanup
		 * session
		 */
		wait (NULL);
		PAM_END;
		exit (0);
	}
	/* child */
#endif

	/* If we were init, we need to start a new session */
	if (getppid() == 1) {
		setsid();
		if (ioctl(0, TIOCSCTTY, 1) != 0) {
			fprintf (stderr, _("TIOCSCTTY failed on %s"), tty);
		}
	}

	/*
	 * The utmp entry needs to be updated to indicate the new status
	 * of the session, the new PID and SID.
	 */
	update_utmp (username, tty, hostname, utent);

	/* The pwd and spwd entries for the user have been copied.
	 *
	 * Close all the files so that unauthorized access won't occur.
	 */
	endpwent ();		/* stop access to password file */
	endgrent ();		/* stop access to group file */
	endspent ();		/* stop access to shadow passwd file */
#ifdef	SHADOWGRP
	endsgent ();		/* stop access to shadow group file */
#endif

	/* Drop root privileges */
#ifndef USE_PAM
	if (setup_uid_gid (pwd, is_console))
#else
	/* The group privileges were already dropped.
	 * See setup_groups() above.
	 */
	if (change_uid (pwd))
#endif
	{
		exit (1);
	}

	setup_env (pwd);	/* set env vars, cd to the home dir */

#ifdef USE_PAM
	{
		const char *const *env;

		env = (const char *const *) pam_getenvlist (pamh);
		while ((NULL != env) && (NULL != *env)) {
			addenv (*env, NULL);
			env++;
		}
	}
#endif

	(void) setlocale (LC_ALL, "");
	(void) bindtextdomain (PACKAGE, LOCALEDIR);
	(void) textdomain (PACKAGE);

	if (!hushed (username)) {
		addenv ("HUSHLOGIN=FALSE", NULL);
		/*
		 * pam_unix, pam_mail and pam_lastlog should take care of
		 * this
		 */
#ifndef USE_PAM
		motd ();	/* print the message of the day */
		if (   getdef_bool ("FAILLOG_ENAB")
		    && (0 != faillog.fail_cnt)) {
			failprint (&faillog);
			/* Reset the lockout times if logged in */
			if (   (0 != faillog.fail_max)
			    && (faillog.fail_cnt >= faillog.fail_max)) {
				(void) puts (_("Warning: login re-enabled after temporary lockout."));
				SYSLOG ((LOG_WARN,
				         "login '%s' re-enabled after temporary lockout (%d failures)",
				         username, (int) faillog.fail_cnt));
			}
		}
		if (   getdef_bool ("LASTLOG_ENAB")
		    && (ll.ll_time != 0)) {
			time_t ll_time = ll.ll_time;

#ifdef HAVE_STRFTIME
			(void) strftime (ptime, sizeof (ptime),
			                 "%a %b %e %H:%M:%S %z %Y",
			                 localtime (&ll_time));
			printf (_("Last login: %s on %s"),
			        ptime, ll.ll_line);
#else
			printf (_("Last login: %.19s on %s"),
			        ctime (&ll_time), ll.ll_line);
#endif
#ifdef HAVE_LL_HOST		/* __linux__ || SUN4 */
			if ('\0' != ll.ll_host[0]) {
				printf (_(" from %.*s"),
				        (int) sizeof ll.ll_host, ll.ll_host);
			}
#endif
			printf (".\n");
		}
		agecheck (spwd);

		mailcheck ();	/* report on the status of mail */
#endif				/* !USE_PAM */
	} else {
		addenv ("HUSHLOGIN=TRUE", NULL);
	}

	ttytype (tty);

	(void) signal (SIGQUIT, SIG_DFL);	/* default quit signal */
	(void) signal (SIGTERM, SIG_DFL);	/* default terminate signal */
	(void) signal (SIGALRM, SIG_DFL);	/* default alarm signal */
	(void) signal (SIGHUP, SIG_DFL);	/* added this.  --marekm */
	(void) signal (SIGINT, SIG_DFL);	/* default interrupt signal */

	if (0 == pwd->pw_uid) {
		SYSLOG ((LOG_NOTICE, "ROOT LOGIN %s", fromhost));
	} else if (getdef_bool ("LOG_OK_LOGINS")) {
		SYSLOG ((LOG_INFO, "'%s' logged in %s", username, fromhost));
	}
	closelog ();
	tmp = getdef_str ("FAKE_SHELL");
	if (NULL != tmp) {
		err = shell (tmp, pwd->pw_shell, newenvp); /* fake shell */
	} else {
		/* exec the shell finally */
		err = shell (pwd->pw_shell, (char *) 0, newenvp);
	}

	return ((err == ENOENT) ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
}
Esempio n. 8
0
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
				   int argc, const char **argv) {
    int i;
    const char *name;
    char const * password;
    char const * pwdfilename = NULL;
    char const * stored_crypted_password = NULL;
    char const * crypted_password;
    FILE *pwdfile;
    int use_flock = 0;
    int use_delay = 1;
    int legacy_crypt = 0;
    int debug = 0;
    char * linebuf = NULL;
    size_t linebuflen;
#ifdef USE_CRYPT_R
    struct crypt_data crypt_buf;
#endif
    
    /* we require the pwdfile switch and argument to be present, else we don't work */
    for (i = 0; i < argc; ++i) {
	if (!strcmp(argv[i], "pwdfile") && i + 1 < argc)
	    pwdfilename = argv[++i];
	else if (!strncmp(argv[i], "pwdfile=", strlen("pwdfile=")))
	    pwdfilename = argv[i] + strlen("pwdfile=");
	else if (!strcmp(argv[i], "flock"))
	    use_flock = 1;
	else if (!strcmp(argv[i], "noflock"))
	    use_flock = 0;
	else if (!strcmp(argv[i], "nodelay"))
	    use_delay = 0;
	else if (!strcmp(argv[i], "debug"))
	    debug = 1;
	else if (!strcmp(argv[i], "legacy_crypt"))
	    legacy_crypt = 1;
    }
    
#ifdef HAVE_PAM_FAIL_DELAY
    if (use_delay) {
	if (debug) pam_syslog(pamh, LOG_DEBUG, "setting fail delay");
	(void) pam_fail_delay(pamh, 2000000);   /* 2 sec */
    }
#endif
    
    if (!pwdfilename) {
	pam_syslog(pamh, LOG_ERR, "password file name not specified");
	return PAM_AUTHINFO_UNAVAIL;
    }
    
    if (pam_get_user(pamh, &name, NULL) != PAM_SUCCESS) {
	pam_syslog(pamh, LOG_ERR, "couldn't get username from PAM stack");
	return PAM_AUTH_ERR;
    }
    if (debug) pam_syslog(pamh, LOG_DEBUG, "username is %s", name);
    
    if (!(pwdfile = fopen(pwdfilename, "r"))) {
	pam_syslog(pamh, LOG_ALERT, "couldn't open password file %s", pwdfilename);
	return PAM_AUTHINFO_UNAVAIL;
    }
    
    if (use_flock && lock_fd(fileno(pwdfile)) == -1) {
	pam_syslog(pamh, LOG_ALERT, "couldn't lock password file %s", pwdfilename);
	fclose(pwdfile);
	return PAM_AUTHINFO_UNAVAIL;
    }
    
    /* get the crypted password corresponding to this user out of pwdfile */
    while (getline(&linebuf, &linebuflen, pwdfile) > 0) {
	/* strsep changes its argument, make a copy */
	char * nexttok = linebuf;
	
	/* first field: username */
	char * curtok = strsep(&nexttok, ":");
	
	/* skip non-matching usernames */
	if (strcmp(curtok, name))
	    continue;
	
	/* second field: password (until next colon or newline) */
	if ((curtok = strsep(&nexttok, ":\n"))) {
	    stored_crypted_password = curtok;
	    break;
	}
    }
    fclose(pwdfile);
    /* we keep linebuf (allocated by getline), stored_crypted_password is pointing into it */

    if (!stored_crypted_password)
	if (debug) pam_syslog(pamh, LOG_ERR, "user not found in password database");
    
    if (stored_crypted_password && !strlen(stored_crypted_password)) {
	if (debug) pam_syslog(pamh, LOG_DEBUG, "user has empty password field");
	free(linebuf);
	return flags & PAM_DISALLOW_NULL_AUTHTOK ? PAM_AUTH_ERR : PAM_SUCCESS;
    }
    
    if (pam_get_authtok(pamh, PAM_AUTHTOK, &password, NULL) != PAM_SUCCESS) {
	pam_syslog(pamh, LOG_ERR, "couldn't get password from PAM stack");
	free(linebuf);
	return PAM_AUTH_ERR;
    }
    
    if (!stored_crypted_password) {
	free(linebuf);
	return PAM_USER_UNKNOWN;
    }
    
    if (debug) pam_syslog(pamh, LOG_DEBUG, "got crypted password == '%s'", stored_crypted_password);
    
#ifdef USE_CRYPT_R
    crypt_buf.initialized = 0;
    if (!(crypted_password = crypt_r(password, stored_crypted_password, &crypt_buf)))
#else
    if (!(crypted_password = crypt(password, stored_crypted_password)))
#endif
    {
	pam_syslog(pamh, LOG_ERR, "crypt() failed");
	free(linebuf);
	return PAM_AUTH_ERR;
    }
    
    if (legacy_crypt && strcmp(crypted_password, stored_crypted_password)) {
	if (!strncmp(stored_crypted_password, "$1$", 3))
	    crypted_password = Brokencrypt_md5(password, stored_crypted_password);
	else
	    crypted_password = bigcrypt(password, stored_crypted_password);
    }

    if (strcmp(crypted_password, stored_crypted_password)) {
	pam_syslog(pamh, LOG_NOTICE, "wrong password for user %s", name);
	free(linebuf);
	return PAM_AUTH_ERR;
    }
    
    if (debug) pam_syslog(pamh, LOG_DEBUG, "passwords match");
    free(linebuf);
    return PAM_SUCCESS;
}
Esempio n. 9
0
/* expected hook for auth service */
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
				   int argc, const char **argv) {
    int retval, pcnt, pwdfilename_found;
    const char *name;
    char *password;
    char pwdfilename[PWDFN_LEN];
    char salt[12], stored_crypted_password[CRYPTED_BCPWD_LEN+1];
    char *crypted_password;
    FILE *pwdfile;
    int use_flock = 0;
    int use_delay = 1;
    int temp_result = 0;
    
    /* we require the pwdfile switch and argument to be present, else we don't work */
    /* pcnt is the parameter counter variable for iterating through argv */
    pcnt = pwdfilename_found = 0;
    do {
	/* see if the current parameter looks like "pwdfile" */
	if (strcmp(argv[pcnt],PWDF_PARAM)==0) {
	    /* if argv is long enough, grab the subsequent parameter */
	    if (pcnt+1 < argc) {
		/* make sure we can't overflow */
		strncpy(pwdfilename,argv[++pcnt],PWDFN_LEN);
		/* indicate that we've found it */
		pwdfilename_found = 1;
	    }
	    /* also check for "pwdfile=blah" */
	} else if (strncmp(argv[pcnt],PWDF_PARAM "=",sizeof(PWDF_PARAM "=")-1)==0) {
	    /* make sure we can't overflow */
	    strncpy(pwdfilename,argv[pcnt]+sizeof(PWDF_PARAM),PWDFN_LEN);
	    /* indicate that we've found it */
	    pwdfilename_found = 1;
	} else if (strcmp(argv[pcnt],FLOCK_PARAM)==0) {
	    /* we have a "flock" parameter */
	    use_flock = 1;
	} else if (strcmp(argv[pcnt],"no" FLOCK_PARAM)==0) {
	    /* or a "noflock" parameter */
	    use_flock = 0;
	} else if (strcmp(argv[pcnt],NODELAY_PARAM)==0) {
	    /* no delay on authentication failure */
	    use_delay = 0;
	}
	
    } while (++pcnt < argc);
    
#ifdef HAVE_PAM_FAIL_DELAY
    if (use_delay) {
	D(("setting delay"));
	(void) pam_fail_delay(pamh, 2000000);   /* 2 sec delay for on failure */
    }
#endif
    
    /* for some or other reason, the password file wasn't specified */
    if (!pwdfilename_found) {
	_pam_log(LOG_ERR,"password file name not specified");
	return PAM_AUTHINFO_UNAVAIL;
    }
    
    /* DEBUG */
    D(_pam_log(LOG_ERR, "password filename extracted"));
    
    /* now try to open the password file */
    if ((pwdfile=fopen(pwdfilename,"r"))==NULL) {
	_pam_log(LOG_ERR,"couldn't open password file %s",pwdfilename);
	return PAM_AUTHINFO_UNAVAIL;
    }
    
    /* set a lock on the password file */
    if (use_flock && lock_fd(fileno(pwdfile)) == -1) {
	_pam_log(LOG_ERR,"couldn't lock password file %s",pwdfilename);
	return PAM_AUTHINFO_UNAVAIL;
    }
    
    /* get user name */
    if ((retval = pam_get_user(pamh,&name,"login: "******"username not found");
	fclose(pwdfile);
	return retval;
    }
    
    
    /* DEBUG */
    D(_pam_log(LOG_ERR,"username is %s", name));
    
    /* get password - code from pam_unix_auth.c */
    pam_get_item(pamh, PAM_AUTHTOK, (void *)&password);
    if (!password) {
	retval = _set_auth_tok(pamh, flags, argc, argv);
	if (retval!=PAM_SUCCESS) {
	    fclose(pwdfile);
	    return retval;
	}
    }
    pam_get_item(pamh, PAM_AUTHTOK, (void *)&password);
    
    if ((retval = pam_get_item(pamh, PAM_AUTHTOK, (void *)&password)) != PAM_SUCCESS) {
	_pam_log(LOG_ERR, "auth token not found");
	fclose(pwdfile);
	return retval;
    }
    
    /* DEBUG */
    D(_pam_log(LOG_ERR,"got password from user", password));
    
    /* now crypt password and compare to the user entry in the password file */
    /* first make sure password is long enough -- may I do this? */
    if (strlen(password)<2 || password==NULL) {
	_pam_log(LOG_ERR,"password too short or NULL");
	fclose(pwdfile);
	return PAM_AUTH_ERR;
    }
    
    /* get the crypted password corresponding to this user */
    if (!fgetpwnam(pwdfile, name, stored_crypted_password)) {
	_pam_log(LOG_ERR,"user not found in password database");
	fclose(pwdfile);
	return PAM_AUTHINFO_UNAVAIL;
    }
    
    /* DEBUG */
    D(_pam_log(LOG_ERR,"got crypted password == '%s'", stored_crypted_password));
    
    
    temp_result = 0;
    
    /* Extract the salt and set the passwd length, depending on MD5 or DES */
    if (strncmp(stored_crypted_password, "$1$", 3) == 0) {
	D(_pam_log(LOG_ERR,"password hash type is 'md5'"));
	/* get out the salt into "salt" */
	strncpy(salt, stored_crypted_password, 11);
	salt[11] = '\0';
	stored_crypted_password[CRYPTED_MD5PWD_LEN] = '\0';
	/* try both md5 crypts */
	crypted_password = Goodcrypt_md5(password, salt);
	if (strcmp(crypted_password, stored_crypted_password) == 0)
	{
	    temp_result = 1;
	}
	else
	{
	    crypted_password = Brokencrypt_md5(password, salt);
	    if (strcmp(crypted_password, stored_crypted_password) == 0)
	    {
		temp_result = 1;
	    }
	}
    } else {
	/* get the salt out into "salt" */
	strncpy(salt, stored_crypted_password, 2);
	salt[2] = '\0';
	stored_crypted_password[CRYPTED_BCPWD_LEN] = '\0';

	if (strlen(stored_crypted_password) <= CRYPTED_DESPWD_LEN) {
	    D(_pam_log(LOG_ERR,"password hash type is 'crypt'"));
	    crypted_password = crypt(password, salt);
	} else {
	    D(_pam_log(LOG_ERR,"password hash type is 'bigcrypt'"));
	    crypted_password = bigcrypt(password, salt);
	}

	if (strcmp(crypted_password, stored_crypted_password) == 0)
	{
	    temp_result = 1;
	}
    }
    
    /* DEBUG */
    D(_pam_log(LOG_ERR,"user password crypted is '%s'", crypted_password));
    
    /* if things don't match up, complain */
    if (!temp_result) 
    {
	_pam_log(LOG_ERR,"wrong password for user %s",name);
	fclose(pwdfile);
	return PAM_AUTH_ERR;
    }
    
    /* DEBUG */
    D(_pam_log(LOG_ERR,"passwords match"));
    
    /* we've gotten here, i.e. authentication was sucessful! */
    fclose(pwdfile);
    return PAM_SUCCESS;
}