Skip to content
Snippets Groups Projects
Commit f1473de9 authored by ale's avatar ale
Browse files

first commit of PAM module

parents
No related branches found
No related tags found
No related merge requests found
*.in
aclocal.m4
autom4te.cache
config.guess
config.sub
configure
depcomp
install-sh
ltmain.sh
m4/lt*.m4
m4/libtool.m4
missing
*~
ACLOCAL_AMFLAGS = -I m4
pamdir = $(PAMDIR)
lib_LTLIBRARIES = libauthclient.la
pam_LTLIBRARIES = pam_authclient.la
libauthclient_la_SOURCES = \
auth_client.c auth_client.h
libauthclient_la_includedir = $(includedir)/authclient
libauthclient_la_include_HEADERS = auth_client.h
pam_authclient_la_SOURCES = \
pam_authclient.c
pam_authclient_la_LDFLAGS = -module
pam_authclient_la_LIBADD = libauthclient.la
#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <curl/curl.h>
#include "auth_client.h"
static const char *kAuthApiPath = "/api/v1/auth";
struct auth_client {
CURL *c;
const char *service;
const char *server;
};
static void auth_client_set_proto(auth_client_t ac, const char *proto) {
char url[strlen(ac->server) + 32];
sprintf(url, "%s://%s%s", proto, ac->server, kAuthApiPath);
curl_easy_setopt(ac->c, CURLOPT_URL, url);
}
auth_client_t auth_client_new(const char *service, const char *server) {
auth_client_t ac = (auth_client_t)malloc(sizeof(struct auth_client));
ac->service = service;
ac->server = server;
ac->c = curl_easy_init();
curl_easy_setopt(ac->c, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(ac->c, CURLOPT_TIMEOUT, 60);
auth_client_set_proto(ac, "http");
return ac;
}
void auth_client_set_certificate(auth_client_t ac,
const char *ca_file,
const char *crt_file,
const char *key_file) {
curl_easy_setopt(ac->c, CURLOPT_CAPATH, ca_file);
curl_easy_setopt(ac->c, CURLOPT_SSLCERTTYPE, "PEM");
curl_easy_setopt(ac->c, CURLOPT_SSLCERT, crt_file);
curl_easy_setopt(ac->c, CURLOPT_SSLKEYTYPE, "PEM");
curl_easy_setopt(ac->c, CURLOPT_SSLKEY, key_file);
curl_easy_setopt(ac->c, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(ac->c, CURLOPT_SSL_VERIFYHOST, 0);
auth_client_set_proto(ac, "https");
}
void auth_client_free(auth_client_t ac) {
curl_easy_cleanup(ac->c);
free(ac);
}
const char *auth_client_strerror(int err) {
if (err < AC_ERR_CURL_BASE) {
return curl_easy_strerror(auth_client_err_to_curl(err));
}
switch (err) {
case AC_ERR_AUTHENTICATION_FAILURE:
return "Authentication failure";
case AC_ERR_OTP_REQUIRED:
return "OTP required";
default:
return "Unknown error";
}
}
struct responsebuf {
char *buf;
int size;
};
static void responsebuf_init(struct responsebuf *rbuf) {
rbuf->buf = (char *)malloc(1);
rbuf->size = 0;
}
static void responsebuf_free(struct responsebuf *rbuf) {
free(rbuf->buf);
}
static size_t responsebuf_callback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb;
struct responsebuf *rbuf = (struct responsebuf *)userp;
rbuf->buf = (char *)realloc(rbuf->buf, rbuf->size + realsize + 1);
if (rbuf->buf == NULL) {
return 0;
}
memcpy(rbuf->buf + rbuf->size, contents, realsize);
rbuf->size += realsize;
rbuf->buf[rbuf->size] = 0;
return realsize;
}
int auth_client_authenticate(auth_client_t ac,
const char *username,
const char *password,
const char *otp_token,
const char *source_ip) {
struct curl_httppost *formpost = NULL;
struct curl_httppost *formlast = NULL;
struct responsebuf rbuf;
CURLcode res;
int retval;
// Build the POST request contents.
curl_formadd(&formpost, &formlast,
CURLFORM_COPYNAME, "service",
CURLFORM_COPYCONTENTS, ac->service,
CURLFORM_END);
curl_formadd(&formpost, &formlast,
CURLFORM_COPYNAME, "username",
CURLFORM_COPYCONTENTS, username,
CURLFORM_END);
if (password) {
curl_formadd(&formpost, &formlast,
CURLFORM_COPYNAME, "password",
CURLFORM_COPYCONTENTS, password,
CURLFORM_END);
}
if (otp_token) {
curl_formadd(&formpost, &formlast,
CURLFORM_COPYNAME, "otp",
CURLFORM_COPYCONTENTS, otp_token,
CURLFORM_END);
}
if (source_ip) {
curl_formadd(&formpost, &formlast,
CURLFORM_COPYNAME, "source_ip",
CURLFORM_COPYCONTENTS, source_ip,
CURLFORM_END);
}
curl_easy_setopt(ac->c, CURLOPT_HTTPPOST, formpost);
responsebuf_init(&rbuf);
curl_easy_setopt(ac->c, CURLOPT_WRITEFUNCTION, responsebuf_callback);
curl_easy_setopt(ac->c, CURLOPT_WRITEDATA, (void *)&rbuf);
res = curl_easy_perform(ac->c);
if (res == CURLE_OK) {
// Check the auth server response.
if (!strncmp(rbuf.buf, "OK", 2)) {
retval = AC_OK;
} else if (!strncmp(rbuf.buf, "OTP_REQUIRED", 12)) {
retval = AC_ERR_OTP_REQUIRED;
} else if (!strncmp(rbuf.buf, "ERROR", 5)) {
retval = AC_ERR_AUTHENTICATION_FAILURE;
} else {
retval = AC_ERR_BAD_RESPONSE;
}
} else {
retval = auth_client_err_from_curl(res);
}
curl_formfree(formpost);
responsebuf_free(&rbuf);
return retval;
}
#ifndef __libauthclient_authclient_h
#define __libauthclient_authclient_h 1
#include <curl/curl.h>
struct auth_client;
typedef struct auth_client* auth_client_t;
#define AC_OK 0
#define AC_ERR_AUTHENTICATION_FAILURE -1
#define AC_ERR_OTP_REQUIRED -2
#define AC_ERR_BAD_RESPONSE -3
#define AC_ERR_CURL_BASE -100
#define auth_client_err_to_curl(e) ((e)-(AC_ERR_CURL_BASE))
#define auth_client_err_from_curl(e) ((AC_ERR_CURL_BASE)-(e))
auth_client_t auth_client_new(const char *service, const char *server);
void auth_client_free(auth_client_t ac);
const char *auth_client_strerror(int err);
void auth_client_set_certificate(auth_client_t ac,
const char *ca_file,
const char *crt_file,
const char *key_file);
int auth_client_authenticate(auth_client_t ac,
const char *username,
const char *password,
const char *otp_token,
const char *source_ip);
#endif
AC_INIT([libauthclient], [0.1], [info@autistici.org])
AC_CONFIG_SRCDIR([auth_client.h])
AC_LANG(C)
AM_INIT_AUTOMAKE([foreign])
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_LIBTOOL
AC_SUBST(LIBTOOL_DEPS)
AX_LIB_CURL([7.19.14],,[
AC_MSG_ERROR([libcurl > 7.19.14 is required])
])
LIBS="$LIBS $CURL_LIBS"
CFLAGS="$CFLAGS $CURL_CFLAGS"
AX_LIB_PAM
AC_OUTPUT(
Makefile
)
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_compare_version.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
#
# DESCRIPTION
#
# This macro compares two version strings. Due to the various number of
# minor-version numbers that can exist, and the fact that string
# comparisons are not compatible with numeric comparisons, this is not
# necessarily trivial to do in a autoconf script. This macro makes doing
# these comparisons easy.
#
# The six basic comparisons are available, as well as checking equality
# limited to a certain number of minor-version levels.
#
# The operator OP determines what type of comparison to do, and can be one
# of:
#
# eq - equal (test A == B)
# ne - not equal (test A != B)
# le - less than or equal (test A <= B)
# ge - greater than or equal (test A >= B)
# lt - less than (test A < B)
# gt - greater than (test A > B)
#
# Additionally, the eq and ne operator can have a number after it to limit
# the test to that number of minor versions.
#
# eq0 - equal up to the length of the shorter version
# ne0 - not equal up to the length of the shorter version
# eqN - equal up to N sub-version levels
# neN - not equal up to N sub-version levels
#
# When the condition is true, shell commands ACTION-IF-TRUE are run,
# otherwise shell commands ACTION-IF-FALSE are run. The environment
# variable 'ax_compare_version' is always set to either 'true' or 'false'
# as well.
#
# Examples:
#
# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8])
# AX_COMPARE_VERSION([3.15],[lt],[3.15.8])
#
# would both be true.
#
# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8])
# AX_COMPARE_VERSION([3.15],[gt],[3.15.8])
#
# would both be false.
#
# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8])
#
# would be true because it is only comparing two minor versions.
#
# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15])
#
# would be true because it is only comparing the lesser number of minor
# versions of the two values.
#
# Note: The characters that separate the version numbers do not matter. An
# empty string is the same as version 0. OP is evaluated by autoconf, not
# configure, so must be a string, not a variable.
#
# The author would like to acknowledge Guido Draheim whose advice about
# the m4_case and m4_ifvaln functions make this macro only include the
# portions necessary to perform the specific comparison specified by the
# OP argument in the final configure script.
#
# LICENSE
#
# Copyright (c) 2008 Tim Toolan <toolan@ele.uri.edu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 11
dnl #########################################################################
AC_DEFUN([AX_COMPARE_VERSION], [
AC_REQUIRE([AC_PROG_AWK])
# Used to indicate true or false condition
ax_compare_version=false
# Convert the two version strings to be compared into a format that
# allows a simple string comparison. The end result is that a version
# string of the form 1.12.5-r617 will be converted to the form
# 0001001200050617. In other words, each number is zero padded to four
# digits, and non digits are removed.
AS_VAR_PUSHDEF([A],[ax_compare_version_A])
A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
-e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/[[^0-9]]//g'`
AS_VAR_PUSHDEF([B],[ax_compare_version_B])
B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
-e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
-e 's/[[^0-9]]//g'`
dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary
dnl # then the first line is used to determine if the condition is true.
dnl # The sed right after the echo is to remove any indented white space.
m4_case(m4_tolower($2),
[lt],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"`
],
[gt],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"`
],
[le],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"`
],
[ge],[
ax_compare_version=`echo "x$A
x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"`
],[
dnl Split the operator from the subversion count if present.
m4_bmatch(m4_substr($2,2),
[0],[
# A count of zero means use the length of the shorter version.
# Determine the number of characters in A and B.
ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'`
ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'`
# Set A to no more than B's length and B to no more than A's length.
A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"`
B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"`
],
[[0-9]+],[
# A count greater than zero means use only that many subversions
A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
],
[.+],[
AC_WARNING(
[illegal OP numeric parameter: $2])
],[])
# Pad zeros at end of numbers to make same length.
ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`"
B="$B`echo $A | sed 's/./0/g'`"
A="$ax_compare_version_tmp_A"
# Check for equality or inequality as necessary.
m4_case(m4_tolower(m4_substr($2,0,2)),
[eq],[
test "x$A" = "x$B" && ax_compare_version=true
],
[ne],[
test "x$A" != "x$B" && ax_compare_version=true
],[
AC_WARNING([illegal OP parameter: $2])
])
])
AS_VAR_POPDEF([A])dnl
AS_VAR_POPDEF([B])dnl
dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE.
if test "$ax_compare_version" = "true" ; then
m4_ifvaln([$4],[$4],[:])dnl
m4_ifvaln([$5],[else $5])dnl
fi
]) dnl AX_COMPARE_VERSION
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_lib_curl.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_LIB_CURL([VERSION],[ACTION-IF-SUCCESS],[ACTION-IF-FAILURE])
#
# DESCRIPTION
#
# Checks for minimum curl library version VERSION. If successfull executes
# ACTION-IF-SUCCESS otherwise ACTION-IF-FAILURE.
#
# Defines CURL_LIBS and CURL_CFLAGS.
#
# A simple example:
#
# AX_LIB_CURL([7.19.4],,[
# AC_MSG_ERROR([Your system lacks of libcurl >= 7.19.4])
# ])
#
# This macro is a rearranged version of AC_LIB_CURL from Akos Maroy.
#
# LICENSE
#
# Copyright (c) 2009 Francesco Salvestrini <salvestrini@users.sourceforge.net>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 7
AU_ALIAS([AC_CHECK_CURL], [AX_LIB_CURL])
AC_DEFUN([AX_LIB_CURL], [
AX_PATH_GENERIC([curl],[$1],'s/^libcurl\ \+//',[$2],[$3])
])
# AX_LIB_PAM()
AC_DEFUN([AX_LIB_PAM], [
AC_SEARCH_LIBS([pam_set_data], [pam])
AC_CHECK_FUNCS([pam_getenv pam_getenvlist pam_modutil_getpwnam])
dnl AC_REPLACE_FUNCS([pam_syslog pam_vsyslog])
AC_CHECK_HEADERS([security/pam_modutil.h], [],
[AC_CHECK_HEADERS([pam/pam_modutil.h])])
AC_CHECK_HEADERS([security/pam_appl.h], [],
[AC_CHECK_HEADERS([pam/pam_appl.h], [],
[AC_MSG_ERROR([No PAM header files found])])])
AC_CHECK_HEADERS([security/pam_ext.h], [],
[AC_CHECK_HEADERS([pam/pam_ext.h])])
RRA_HEADER_PAM_CONST
AC_SUBST(PAMDIR, "/lib/security")
AC_ARG_WITH(pam-dir,
AC_HELP_STRING([--with-pam-dir=DIR],
[Where to install PAM module [[/lib/security]]]),
[case "${withval}" in
/*) PAMDIR="${withval}";;
./*|../*) AC_MSG_ERROR(Bad value for --with-pam-dir);;
*) PAMDIR="/lib/${withval}";;
esac])
AC_MSG_NOTICE([PAM installation path $PAMDIR])
])
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_path_generic.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PATH_GENERIC(LIBRARY,[MINIMUM-VERSION,[SED-EXPR-EXTRACTOR]],[ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND],[CONFIG-SCRIPTS],[CFLAGS-ARG],[LIBS-ARG])
#
# DESCRIPTION
#
# Runs the LIBRARY-config script and defines LIBRARY_CFLAGS and
# LIBRARY_LIBS unless the user had predefined them in the environment.
#
# The script must support `--cflags' and `--libs' args. If MINIMUM-VERSION
# is specified, the script must also support the `--version' arg. If the
# `--with-library-[exec-]prefix' arguments to ./configure are given, it
# must also support `--prefix' and `--exec-prefix'. Prefereable use
# CONFIG-SCRIPTS as config script, CFLAGS-ARG instead of `--cflags` and
# LIBS-ARG instead of `--libs`, if given.
#
# The SED-EXPR-EXTRACTOR parameter representes the expression used in sed
# to extract the version number. Use it if your 'foo-config --version'
# dumps something like 'Foo library v1.0.0 (alfa)' instead of '1.0.0'.
#
# The macro respects LIBRARY_CONFIG, LIBRARY_CFLAGS and LIBRARY_LIBS
# variables. If the first one is defined, it specifies the name of the
# config script to use. If the latter two are defined, the script is not
# ran at all and their values are used instead (if only one of them is
# defined, the empty value of the remaining one is still used).
#
# Example:
#
# AX_PATH_GENERIC(Foo, 1.0.0)
#
# would run `foo-config --version' and check that it is at least 1.0.0, if
# successful the following variables would be defined and substituted:
#
# FOO_CFLAGS to `foo-config --cflags`
# FOO_LIBS to `foo-config --libs`
#
# Example:
#
# AX_PATH_GENERIC([Bar],,,[
# AC_MSG_ERROR([Cannot find Bar library])
# ])
#
# would check for bar-config program, defining and substituting the
# following variables:
#
# BAR_CFLAGS to `bar-config --cflags`
# BAR_LIBS to `bar-config --libs`
#
# Example:
#
# ./configure BAZ_LIBS=/usr/lib/libbaz.a
#
# would link with a static version of baz library even if `baz-config
# --libs` returns just "-lbaz" that would normally result in using the
# shared library.
#
# This macro is a rearranged version of AC_PATH_GENERIC from Angus Lees.
#
# LICENSE
#
# Copyright (c) 2009 Francesco Salvestrini <salvestrini@users.sourceforge.net>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 11
AU_ALIAS([AC_PATH_GENERIC], [AX_PATH_GENERIC])
AC_DEFUN([AX_PATH_GENERIC],[
AC_REQUIRE([AC_PROG_SED])
dnl we're going to need uppercase and lowercase versions of the
dnl string `LIBRARY'
pushdef([UP], translit([$1], [a-z], [A-Z]))dnl
pushdef([DOWN], translit([$1], [A-Z], [a-z]))dnl
AC_ARG_WITH(DOWN-prefix,[AS_HELP_STRING([--with-]DOWN[-prefix=PREFIX], [Prefix where $1 is installed (optional)])],
DOWN[]_config_prefix="$withval", DOWN[]_config_prefix="")
AC_ARG_WITH(DOWN-exec-prefix,[AS_HELP_STRING([--with-]DOWN[-exec-prefix=EPREFIX], [Exec prefix where $1 is installed (optional)])],
DOWN[]_config_exec_prefix="$withval", DOWN[]_config_exec_prefix="")
AC_ARG_VAR(UP[]_CONFIG, [config script used for $1])
AC_ARG_VAR(UP[]_CFLAGS, [CFLAGS used for $1])
AC_ARG_VAR(UP[]_LIBS, [LIBS used for $1])
AS_IF([test x$UP[]_CFLAGS != x -o x$UP[]_LIBS != x],[
dnl Don't run config script at all, use user-provided values instead.
AC_SUBST(UP[]_CFLAGS)
AC_SUBST(UP[]_LIBS)
:
$4
],[
AS_IF([test x$DOWN[]_config_exec_prefix != x],[
DOWN[]_config_args="$DOWN[]_config_args --exec-prefix=$DOWN[]_config_exec_prefix"
AS_IF([test x${UP[]_CONFIG+set} != xset],[
UP[]_CONFIG=$DOWN[]_config_exec_prefix/bin/DOWN-config
])
])
AS_IF([test x$DOWN[]_config_prefix != x],[
DOWN[]_config_args="$DOWN[]_config_args --prefix=$DOWN[]_config_prefix"
AS_IF([test x${UP[]_CONFIG+set} != xset],[
UP[]_CONFIG=$DOWN[]_config_prefix/bin/DOWN-config
])
])
AC_PATH_PROGS(UP[]_CONFIG,[$6 DOWN-config],[no])
AS_IF([test "$UP[]_CONFIG" == "no"],[
:
$5
],[
dnl Get the CFLAGS from LIBRARY-config script
AS_IF([test x"$7" == x],[
UP[]_CFLAGS="`$UP[]_CONFIG $DOWN[]_config_args --cflags`"
],[
UP[]_CFLAGS="`$UP[]_CONFIG $DOWN[]_config_args $7`"
])
dnl Get the LIBS from LIBRARY-config script
AS_IF([test x"$8" == x],[
UP[]_LIBS="`$UP[]_CONFIG $DOWN[]_config_args --libs`"
],[
UP[]_LIBS="`$UP[]_CONFIG $DOWN[]_config_args $8`"
])
AS_IF([test x"$2" != x],[
dnl Check for provided library version
AS_IF([test x"$3" != x],[
dnl Use provided sed expression
DOWN[]_version="`$UP[]_CONFIG $DOWN[]_config_args --version | $SED -e $3`"
],[
DOWN[]_version="`$UP[]_CONFIG $DOWN[]_config_args --version | $SED -e 's/^\ *\(.*\)\ *$/\1/'`"
])
AC_MSG_CHECKING([for $1 ($DOWN[]_version) >= $2])
AX_COMPARE_VERSION($DOWN[]_version,[ge],[$2],[
AC_MSG_RESULT([yes])
AC_SUBST(UP[]_CFLAGS)
AC_SUBST(UP[]_LIBS)
:
$4
],[
AC_MSG_RESULT([no])
:
$5
])
],[
AC_SUBST(UP[]_CFLAGS)
AC_SUBST(UP[]_LIBS)
:
$4
])
])
])
popdef([UP])
popdef([DOWN])
])
dnl Determine whether PAM uses const in prototypes.
dnl
dnl Linux marks several PAM arguments const, including the argument to
dnl pam_get_item and some arguments to conversation functions, which Solaris
dnl doesn't. This test tries to determine which style is in use to select
dnl whether to declare variables const in order to avoid compiler warnings.
dnl
dnl Since this is just for compiler warnings, it's not horribly important if
dnl we guess wrong. This test is ugly, but it seems to work.
dnl
dnl Contributed by Markus Moeller.
dnl
dnl The canonical version of this file is maintained in the rra-c-util
dnl package, available at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
dnl
dnl Copyright 2007 Russ Allbery <rra@stanford.edu>
dnl Copyright 2007, 2008 Markus Moeller
dnl
dnl This file is free software; the authors give unlimited permission to copy
dnl and/or distribute it, with or without modifications, as long as this
dnl notice is preserved.
dnl Source used by RRA_HEADER_PAM_CONST.
AC_DEFUN([_RRA_HEADER_PAM_CONST_SOURCE],
[#ifdef HAVE_SECURITY_PAM_APPL_H
# include <security/pam_appl.h>
#else
# include <pam/pam_appl.h>
#endif
])
AC_DEFUN([RRA_HEADER_PAM_CONST],
[AC_CACHE_CHECK([whether PAM prefers const], [rra_cv_header_pam_const],
[AC_EGREP_CPP([const void \*\* *_?item], _RRA_HEADER_PAM_CONST_SOURCE(),
[rra_cv_header_pam_const=yes], [rra_cv_header_pam_const=no])])
AS_IF([test x"$rra_cv_header_pam_const" = xyes],
[rra_header_pam_const=const], [rra_header_pam_const=])
AC_DEFINE_UNQUOTED([PAM_CONST], [$rra_header_pam_const],
[Define to const if PAM uses const in pam_get_item, empty otherwise.])])
#include "config.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include "auth_client.h"
/* These #defines must be present according to PAM documentation. */
#define PAM_SM_AUTH
#ifdef HAVE_SECURITY_PAM_APPL_H
#include <security/pam_appl.h>
#endif
#ifdef HAVE_SECURITY_PAM_MODULES_H
#include <security/pam_modules.h>
#endif
#ifdef HAVE_PAM_PAM_APPL_H
#include <pam/pam_appl.h>
#endif
#ifdef HAVE_PAM_PAM_MODULES_H
#include <pam/pam_modules.h>
#endif
#ifndef PAM_EXTERN
#ifdef PAM_STATIC
#define PAM_EXTERN static
#else
#define PAM_EXTERN extern
#endif
#endif
static const char *kPasswordPrompt = "Password: ";
static const char *kOtpPrompt = "OTP Token: ";
struct cfg {
int debug;
int use_first_pass;
int try_first_pass;
char *auth_server;
char *ssl_crt;
char *ssl_key;
char *ca_file;
};
static void parse_cfg(int argc, const char **argv, struct cfg *cfg) {
int i;
memset(cfg, 0, sizeof(struct cfg));
for (i = 0; i < argc; i++) {
if (!strcmp(argv[i], "debug")) {
cfg->debug = 1;
} else if (!strcmp(argv[i], "try_first_pass")) {
cfg->try_first_pass = 1;
} else if (!strcmp(argv[i], "use_first_pass")) {
cfg->use_first_pass = 1;
} else if (!strncmp(argv[i], "auth_server=", 12)) {
cfg->auth_server = (char *)(argv[i] + 12);
} else if (!strncmp(argv[i], "ssl_crt=", 8)) {
cfg->ssl_crt = (char *)(argv[i] + 8);
} else if (!strncmp(argv[i], "ssl_key=", 8)) {
cfg->ssl_key = (char *)(argv[i] + 8);
} else if (!strncmp(argv[i], "ca=", 3)) {
cfg->ca_file = (char *)(argv[i] + 3);
}
}
}
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,
int flags, int argc, const char **argv) {
int i, retval, err;
const char *service = NULL;
const char *username = NULL;
const char *password = NULL;
const char *otp_token = NULL;
const char *source_ip = NULL;
struct pam_conv *conv = NULL;
struct cfg cfg;
auth_client_t ac;
parse_cfg(argc, argv, &cfg);
err = pam_get_user(pamh, &username, NULL);
if (err != PAM_SUCCESS) {
D(("pam_get_user() error: %s", pam_strerror(pamh, err)));
return PAM_AUTH_ERR;
}
err = pam_get_item(pamh, PAM_SERVICE, (PAM_CONST void **)&service);
if (err != PAM_SUCCESS) {
D(("pam_get_item(service) error: %s", pam_strerror(pamh, err)));
return PAM_AUTH_ERR;
}
pam_get_item(pamh, PAM_RHOST, (PAM_CONST void **)&source_ip);
if (cfg.try_first_pass || cfg.use_first_pass) {
if (pam_get_item(pamh, PAM_AUTHTOK, (PAM_CONST void **)&password) != PAM_SUCCESS) {
return PAM_AUTH_ERR;
}
}
if (cfg.use_first_pass && password == NULL) {
return PAM_AUTH_ERR;
}
if (password == NULL) {
// Ask for the password interactively.
struct pam_message *pmsg[1], msg[1];
struct pam_response *resp;
err = pam_get_item(pamh, PAM_CONV, (PAM_CONST void **)&conv);
if (err != PAM_SUCCESS) {
D(("pam_get_item(conv) error: %s", pam_strerror(pamh, err)));
return PAM_AUTH_ERR;
}
pmsg[0] = &msg[0];
msg[0].msg = (char *)kPasswordPrompt;
msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
err = conv->conv(1, (const struct pam_message **)pmsg,
&resp, conv->appdata_ptr);
if (err != PAM_SUCCESS) {
D(("conv() error: %s", pam_strerror(pamh, err)));
return PAM_AUTH_ERR;
}
password = resp->resp;
}
// Create the auth client request.
ac = auth_client_new(service, cfg.auth_server);
if (cfg.ssl_crt && cfg.ssl_key && cfg.ca_file) {
auth_client_set_certificate(ac, cfg.ca_file, cfg.ssl_crt, cfg.ssl_key);
}
retval = PAM_AUTH_ERR;
// Allow two authentication attempts in case we receive an
// OTP_REQUIRED response from the server.
for (i = 0; i < 2; i++) {
int ac_err = auth_client_authenticate(ac, username, password, otp_token, source_ip);
if (ac_err == AC_OK) {
retval = PAM_SUCCESS;
} else if (ac_err == AC_ERR_OTP_REQUIRED) {
struct pam_message *pmsg[1], msg[1];
struct pam_response *resp;
// Ask for the OTP token interactively.
if (conv == NULL) {
err = pam_get_item(pamh, PAM_CONV, (PAM_CONST void **)&conv);
if (err != PAM_SUCCESS) {
D(("pam_get_item(conv) error: %s", pam_strerror(pamh, err)));
break;
}
}
pmsg[0] = &msg[0];
msg[0].msg = (char *)kOtpPrompt;
msg[0].msg_style = PAM_PROMPT_ECHO_ON;
err = conv->conv(1, (const struct pam_message **)pmsg,
&resp, conv->appdata_ptr);
if (err != PAM_SUCCESS) {
D(("conv() error: %s", pam_strerror(pamh, err)));
break;
}
otp_token = resp->resp;
continue;
} else {
D(("auth_client error: %s", auth_client_strerror(ac_err)));
}
break;
}
auth_client_free(ac);
return retval;
}
PAM_EXTERN int pam_sm_setcred(pam_handle_t *pam, int flags, int argc, const char **argv) {
return PAM_SUCCESS;
}
#ifdef PAM_STATIC
struct pam_module _pam_authclient_modstruct = {
"pam_authclient",
pam_sm_authenticate,
pam_sm_setcred,
NULL,
NULL,
NULL,
NULL
};
#endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment