static int
send_text (pam_handle_t *pamh, const char *text, int debug)
{
  if (debug)
    pam_syslog(pamh, LOG_NOTICE, "%s", text);
  return pam_info (pamh, "%s", text);
}
Beispiel #2
0
static int
_pam_echo(pam_handle_t *pamh, int flags,
    int argc, const char *argv[])
{
	char msg[PAM_MAX_MSG_SIZE];
	const void *str;
	const char *p, *q;
	int err, i, item;
	size_t len;

	if (flags & PAM_SILENT)
		return (PAM_SUCCESS);
	for (i = 0, len = 0; i < argc && len < sizeof(msg) - 1; ++i) {
		if (i > 0)
			msg[len++] = ' ';
		for (p = argv[i]; *p != '\0' && len < sizeof(msg) - 1; ++p) {
			if (*p != '%' || p[1] == '\0') {
				msg[len++] = *p;
				continue;
			}
			switch (*++p) {
			case 'H':
				item = PAM_RHOST;
				break;
			case 'h':
				/* not implemented */
				item = -1;
				break;
			case 's':
				item = PAM_SERVICE;
				break;
			case 't':
				item = PAM_TTY;
				break;
			case 'U':
				item = PAM_RUSER;
				break;
			case 'u':
				item = PAM_USER;
				break;
			default:
				item = -1;
				msg[len++] = *p;
				break;
			}
			if (item == -1)
				continue;
			err = pam_get_item(pamh, item, &str);
			if (err != PAM_SUCCESS)
				return (err);
			if (str == NULL)
				str = "(null)";
			for (q = str; *q != '\0' && len < sizeof(msg) - 1; ++q)
				msg[len++] = *q;
		}
	}
	msg[len] = '\0';
	return (pam_info(pamh, "%s", msg));
}
Beispiel #3
0
static int
report_mail(pam_handle_t *pamh, int ctrl, int type, const char *folder)
{
    int retval;

    if (!(ctrl & PAM_MAIL_SILENT) ||
	((ctrl & PAM_QUIET_MAIL) && type == HAVE_NEW_MAIL))
      {
	if (ctrl & PAM_STANDARD_MAIL)
	  switch (type)
	    {
	    case HAVE_NO_MAIL:
	      retval = pam_info (pamh, "%s", _("No mail."));
	      break;
	    case HAVE_NEW_MAIL:
	      retval = pam_info (pamh, "%s", _("You have new mail."));
	      break;
	    case HAVE_OLD_MAIL:
	      retval = pam_info (pamh, "%s", _("You have old mail."));
	      break;
	    case HAVE_MAIL:
	    default:
	      retval = pam_info (pamh, "%s", _("You have mail."));
	      break;
	    }
	else
	  switch (type)
	    {
	    case HAVE_NO_MAIL:
	      retval = pam_info (pamh, _("You have no mail in folder %s."),
				 folder);
	      break;
	    case HAVE_NEW_MAIL:
	      retval = pam_info (pamh, _("You have new mail in folder %s."),
				 folder);
	      break;
	    case HAVE_OLD_MAIL:
	      retval = pam_info (pamh, _("You have old mail in folder %s."),
				 folder);
	      break;
	    case HAVE_MAIL:
	    default:
	      retval = pam_info (pamh, _("You have mail in folder %s."),
				 folder);
	      break;
	    }
      }
    else
      {
	D(("keeping quiet"));
	retval = PAM_SUCCESS;
      }

    D(("returning %s", pam_strerror(pamh, retval)));
    return retval;
}
Beispiel #4
0
static int
read_fd(pam_handle_t *pamh, const char *file, int fd)
{
	char buf[1024], *p;
	ssize_t rd;
	size_t total = 0;
	size_t level = 0;
	
	while (total < max_output_size) {
		size_t rdsize = sizeof(buf) - level - 1;

		if (total + rdsize >= max_output_size &&
		    (rdsize = max_output_size - total) == 0)
			break;
		rd = read(fd, buf + level, rdsize);
		if (rd <= 0)
			break;
		total += rd;
		level += rd;
		buf[level] = 0;
		p = strrchr(buf, '\n');
		if (p)
			*p++ = 0;
		pam_info(pamh, "%s", buf);
		if (p && *p) {
			level = strlen(p);
			memmove(buf, p, level);
		} else
			level = 0;
	}
	if (level) {
		buf[level] = 0;
		pam_info(pamh, "%s", buf);
	}
	if (rd < 0) {
		_pam_log(LOG_ERR, "error reading file %s: %s",
			 file, strerror(errno));
		return PAM_SYSTEM_ERR;
	}
	return PAM_SUCCESS;
}
static int state(pam_handle_t *pamh, const char *text)
{
    int retval;

    retval = pam_info (pamh, "%s", text);

    if (retval != PAM_SUCCESS) {
	D(("pam_info failed"));
    }

    return retval;
}
Beispiel #6
0
static void test_pam_prompt(void **state)
{
	struct pwrap_test_ctx *test_ctx;
	int rv;
	char *response;
	int resp_array[2];

	test_ctx = (struct pwrap_test_ctx *) *state;

	memset(resp_array, 0, sizeof(resp_array));

	test_ctx->conv.conv = pwrap_echo_conv;
	test_ctx->conv.appdata_ptr = resp_array;

	rv = pam_start("matrix", "trinity",
		       &test_ctx->conv, &test_ctx->ph);
	assert_int_equal(rv, PAM_SUCCESS);

	rv = pam_prompt(test_ctx->ph, PAM_PROMPT_ECHO_OFF, &response, "no echo");
	assert_int_equal(rv, PAM_SUCCESS);
	assert_string_equal(response, "echo off: no echo");
	free(response);

	rv = vprompt_test_fn(test_ctx->ph, PAM_PROMPT_ECHO_OFF, &response, "no echo");
	assert_int_equal(rv, PAM_SUCCESS);
	assert_string_equal(response, "echo off: no echo");
	free(response);

	rv = pam_prompt(test_ctx->ph, PAM_PROMPT_ECHO_ON, &response, "echo");
	assert_int_equal(rv, PAM_SUCCESS);
	assert_string_equal(response, "echo on: echo");
	free(response);

	rv = vprompt_test_fn(test_ctx->ph, PAM_PROMPT_ECHO_ON, &response, "echo");
	assert_int_equal(rv, PAM_SUCCESS);
	assert_string_equal(response, "echo on: echo");
	free(response);

	assert_int_equal(resp_array[0], 0);
	pam_info(test_ctx->ph, "info");
	assert_int_equal(resp_array[0], 1);

	assert_int_equal(resp_array[1], 0);
	pam_error(test_ctx->ph, "error");
	assert_int_equal(resp_array[1], 1);
}
Beispiel #7
0
static int
replace_and_print (pam_handle_t *pamh, const char *mesg)
{
  char *output;
  size_t length = strlen (mesg) + PAM_MAX_MSG_SIZE;
  char myhostname[HOST_NAME_MAX+1];
  const void *str = NULL;
  const char *p, *q;
  int item;
  size_t len;

  output = malloc (length);
  if (output == NULL)
    {
      pam_syslog (pamh, LOG_ERR, "running out of memory");
      return PAM_BUF_ERR;
    }

  for (p = mesg, len = 0; *p != '\0' && len < length - 1; ++p)
    {
      if (*p != '%' || p[1] == '\0')
	{
	  output[len++] = *p;
	  continue;
	}
      switch (*++p)
	{
	case 'H':
	  item = PAM_RHOST;
	  break;
	case 'h':
	  item = -2; /* aka PAM_LOCALHOST */
	  break;
	case 's':
	  item = PAM_SERVICE;
	  break;
	case 't':
	  item = PAM_TTY;
	  break;
	case 'U':
	  item = PAM_RUSER;
	  break;
	case 'u':
	  item = PAM_USER;
	  break;
	default:
	  output[len++] = *p;
	  continue;
	}
      if (item == -2)
	{
	  if (gethostname (myhostname, sizeof (myhostname)) == -1)
	    str = NULL;
	  else
	    str = &myhostname;
	}
      else
	{
	  if (pam_get_item (pamh, item, &str) != PAM_SUCCESS)
	    str = NULL;
	}
      if (str == NULL)
	str = "(null)";
      for (q = str; *q != '\0' && len < length - 1; ++q)
	output[len++] = *q;
    }
  output[len] = '\0';

  pam_info (pamh, "%s", output);
  free (output);

  return PAM_SUCCESS;
}
Beispiel #8
0
static void
__duo_status(void *arg, const char *msg)
{
	pam_info((pam_handle_t *)arg, "%s", msg);
}
Beispiel #9
0
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int pam_flags,
    int argc, const char *argv[])
{
	struct duo_config cfg;
	struct passwd *pw;
	struct in_addr addr;
	duo_t *duo;
	duo_code_t code;
	duopam_const char *config, *cmd, *p, *service, *user;
	const char *ip, *host;
	int i, flags, pam_err, matched;

	duo_config_default(&cfg);

	/* Parse configuration */
	config = DUO_CONF;
	for (i = 0; i < argc; i++) {
		if (strncmp("conf=", argv[i], 5) == 0) {
			config = argv[i] + 5;
		} else if (strcmp("debug", argv[i]) == 0) {
			duo_debug = 1;
		} else {
			duo_syslog(LOG_ERR, "Invalid pam_duo option: '%s'",
			    argv[i]);
			return (PAM_SERVICE_ERR);
		}
	}
	i = duo_parse_config(config, __ini_handler, &cfg);
	if (i == -2) {
		duo_syslog(LOG_ERR, "%s must be readable only by user 'root'",
		    config);
		return (cfg.failmode == DUO_FAIL_SAFE ? PAM_SUCCESS : PAM_SERVICE_ERR);
	} else if (i == -1) {
		duo_syslog(LOG_ERR, "Couldn't open %s: %s",
		    config, strerror(errno));
		return (cfg.failmode == DUO_FAIL_SAFE ? PAM_SUCCESS : PAM_SERVICE_ERR);
	} else if (i > 0) {
		duo_syslog(LOG_ERR, "Parse error in %s, line %d", config, i);
		return (cfg.failmode == DUO_FAIL_SAFE ? PAM_SUCCESS : PAM_SERVICE_ERR);
	} else if (!cfg.apihost || !cfg.apihost[0] ||
            !cfg.skey || !cfg.skey[0] || !cfg.ikey || !cfg.ikey[0]) {
		duo_syslog(LOG_ERR, "Missing host, ikey, or skey in %s", config);
		return (cfg.failmode == DUO_FAIL_SAFE ? PAM_SUCCESS : PAM_SERVICE_ERR);
	}
        
    /* Check user */
    if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS ||
        (pw = getpwnam(user)) == NULL) {
            return (PAM_USER_UNKNOWN);
    }
    /* XXX - Service-specific behavior */
	flags = 0;
    cmd = NULL;
	if (pam_get_item(pamh, PAM_SERVICE, (duopam_const void **)
		(duopam_const void *)&service) != PAM_SUCCESS) {
                return (PAM_SERVICE_ERR);
        }
    if (strcmp(service, "sshd") == 0) {
            /*
             * Disable incremental status reporting for sshd :-(
             * OpenSSH accumulates PAM_TEXT_INFO from modules to send in
             * an SSH_MSG_USERAUTH_BANNER post-auth, not real-time!
             */
            flags |= DUO_FLAG_SYNC;
    } else if (strcmp(service, "sudo") == 0) {
            cmd = getenv("SUDO_COMMAND");
    } else if (strcmp(service, "su") == 0) {
            /* Check calling user for Duo auth, just like sudo */
            if ((pw = getpwuid(getuid())) == NULL) {
                    return (PAM_USER_UNKNOWN);
            }
            user = pw->pw_name;
    }
	/* Check group membership */
    matched = duo_check_groups(pw, cfg.groups, cfg.groups_cnt);
    if (matched == -1) {
        return (PAM_SERVICE_ERR);
    } else if (matched == 0) {
        return (PAM_SUCCESS);
    }

    /* Grab the remote host */
	ip = NULL;
	pam_get_item(pamh, PAM_RHOST,
	    (duopam_const void **)(duopam_const void *)&ip);
	host = ip;
	/* PAM is weird, check to see if PAM_RHOST is IP or hostname */
	if (ip == NULL) {
		ip = ""; /* XXX inet_addr needs a non-null IP */
	}
	if (!inet_aton(ip, &addr)) {
		/* We have a hostname, don't try to resolve, check fallback */
		ip = (cfg.local_ip_fallback ? duo_local_ip() : NULL);
	}

	/* Honor configured http_proxy */
	if (cfg.http_proxy != NULL) {
		setenv("http_proxy", cfg.http_proxy, 1);
	}

	/* Try Duo auth */
	if ((duo = duo_open(cfg.apihost, cfg.ikey, cfg.skey,
                    "pam_duo/" PACKAGE_VERSION,
                    cfg.noverify ? "" : cfg.cafile, DUO_NO_TIMEOUT)) == NULL) {
		duo_log(LOG_ERR, "Couldn't open Duo API handle", user, host, NULL);
		return (PAM_SERVICE_ERR);
	}
	duo_set_conv_funcs(duo, __duo_prompt, __duo_status, pamh);

	if (cfg.autopush) {
		flags |= DUO_FLAG_AUTO;
	}

	pam_err = PAM_SERVICE_ERR;

	for (i = 0; i < cfg.prompts; i++) {
		code = duo_login(duo, user, host, flags,
                    cfg.pushinfo ? cmd : NULL, cfg.suffix);
		if (code == DUO_FAIL) {
			duo_log(LOG_WARNING, "Failed Duo login",
			    user, host, duo_geterr(duo));
			if ((flags & DUO_FLAG_SYNC) == 0) {
				pam_info(pamh, "%s", "");
                        }
			/* Keep going */
			continue;
		}
		/* Terminal conditions */
		if (code == DUO_OK) {
			if ((p = duo_geterr(duo)) != NULL) {
				duo_log(LOG_WARNING, "Skipped Duo login",
				    user, host, p);
			} else {
				duo_log(LOG_INFO, "Successful Duo login",
				    user, host, NULL);
			}
			pam_err = PAM_SUCCESS;
		} else if (code == DUO_ABORT) {
			duo_log(LOG_WARNING, "Aborted Duo login",
			    user, host, duo_geterr(duo));
			pam_err = PAM_ABORT;
		} else if (cfg.failmode == DUO_FAIL_SAFE &&
                    (code == DUO_CONN_ERROR ||
                     code == DUO_CLIENT_ERROR || code == DUO_SERVER_ERROR)) {
			duo_log(LOG_WARNING, "Failsafe Duo login",
			    user, host, duo_geterr(duo));
			pam_err = PAM_SUCCESS;
		} else {
			duo_log(LOG_ERR, "Error in Duo login",
			    user, host, duo_geterr(duo));
			pam_err = PAM_SERVICE_ERR;
		}
		break;
	}
	if (i == MAX_PROMPTS) {
		pam_err = PAM_MAXTRIES;
	}
	duo_close(duo);
	
	return (pam_err);
}
Beispiel #10
0
static int
last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime)
{
    int retval;
    int fd;
    struct utmp ut;
    struct utmp utuser;
    int failed = 0;
    char the_time[256];
    char *date = NULL;
    char *host = NULL;
    char *line = NULL;

    if (strlen(user) > UT_NAMESIZE) {
	pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate");
    }

    /* obtain the failed login attempt records from btmp */
    fd = open(_PATH_BTMP, O_RDONLY);
    if (fd < 0) {
        int save_errno = errno;
	pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP);
	D(("unable to open %s file", _PATH_BTMP));
        if (save_errno == ENOENT)
	  return PAM_SUCCESS;
	else
	  return PAM_SERVICE_ERR;
    }

    while ((retval=pam_modutil_read(fd, (void *)&ut,
			 sizeof(ut))) == sizeof(ut)) {
	if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) {
	    memcpy(&utuser, &ut, sizeof(utuser));
	    failed++;
	}
    }

    if (failed) {
	/* we want the date? */
	if (announce & LASTLOG_DATE) {
	    struct tm *tm, tm_buf;
	    time_t lf_time;

	    lf_time = utuser.ut_tv.tv_sec;
	    tm = localtime_r (&lf_time, &tm_buf);
	    strftime (the_time, sizeof (the_time),
	        /* TRANSLATORS: "strftime options for date of last login" */
		_(" %a %b %e %H:%M:%S %Z %Y"), tm);

	    date = the_time;
	}

	/* we want & have the host? */
	if ((announce & LASTLOG_HOST)
		&& (utuser.ut_host[0] != '\0')) {
	    /* TRANSLATORS: " from <host>" */
	    if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
		    utuser.ut_host) < 0) {
		pam_syslog(pamh, LOG_ERR, "out of memory");
		retval = PAM_BUF_ERR;
		goto cleanup;
	    }
	}

	/* we want and have the terminal? */
	if ((announce & LASTLOG_LINE)
		&& (utuser.ut_line[0] != '\0')) {
	    /* TRANSLATORS: " on <terminal>" */
	    if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
			utuser.ut_line) < 0) {
		pam_syslog(pamh, LOG_ERR, "out of memory");
		retval = PAM_BUF_ERR;
		goto cleanup;
	    }
	}

	if (line != NULL || date != NULL || host != NULL) {
	    /* TRANSLATORS: "Last failed login: <date> from <host> on <terminal>" */
	    pam_info(pamh, _("Last failed login:%s%s%s"),
			      date ? date : "",
			      host ? host : "",
			      line ? line : "");
	}

	_pam_drop(line);
