Skip to content

Commit

Permalink
Merge branch 'pr-221'
Browse files Browse the repository at this point in the history
  • Loading branch information
klali committed Jan 12, 2021
2 parents d4ed13d + b708e6d commit d468b61
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Expand Up @@ -32,7 +32,7 @@ jobs:
run: |
sudo apt update
sudo apt install -y libykclient-dev libykpers-1-dev libyubikey-dev \
libpam-dev help2man asciidoc-base
libpam-dev help2man asciidoc-base libmysqlclient-dev
autoreconf --install
./configure
make
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scan.yml
Expand Up @@ -8,7 +8,7 @@ on:
env:
SCAN_IMG:
yubico-yes-docker-local.jfrog.io/static-code-analysis/c:v1
COMPILE_DEPS: "libykclient-dev libykpers-1-dev libyubikey-dev"
COMPILE_DEPS: "libykclient-dev libykpers-1-dev libyubikey-dev libmysqlclient-dev"

jobs:
build:
Expand Down
14 changes: 7 additions & 7 deletions .travis.yml
@@ -1,4 +1,3 @@
sudo: required
language: c
os:
- linux
Expand All @@ -7,13 +6,14 @@ compiler:
- gcc
- clang
env:
- CONFIGURE_ARGS="" EXTRA="libldap2-dev libykpers-1-dev libnet-ldap-server-perl"
- CONFIGURE_ARGS="--without-ldap" EXTRA="libykpers-1-dev"
- CONFIGURE_ARGS="--without-cr" EXTRA="libldap2-dev libnet-ldap-server-perl"
- CONFIGURE_ARGS="--without-ldap --without-cr"
- CONFIGURE_ARGS="" EXTRA="libldap2-dev libykpers-1-dev libnet-ldap-server-perl libmysqlclient-dev"
- CONFIGURE_ARGS="--without-ldap" EXTRA="libykpers-1-dev libmysqlclient-dev"
- CONFIGURE_ARGS="--without-cr" EXTRA="libldap2-dev libnet-ldap-server-perl libmysqlclient-dev"
- CONFIGURE_ARGS="--without-ldap --without-cr" EXTRA="libmysqlclient-dev"
script: tests/aux/build-and-test.sh
matrix:
jobs:
install: travis_wait 30 mvn install
include:
- compiler: gcc
os: linux
env: COVERAGE="--enable-coverage" EXTRA="libldap2-dev libykpers-1-dev libnet-ldap-server-perl lcov"
env: COVERAGE="--enable-coverage" EXTRA="libldap2-dev libykpers-1-dev libnet-ldap-server-perl lcov libmysqlclient-dev"
7 changes: 6 additions & 1 deletion Makefile.am
Expand Up @@ -45,7 +45,12 @@ pam_yubico_la_LDFLAGS = -module -avoid-version

noinst_LTLIBRARIES = libpam_util.la libpam_real.la
libpam_util_la_SOURCES = util.c util.h
libpam_util_la_LIBADD = @LTLIBYUBIKEY@ @YKPERS_LIBS@
libpam_util_la_LIBADD = @LTLIBYUBIKEY@ @YKPERS_LIBS@

# if MYSQL_SUPPORT
AM_CFLAGS += @MYSQL_CFLAGS@
libpam_util_la_LIBADD += @MYSQL_LIBS@
# endif

libpam_real_la_SOURCES = pam_yubico.c

Expand Down
13 changes: 13 additions & 0 deletions configure.ac
Expand Up @@ -75,6 +75,19 @@ AC_ARG_WITH([ldap],
[libldap not found, will not be compiled (--without-ldap to disable ldap support)])],
[])])

AC_ARG_WITH([mysql],
[AS_HELP_STRING([--without-mysql],
[disable support for mysql])],
[],
[with_mysql=yes])
AS_IF([test "x$with_mysql" != xno],
[
PKG_CHECK_MODULES([MYSQL], [mysqlclient],
[AC_DEFINE([HAVE_MYSQL], [1],[Define if you have mysqlclient])],
[AC_MSG_WARN(
[libmysqlclient not found, will not be compiled (--without-mysql to disable mysql support)])])
])
AM_CONDITIONAL(MYSQL_SUPPORT,test "x$with_mysql" != xno)