#if defined HAVE_DNGETTEXT && defined ENABLE_NLS
        retval = asprintf (&line, dngettext(PACKAGE,
		"There was %d failed login attempt since the last successful login.",
		"There were %d failed login attempts since the last successful login.",
		failed),
	    failed);
#else
	if (failed == 1)
	    retval = asprintf(&line,
		_("There was %d failed login attempt since the last successful login."),
		failed);
	else
	    retval = asprintf(&line,
		/* TRANSLATORS: only used if dngettext is not supported */
		_("There were %d failed login attempts since the last successful login."),
		failed);
#endif
	if (retval >= 0)
		retval = pam_info(pamh, "%s", line);
	else {
		retval = PAM_BUF_ERR;
		line = NULL;
	}
    }

cleanup:
    free(host);
    free(line);
    close(fd);
    D(("all done with btmp"));

    return retval;
}
Beispiel #11
0
static int
last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime)
{
    struct flock last_lock;
    struct lastlog last_login;
    int retval = PAM_SUCCESS;
    char the_time[256];
    char *date = NULL;
    char *host = NULL;
    char *line = NULL;

    memset(&last_lock, 0, sizeof(last_lock));
    last_lock.l_type = F_RDLCK;
    last_lock.l_whence = SEEK_SET;
    last_lock.l_start = sizeof(last_login) * (off_t) uid;
    last_lock.l_len = sizeof(last_login);

    if (fcntl(last_fd, F_SETLK, &last_lock) < 0) {
        D(("locking %s failed..(waiting a little)", _PATH_LASTLOG));
	pam_syslog(pamh, LOG_WARNING,
		   "file %s is locked/read", _PATH_LASTLOG);
	sleep(LASTLOG_IGNORE_LOCK_TIME);
    }

    if (pam_modutil_read(last_fd, (char *) &last_login,
			 sizeof(last_login)) != sizeof(last_login)) {
        memset(&last_login, 0, sizeof(last_login));
    }

    last_lock.l_type = F_UNLCK;
    (void) fcntl(last_fd, F_SETLK, &last_lock);        /* unlock */

    *lltime = last_login.ll_time;
    if (!last_login.ll_time) {
        if (announce & LASTLOG_DEBUG) {
	    pam_syslog(pamh, LOG_DEBUG,
		       "first login for user with uid %lu",
		       (unsigned long int)uid);
	}
    }

    if (!(announce & LASTLOG_QUIET)) {

	if (last_login.ll_time) {

	    /* we want the date? */
	    if (announce & LASTLOG_DATE) {
	        struct tm *tm, tm_buf;
		time_t ll_time;

		ll_time = last_login.ll_time;
		tm = localtime_r (&ll_time, &tm_buf);
		strftime (the_time, sizeof (the_time),
	        /* TRANSLATORS: "strftime options for date of last login" */
			  _(" %a %b %e %H:%M:%S %Z %Y"), tm);

		date = the_time;
	    }

	    /* we want & have the host? */
	    if ((announce & LASTLOG_HOST)
		&& (last_login.ll_host[0] != '\0')) {
		/* TRANSLATORS: " from <host>" */
		if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE,
			     last_login.ll_host) < 0) {
		    pam_syslog(pamh, LOG_ERR, "out of memory");
		    retval = PAM_BUF_ERR;
		    goto cleanup;
		}
	    }

	    /* we want and have the terminal? */
	    if ((announce & LASTLOG_LINE)
		&& (last_login.ll_line[0] != '\0')) {
		/* TRANSLATORS: " on <terminal>" */
		if (asprintf(&line, _(" on %.*s"), UT_LINESIZE,
			     last_login.ll_line) < 0) {
		    pam_syslog(pamh, LOG_ERR, "out of memory");
		    retval = PAM_BUF_ERR;
		    goto cleanup;
		}
	    }

	    if (date != NULL || host != NULL || line != NULL)
		    /* TRANSLATORS: "Last login: <date> from <host> on <terminal>" */
		    retval = pam_info(pamh, _("Last login:%s%s%s"),
			      date ? date : "",
			      host ? host : "",
			      line ? line : "");
	} else if (announce & LASTLOG_NEVER) {
		D(("this is the first time this user has logged in"));
		retval = pam_info(pamh, "%s", _("Welcome to your new account!"));
	}
    }

    /* cleanup */
 cleanup:
    memset(&last_login, 0, sizeof(last_login));
    _pam_overwrite(date);
    _pam_overwrite(host);
    _pam_drop(host);
    _pam_overwrite(line);
    _pam_drop(line);

    return retval;
}
Beispiel #12
0
int
pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
{
  struct passwd *pwd;
  const char *newpass;
  const char *user;
    int retval, tries;
  options_t options;

  memset (&options, 0, sizeof (options));

  /* Set some default values, which could be overwritten later.  */
  options.remember = 10;
  options.tries = 1;

  /* Parse parameters for module */
  for ( ; argc-- > 0; argv++)
    parse_option (pamh, *argv, &options);

  if (options.debug)
    pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok entered");


  if (options.remember == 0)
    return PAM_IGNORE;

  retval = pam_get_user (pamh, &user, NULL);
  if (retval != PAM_SUCCESS)
    return retval;

  if (user == NULL || strlen (user) == 0)
    {
      if (options.debug)
	pam_syslog (pamh, LOG_DEBUG,
		    "User is not known to system");

      return PAM_USER_UNKNOWN;
    }

  if (flags & PAM_PRELIM_CHECK)
    {
      if (options.debug)
	pam_syslog (pamh, LOG_DEBUG,
		    "pam_sm_chauthtok(PAM_PRELIM_CHECK)");

      return PAM_SUCCESS;
    }

  pwd = pam_modutil_getpwnam (pamh, user);
  if (pwd == NULL)
    return PAM_USER_UNKNOWN;

  if ((strcmp(pwd->pw_passwd, "x") == 0)  ||
      ((pwd->pw_passwd[0] == '#') &&
       (pwd->pw_passwd[1] == '#') &&
       (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)))
    {
      struct spwd *spw = pam_modutil_getspnam (pamh, user);
      if (spw == NULL)
	return PAM_USER_UNKNOWN;

      retval = save_old_pass (pamh, user, pwd->pw_uid, spw->sp_pwdp,
			      options.remember, options.debug);
      if (retval != PAM_SUCCESS)
	return retval;
    }
  else
    {
      retval = save_old_pass (pamh, user, pwd->pw_uid, pwd->pw_passwd,
			      options.remember, options.debug);
      if (retval != PAM_SUCCESS)
	return retval;
    }

  newpass = NULL;
  tries = 0;
  while ((newpass == NULL) && (tries < options.tries))
    {
      retval = pam_get_authtok (pamh, PAM_AUTHTOK, &newpass, NULL);
      if (retval != PAM_SUCCESS && retval != PAM_TRY_AGAIN)
	{
	  if (retval == PAM_CONV_AGAIN)
	    retval = PAM_INCOMPLETE;
	  return retval;
	}
      tries++;

      if (options.debug)
	{
	  if (newpass)
	    pam_syslog (pamh, LOG_DEBUG, "got new auth token");
	  else
	    pam_syslog (pamh, LOG_DEBUG, "got no auth token");
	}

      if (newpass == NULL || retval == PAM_TRY_AGAIN)
	continue;

      if (options.debug)
	pam_syslog (pamh, LOG_DEBUG, "check against old password file");

      if (check_old_pass (pamh, user, newpass,
			  options.debug) != PAM_SUCCESS)
	{
	  if (getuid() || options.enforce_for_root ||
	      (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
	    {
	      pam_error (pamh,
		         _("Password has been already used. Choose another."));
	      newpass = NULL;
	      /* Remove password item, else following module will use it */
	      pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL);
	    }
	  else
	    pam_info (pamh,
		       _("Password has been already used."));
	}
    }

  if (newpass == NULL && tries >= options.tries)
    {
      if (options.debug)
	pam_syslog (pamh, LOG_DEBUG, "Aborted, too many tries");
      return PAM_MAXTRIES;
    }

  return PAM_SUCCESS;
}
Beispiel #13
0
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int pam_flags,
    int argc, const char *argv[])
{
	struct duo_config cfg;
	struct passwd *pw;
	duo_t *duo;
	duo_code_t code;
	duopam_const char *config, *cmd, *ip, *p, *service, *user;
	int i, flags, pam_err;

	memset(&cfg, 0, sizeof(cfg));
        cfg.failmode = DUO_FAIL_SAFE;

	/* Parse configuration */
	config = DUO_CONF;
	for (i = 0; i < argc; i++) {
		if (strncmp("conf=", argv[i], 5) == 0) {
			config = argv[i] + 5;
		} else if (strcmp("debug", argv[i]) == 0) {
			options |= PAM_OPT_DEBUG;
		} else if (strcmp("try_first_pass", argv[i]) == 0) {
			options |= PAM_OPT_TRY_FIRST_PASS;
		} else if (strcmp("use_first_pass", argv[i]) == 0) {
			options |= PAM_OPT_USE_FIRST_PASS|PAM_OPT_TRY_FIRST_PASS;
		} else if (strcmp("use_uid", argv[i]) == 0) {
			options |= PAM_OPT_USE_UID;
		} else if (strcmp("push", argv[i]) == 0) {
			options |= PAM_OPT_PUSH;
		} else {
			_syslog(LOG_ERR, "Invalid pam_duo option: '%s'",
			    argv[i]);
			return (PAM_SERVICE_ERR);
		}
	}
	i = duo_parse_config(config, __ini_handler, &cfg);
	if (i == -2) {
		_syslog(LOG_ERR, "%s must be readable only by user 'root'",
		    config);
		return (PAM_SERVICE_ERR);
	} else if (i == -1) {
		_syslog(LOG_ERR, "Couldn't open %s: %s",
		    config, strerror(errno));
		return (PAM_SERVICE_ERR);
	} else if (i > 0) {
		_syslog(LOG_ERR, "Parse error in %s, line %d", config, i);
		return (PAM_SERVICE_ERR);
	} else if (!cfg.host || !cfg.host[0] ||
            !cfg.skey || !cfg.skey[0] || !cfg.ikey || !cfg.ikey[0]) {
		_syslog(LOG_ERR, "Missing host, ikey, or skey in %s", config);
		return (PAM_SERVICE_ERR);
	}
        
        /* Check user */
        if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS ||
            (pw = getpwnam(user)) == NULL) {
                return (PAM_USER_UNKNOWN);
        }
        /* XXX - Service-specific behavior */
	flags = 0;
        cmd = NULL;
	if (pam_get_item(pamh, PAM_SERVICE, (duopam_const void **)
		(duopam_const void *)&service) != PAM_SUCCESS) {
                return (PAM_SERVICE_ERR);
        }
	if (options & PAM_OPT_USE_UID) {
                /* Check calling user for Duo auth, just like sudo */
                if ((pw = getpwuid(getuid())) == NULL) {
                        return (PAM_USER_UNKNOWN);
                }
                user = pw->pw_name;
	}

        if (strcmp(service, "sshd") == 0) {
                /*
                 * Disable incremental status reporting for sshd :-(
                 * OpenSSH accumulates PAM_TEXT_INFO from modules to send in
                 * an SSH_MSG_USERAUTH_BANNER post-auth, not real-time!
                 */
                flags |= DUO_FLAG_SYNC;
        } else if (strcmp(service, "sudo") == 0) {
                cmd = getenv("SUDO_COMMAND");
        }
	/* Check group membership */
	if (cfg.groups_cnt > 0) {
		int matched = 0;

		if (ga_init(pw->pw_name, pw->pw_gid) < 0) {
			_log(LOG_ERR, "Couldn't get groups",
			    pw->pw_name, NULL, strerror(errno));
			return (PAM_SERVICE_ERR);
		}
		for (i = 0; i < cfg.groups_cnt; i++) {
			if (ga_match_pattern_list(cfg.groups[i])) {
				matched = 1;
				break;
			}
		}
		ga_free();

		/* User in configured groups for Duo auth? */
		if (!matched)
			return (PAM_SUCCESS);
	}

	ip = NULL;
	pam_get_item(pamh, PAM_RHOST,
	    (duopam_const void **)(duopam_const void *)&ip);

	/* Honor configured http_proxy */
	if (cfg.http_proxy != NULL) {
		setenv("http_proxy", cfg.http_proxy, 1);
	}

	/* Try Duo auth */
	if ((duo = duo_open(cfg.host, cfg.ikey, cfg.skey,
                    "pam_duo/" PACKAGE_VERSION,
                    cfg.noverify ? "" : cfg.cafile)) == NULL) {
		_log(LOG_ERR, "Couldn't open Duo API handle", user, ip, NULL);
		return (PAM_SERVICE_ERR);
	}
	duo_set_conv_funcs(duo, __duo_prompt, __duo_status, pamh);

	pam_err = PAM_SERVICE_ERR;
	
	for (i = 0; i < MAX_RETRIES; i++) {
		code = duo_login(duo, user, ip, flags,
                    cfg.pushinfo ? cmd : NULL);
		if (code == DUO_FAIL) {
			_log(LOG_WARNING, "Failed Duo login",
			    user, ip, duo_geterr(duo));
			if ((flags & DUO_FLAG_SYNC) == 0) {
				pam_info(pamh, "%s", "");
                        }
			/* Keep going */
			continue;
		}
		/* Terminal conditions */
		if (code == DUO_OK) {
			if ((p = duo_geterr(duo)) != NULL) {
				_log(LOG_WARNING, "Skipped Duo login",
				    user, ip, p);
			} else {
				_log(LOG_INFO, "Successful Duo login",
				    user, ip, NULL);
			}
			pam_err = PAM_SUCCESS;
		} else if (code == DUO_ABORT) {
			_log(LOG_WARNING, "Aborted Duo login",
			    user, ip, duo_geterr(duo));
			pam_err = PAM_ABORT;
		} else if (cfg.failmode == DUO_FAIL_SAFE &&
                    (code == DUO_CONN_ERROR ||
                     code == DUO_CLIENT_ERROR || code == DUO_SERVER_ERROR)) {
			_log(LOG_WARNING, "Failsafe Duo login",
			    user, ip, duo_geterr(duo));
			pam_err = PAM_SUCCESS;
		} else {
			_log(LOG_ERR, "Error in Duo login",
			    user, ip, duo_geterr(duo));
			pam_err = PAM_SERVICE_ERR;
		}
		break;
	}
	if (i == MAX_RETRIES) {
		pam_err = PAM_MAXTRIES;
	}
	duo_close(duo);
	
	return (pam_err);
}
Beispiel #14
0
/* Tell the user that access has been granted. */
static void
verbose_success(pam_handle_t *pamh, long diff)
{
	pam_info(pamh, _("Access granted (last access was %ld seconds ago)."), diff);
}
Beispiel #15
0
// CALLED BY PAM_AUTHENTICATE
PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,
				   int argc, const char **argv)
{
    module_config *cfg = NULL;
    user_config *user_cfg = NULL;
    int retval;
    unsigned int trial;
    const char *authtok = NULL;

    retval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) &authtok);
    if (retval != PAM_SUCCESS || (authtok != NULL && !strcmp(authtok, AUTHTOK_INCORRECT))) {
        D(("Previous authentication failed, let's stop here!"));
	return PAM_AUTH_ERR;
    }

    retval = parse_config(pamh, argc, argv, &cfg);

    //CHECK PAM CONFIGURATION
    if (retval == CONFIG_ERROR) {
        D(("Invalid configuration"));
	pam_syslog(pamh, LOG_ERR, "Invalid parameters to pam_2fa module");
	pam_error(pamh, "Sorry, 2FA Pam Module is misconfigured, please contact admins!\n");
        return PAM_AUTH_ERR;
    }

    // Get User configuration
    user_cfg = get_user_config(pamh, cfg);
    if(!user_cfg) {
	pam_syslog(pamh, LOG_INFO, "Unable to get user configuration");
        // cleanup
        free_config(cfg);
	return PAM_AUTH_ERR;
    }

    const auth_mod *available_mods[4] = { NULL, NULL, NULL, NULL };
    int menu_len = 0;

    if (cfg->gauth_enabled && user_cfg->gauth_login[0] != '\0') {
#ifdef HAVE_CURL
	++menu_len;
	available_mods[menu_len] = &gauth_auth;
#else
	DBG(("GAuth configured, but CURL not compiled (should never happen!)"));
#endif
    }
    if (cfg->sms_enabled && user_cfg->sms_mobile[0] != '\0') {
	++menu_len;
	available_mods[menu_len] = &sms_auth;
    }
    if (cfg->yk_enabled && user_cfg->yk_publicids) {
#ifdef HAVE_YKCLIENT
	++menu_len;
	available_mods[menu_len] = &yk_auth;
#else
	DBG(("Yubikey configured, but ykclient not compiled (should never happen!)"));
#endif
    }

    retval = PAM_AUTH_ERR;
    for (trial = 0; trial < cfg->retry && retval != PAM_SUCCESS; ++trial) {
        const auth_mod *selected_auth_mod = NULL;
        char *user_input = NULL;
        if (menu_len > 1) {
            size_t user_input_len;
            int i = 1;

            pam_info(pamh, "Login for %s:\n", user_cfg->username);
            for (i = 1; i <= menu_len; ++i) {
                pam_info(pamh, "        %d. %s", i, available_mods[i]->name);
            }

            if (pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &user_input, "\nOption (1-%d): ", menu_len) != PAM_SUCCESS) {
        	pam_syslog(pamh, LOG_INFO, "Unable to get 2nd factors for user '%s'", user_cfg->username);
        	pam_error(pamh, "Unable to get user input");
        	retval = PAM_AUTH_ERR;
		break;
            }

            user_input_len = user_input ? strlen(user_input) : 0;
            for (i = 1; i <= menu_len; ++i) {
                if (available_mods[i]->pre_auth == NULL && available_mods[i]->otp_len) {
                    if (user_input_len == available_mods[i]->otp_len) {
                        selected_auth_mod = available_mods[i];
                        break;
                    }
                }
            }
            if (selected_auth_mod == NULL) {
                if (user_input_len == 1 && user_input[0] >= '1' && user_input[0] <= menu_len + '0') {
                    selected_auth_mod = available_mods[user_input[0] - '0'];
                    free(user_input);
                    user_input = NULL;
                } else {
                    pam_error(pamh, "Invalid input");
                    free(user_input);
                    user_input = NULL;
                }
            }
        } else if (menu_len == 1) {
            selected_auth_mod = available_mods[1];
        } else {
	    pam_syslog(pamh, LOG_INFO, "No supported 2nd factor for user '%s'", user_cfg->username);
	    pam_error(pamh, "No supported 2nd factors for user '%s'", user_cfg->username);
	    retval = PAM_AUTH_ERR;
            break;
        }
        if (selected_auth_mod != NULL) {
            void * pre_auth_data = NULL;
            if (selected_auth_mod->pre_auth != NULL) {
                 pre_auth_data = selected_auth_mod->pre_auth(pamh, user_cfg, cfg);
                 if (pre_auth_data == NULL)
                     continue;
            }
            if (user_input == NULL) {
                if (pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &user_input, "%s", selected_auth_mod->prompt) != PAM_SUCCESS) {
                    pam_syslog(pamh, LOG_INFO, "Unable to get %s", selected_auth_mod->prompt);
                    pam_error(pamh, "Unable to get user input");
                    free(pre_auth_data);
                    retval = PAM_AUTH_ERR;
                    break;
                }
            }
            retval = selected_auth_mod->do_auth(pamh, user_cfg, cfg, user_input, pre_auth_data);
            free(user_input);
        }
    }

    // final cleanup
    free_user_config(user_cfg);
    free_config(cfg);
    return retval;
}
Beispiel #16
0
static int
exec_file(pam_handle_t *pamh, char **argv, const char *logfile)
{
	pid_t pid, rc;
	int p[2];
	char buf[1024];
	long ttl;
	time_t start;
	int i, status, intr;
	fd_set rd;
	struct timeval tv;
	size_t total = 0;
	
	if (pipe(p)) {
		_pam_log(LOG_ERR, "pipe: %s", strerror(errno));
		return PAM_SYSTEM_ERR;
	}
		
	pid = fork();
	if (pid == -1) {
		close(p[0]);
		close(p[1]);
		_pam_log(LOG_ERR, "fork: %s", strerror(errno));
		return PAM_SYSTEM_ERR;
	}
	
	if (pid == 0) {		
		/* child */
		if (dup2(p[1], 1) == -1) {
			_pam_log(LOG_ERR, "dup2: %s", strerror(errno));
			_exit(127);
		}
		for (i = sysconf(_SC_OPEN_MAX); i >= 0; i--) {
			if (i != 1)
				close(i);
		}
		open("/dev/null", O_RDONLY);
		if (logfile) {
			if (open(logfile, O_CREAT|O_APPEND|O_WRONLY,
				 0644) == -1) {
				_pam_log(LOG_ERR, "open(%s): %s",
					 logfile, strerror(errno));
				_exit(127);
			}
		} else
			dup2(1, 2);
		
		execv(argv[0], argv);
		_exit(127);
	}

	/* master */
	close(p[1]);

	start = time(NULL);
	intr = 0;
	rc = 0;
	status = 0;
	for (i = 0; total < max_output_size;) {
		FD_ZERO(&rd);
		FD_SET(p[0], &rd);

		if (intr) {
			rc = waitpid(pid, &status, WNOHANG);
			if (rc == pid)
				break;
			if (rc == (pid_t)-1) {
				_pam_log(LOG_ERR, "waitpid: %s",
					 strerror(errno));
				break;
			}
			intr = 0;
		}
		ttl = timeout_option - (time(NULL) - start);
		if (ttl <= 0) {
			_pam_log(LOG_ERR, "timed out reading from %s",
				 argv[0]);
			break;
		}
		tv.tv_sec = ttl;
		tv.tv_usec = 0;
		rc = select(p[0] + 1, &rd, NULL, NULL, &tv);
		if (rc < 0) {
			if (errno == EINTR || errno == EAGAIN) {
				intr = 1;
				continue;
			}
			_pam_log(LOG_ERR, "select: %s", strerror(errno));
		}
		if (i == sizeof(buf) - 1) {
			char *p;
			buf[i] = 0;
			p = strrchr(buf, '\n');
			if (p)
				*p++ = 0;
			pam_info(pamh, "%s", buf);
			if (p && *p) {
				i = strlen(p);
				memmove(buf, p, i);
			}
		}
		if (FD_ISSET(p[0], &rd)) {
			char c;
			
			rc = read(p[0], &c, 1);
			if (rc == 1) {
				buf[i++] = c;
				total++;
			} else if (rc == 0
				   || errno == EINTR || errno == EAGAIN) {
				intr = 1;
				continue;
			} else {
				_pam_log(LOG_ERR, "read: %s", strerror(errno));
				break;
			}
		}
	}
	if (i) {
		buf[i] = 0;
		pam_info(pamh, "%s", buf);
	}
	close(p[0]);

	if (rc != pid) {
		_pam_log(LOG_NOTICE, "killing %s (pid %lu)",
			 argv[0], (unsigned long) pid);
		kill(pid, SIGKILL);
		
		while ((rc = waitpid(pid, &status, 0)) == -1 &&
		       errno == EINTR);
		if (rc == (pid_t)-1) {
			_pam_log(LOG_ERR, "waitpid: %s", strerror(errno));
			return PAM_SYSTEM_ERR;
		}
	} else if (WIFEXITED(status)) {
		status = WEXITSTATUS(status);
		if (status) {
			_pam_log(LOG_ERR, "%s exited with status %d",
				 argv[0], status);
			return PAM_SYSTEM_ERR;
		}
	} else if (WIFSIGNALED(status)) {
		status = WTERMSIG(status);
		_pam_log(LOG_ERR, "%s got signal %d", argv[0], status);
		return PAM_SYSTEM_ERR;
	} else if (status) {
		_pam_log(LOG_ERR, "%s failed: unknown status 0x%x",
			 argv[0], status);
		return PAM_SYSTEM_ERR;
	}
	return PAM_SUCCESS;
}