AC_LIB_HAVE_LINKFLAGS([ykclient],, [#include <ykclient.h>],
[ykclient_set_proxy(0, 0)])
Expand Down
12 changes: 12 additions & 0 deletions pam_yubico.8.txt
Expand Up @@ -116,6 +116,18 @@ CA certitificate file for the LDAP connection.
*chalresp_path*=_path_::
Path of a system-wide directory where challenge-response files can be found for users. Default location is `$HOME/.yubico/`.

*mysql_server*=_mysqlserver_::
Hostname/Adress of mysql server. Example 10.0.0.1

*mysql_user*=_mysqluser_::
User for accessing to the database. Strongly recommended to use a specific user with read only access.

*mysql_password*=_mysqlpassword_::
Mysql password associated to the user.

*mysql_database*=_mysqldatabase_::
the name of the database. Example : otp

== EXAMPLES

auth sufficient pam_yubico.so id=16 debug
Expand Down
32 changes: 30 additions & 2 deletions pam_yubico.c
Expand Up @@ -134,6 +134,11 @@ struct cfg
const char *user_attr;
const char *yubi_attr;
const char *yubi_attr_prefix;
const char *mysql_server;
const char *mysql_user;
const char *mysql_password;
const char *mysql_database;

unsigned int token_id_length;
enum key_mode mode;
const char *chalresp_path;
Expand Down Expand Up @@ -164,8 +169,19 @@ authorize_user_token (struct cfg *cfg,
pam_handle_t *pamh)
{
int retval = AUTH_ERROR;

if (cfg->auth_file)
if (cfg->mysql_server)
{
#ifdef HAVE_MYSQL
/* Administrator had configured the database and specified is name
as an argument for this module.
*/
DBG ("Using Mariadb or Mysql Database");
retval = check_user_token_mysql(cfg->mysql_server, cfg->mysql_user, cfg->mysql_password, cfg->mysql_database, username, otp_id, cfg->debug, cfg->debug_file);
#else
DBG (("Trying to use MYSQL, but this function is not compiled in pam_yubico!!"));
#endif
}
else if (cfg->auth_file)
{
/* Administrator had configured the file and specified is name
as an argument for this module.
Expand Down Expand Up @@ -874,6 +890,15 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg)
cfg->mode = CLIENT;
if (strncmp (argv[i], "chalresp_path=", 14) == 0)
cfg->chalresp_path = argv[i] + 14;
if (strncmp (argv[i], "mysql_server=", 13) == 0)
cfg->mysql_server = argv[i] + 13;
if (strncmp (argv[i], "mysql_user=", 11) == 0)
cfg->mysql_user = argv[i] + 11;
if (strncmp (argv[i], "mysql_password=", 15) == 0)
cfg->mysql_password = argv[i] + 15;
if (strncmp (argv[i], "mysql_database=", 15) == 0)
cfg->mysql_database = argv[i] + 15;

if (strncmp (argv[i], "debug_file=", 11) == 0)
{
const char *filename = argv[i] + 11;
Expand Down Expand Up @@ -939,6 +964,9 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg)
DBG ("token_id_length=%u", cfg->token_id_length);
DBG ("mode=%s", cfg->mode == CLIENT ? "client" : "chresp" );
DBG ("chalresp_path=%s", cfg->chalresp_path ? cfg->chalresp_path : "(null)");
DBG ("mysql_server=%s", cfg->mysql_server ? cfg->mysql_server : "(null)");
DBG ("mysql_user=%s", cfg->mysql_user ? cfg->mysql_user : "(null)");
DBG ("mysql_database=%s", cfg->mysql_database ? cfg->mysql_database : "(null)");

if (fd != -1)
close(fd);
Expand Down
3 changes: 2 additions & 1 deletion tests/aux/build-and-test.sh
Expand Up @@ -7,7 +7,7 @@ autoreconf -i
if [ "x$TRAVIS_OS_NAME" != "xosx" ]; then
sudo add-apt-repository -y ppa:yubico/stable
sudo apt-get update -qq || true
sudo apt-get install -qq -y --no-install-recommends libykclient-dev libpam0g-dev libyubikey-dev asciidoc docbook-xsl xsltproc libxml2-utils $EXTRA
sudo apt-get install -qq -y --no-install-recommends libykclient-dev libpam0g-dev libyubikey-dev asciidoc docbook-xsl xsltproc libxml2-utils libmysqlclient-dev $EXTRA
else
brew update
brew install pkg-config
Expand All @@ -17,6 +17,7 @@ else
brew install libyubikey
brew install ykclient
brew install ykpers
brew install mysql-connector-c #Mysql
cpanp install Net::LDAP::Server

# this is required so asciidoc can find the xml catalog
Expand Down
193 changes: 193 additions & 0 deletions util.c
Expand Up @@ -52,6 +52,10 @@
#include <ykdef.h>
#endif /* HAVE_CR */

#ifdef HAVE_MYSQL
#include <mysql.h>
#endif

int
get_user_cfgfile_path(const char *common_path, const char *filename, const struct passwd *user, char **fn)
{
Expand Down Expand Up @@ -93,6 +97,195 @@ get_user_cfgfile_path(const char *common_path, const char *filename, const struc
return 1;
}

#ifdef HAVE_MYSQL
/*
* This function will look for users name with valid user token id, in a database Mysql
*
* Returns one of AUTH_FOUND, AUTH_NOT_FOUND, AUTH_NO_TOKENS, AUTH_ERROR.
*
* Need database with this table structure:
*
* CREATE TABLE IF NOT EXISTS `otp`.`yubikey_mappings`(
* `otp_id` VARCHAR(12) NOT NULL ,
* `username` VARCHAR(64) NOT NULL ,
* PRIMARY KEY (`otp_id`(12))
* );
*
*/
int
check_user_token_mysql(const char *mysql_server,
const char *mysql_user,
const char *mysql_password,
const char *mysql_database,
const char *username,
const char *otp_id,
int verbose,
FILE *debug_file)
{

int retval = AUTH_ERROR;
int fd;
struct stat st;
FILE *opwfile;
MYSQL *con = NULL;
MYSQL_STMT *stmt;
MYSQL_BIND ps_params[2];
MYSQL_BIND bind[1];
long unsigned int str_username;
long unsigned int str_otp;
long unsigned int length;
int int_data;
int row_count;
bool is_null;
bool error;

if(mysql_library_init(0, NULL, NULL)){
if(verbose){
D (debug_file, "could not initialize MySQL client library");
}

return retval;
}

con = mysql_init(con);
if(!con) {
if(verbose)
D (debug_file, "out of memorys");
return retval;
}

if(mysql_real_connect(con, mysql_server,mysql_user,mysql_password,mysql_database, 0, NULL, 0) == NULL)
{
if(verbose)
D (debug_file, "Connection failed ...");
return retval;
}

stmt = mysql_stmt_init(con);
if(!stmt)
{
if(verbose)
D (debug_file, "Connection failed ... 2");
return retval;
}

const char *sql = "SELECT count(username) FROM yubikey_mappings WHERE username = ?;";
const char *sql2 = "SELECT count(username) FROM yubikey_mappings WHERE username = ? and otp_id = ?;";

if(otp_id == NULL)
{
if(mysql_stmt_prepare(stmt, sql, strlen(sql)))
{
if(verbose)
D (debug_file, "mysql_stmt_prepare() failed %s", mysql_stmt_error(stmt));
return retval;
}
}else{
if(mysql_stmt_prepare(stmt, sql2, strlen(sql2)))
{
if(verbose)
D (debug_file, "mysql_stmt_prepare() failed %s", mysql_stmt_error(stmt));
return retval;
}
}

str_username = strlen(username);
memset(ps_params, 0, sizeof(ps_params));
ps_params[0].buffer_type = MYSQL_TYPE_STRING;
ps_params[0].buffer = (char *)username;
ps_params[0].buffer_length = str_username;
ps_params[0].length = &str_username;
ps_params[0].is_null = 0;

if(otp_id != NULL)
{
str_otp= strlen(otp_id);
ps_params[1].buffer_type = MYSQL_TYPE_STRING;
ps_params[1].buffer = (char *)otp_id;
ps_params[1].buffer_length = str_otp;
ps_params[1].length = &str_otp;
ps_params[1].is_null = 0;
}

if(mysql_stmt_bind_param(stmt, ps_params))
{
if(verbose)
D (debug_file, "mysql_stmt_bind_param() failed %s", mysql_stmt_error(stmt));
return retval;
}

if(mysql_stmt_execute(stmt))
{
if(verbose)
D (debug_file, "mysql_stmt_execute() failed %s", mysql_stmt_error(stmt));
return retval;
}

memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = (char *)&int_data;
bind[0].length = &length;
bind[0].is_null = &is_null;
bind[0].error = &error;

if(mysql_stmt_bind_result(stmt, bind))
{
if(verbose)
D (debug_file, "mysql_stmt_bind_result() failed %s", mysql_stmt_error(stmt));
}

if(mysql_stmt_store_result(stmt))
{
if(verbose)
D (debug_file, "mysql_stmt_store_result() failed %s", mysql_stmt_error(stmt));
return retval;
}

while(!mysql_stmt_fetch(stmt))
{
if(is_null)
{
D (debug_file, "mysql_stmt_fetch() failed");
}
else
{
if(otp_id != NULL){
if(int_data)
{
return AUTH_FOUND;
}
else
{
return AUTH_NOT_FOUND;
}
}
else if(otp_id == NULL)
{
if(int_data)
{
return AUTH_NOT_FOUND;
}
else
{
return AUTH_NO_TOKENS;
}
}
}
}

if(mysql_stmt_close(stmt))
{
if(verbose)
D (debug_file, "mysql_stmt_close() failed %s", mysql_stmt_error(stmt));
return retval;
}

mysql_close(con);
mysql_library_end();

return retval;
}
#endif

/*
* This function will look for users name with valid user token id.
Expand Down
3 changes: 3 additions & 0 deletions util.h
Expand Up @@ -51,6 +51,9 @@
#define AUTH_NOT_FOUND -1 /* The requested token is not associated to the user */

int get_user_cfgfile_path(const char *common_path, const char *filename, const struct passwd *user, char **fn);
#ifdef HAVE_MYSQL
int check_user_token_mysql(const char *mysql_server,const char *mysql_user,const char *mysql_password,const char *mysql_database,const char *username,const char *otp_id,int verbose,FILE *debug_file);
#endif
int check_user_token(const char *authfile, const char *username, const char *otp_id, int verbose, FILE *debug_file);

#if HAVE_CR
Expand Down

0 comments on commit d468b61

Please sign in to comment.