Use wordsplit for keyword expansion.

* .gitmodules: Add wordsplit
* configure.ac: Likewise.
* lib/Makefile.am: Likewise.
* lib/graypam.h (gray_expand_argv): Remove.
(gray_expand_string): Change prototype.
* lib/vartab.c (gray_expand_argv): Remove.
(gray_expand_string): Rewrite using wordsplit.
* pam_ldaphome/pam_ldaphome.c (import_public_key): Assume
sshPublicKey as a default attribute.
* pam_log/pam_log.c (_pam_parse): Take two return arguments.
(echo): Use gray_expand_string.
* pam_sql/pam_mysql.c: Update gray_expand_string usage.
* pam_sql/pam_pgsql.c: Likewise.
* pam_sql/pam_sql.c: Likewise.
* pam_sql/pam_sql.h (gpam_sql_get_query): Change signature.
* pam_umotd/pam_umotd.c (pam_sm_open_session): Update
gray_expand_string usage.
This commit is contained in:
Sergey Poznyakoff 2020-03-29 17:39:54 +03:00
parent 5d4abc6e44
commit b6c720096e
13 changed files with 167 additions and 291 deletions

3
.gitmodules vendored
View file

@ -1,3 +1,6 @@
[submodule "imprimatur"]
path = imprimatur
url = git://git.gnu.org.ua/imprimatur.git
[submodule "lib/wordsplit"]
path = lib/wordsplit
url = git://git.gnu.org.ua/wordsplit.git

View file

@ -20,7 +20,7 @@ AC_INIT(pam-modules, 2.3.90, bug-pam-modules@gnu.org.ua)
AC_CONFIG_SRCDIR(pam_fshadow/pam_fshadow.c)
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([1.11 no-exeext tar-ustar dist-xz silent-rules])
AM_INIT_AUTOMAKE([1.11 no-exeext tar-ustar dist-xz silent-rules subdir-objects])
AM_CONFIG_HEADER(config.h)
# Enable silent rules by default:

View file

@ -1,5 +1,5 @@
# This file is part of pam-modules.
# Copyright (C) 2008, 2010-2012, 2014-2015, 2018 Sergey Poznyakoff
# Copyright (C) 2008-2020 Sergey Poznyakoff
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -32,6 +32,9 @@ libgraypam_la_SOURCES = \
sha1.h\
strutil.c\
vartab.c\
escape.c
escape.c\
wordsplit/wordsplit.c
noinst_HEADERS = graypam.h
noinst_HEADERS = graypam.h wordsplit/wordsplit.h
AM_CPPFLAGS = -I$(srcdir)/wordsplit

View file

@ -177,10 +177,7 @@ ssize_t gray_base64_decode(gray_slist_t slist, const char *iptr, size_t isize);
int gray_check_ldap_pass (const char *db_pass, const char *pass);
void gray_expand_argv(pam_handle_t *pamh, int argc, const char **argv,
gray_slist_t slist);
void gray_expand_string(pam_handle_t *pamh, const char *str,
gray_slist_t slist);
int gray_expand_string(pam_handle_t *pamh, const char *str, char **output);
void gray_escape_string(gray_slist_t slist, const char *str, size_t len);
struct keyword {

View file

@ -1,5 +1,5 @@
/* This file is part of pam-modules.
Copyright (C) 2009-2012, 2014-2015, 2018 Sergey Poznyakoff
Copyright (C) 2009-2020 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@ -15,6 +15,7 @@
with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <graypam.h>
#include "wordsplit/wordsplit.h"
struct keyword *
gray_find_keyword(struct keyword *kwtab, const char *str, size_t len)
@ -25,177 +26,77 @@ gray_find_keyword(struct keyword *kwtab, const char *str, size_t len)
return kwtab;
return NULL;
}
static struct keyword vartab[] = {
DCL("service", PAM_SERVICE),
DCL("user", PAM_USER),
DCL("tty", PAM_TTY),
DCL("rhost", PAM_RHOST),
DCL("ruser", PAM_RUSER),
DCL("prompt", PAM_USER_PROMPT),
DCL("password", PAM_AUTHTOK),
{ NULL }
DCL("service", PAM_SERVICE),
DCL("user", PAM_USER),
DCL("tty", PAM_TTY),
DCL("rhost", PAM_RHOST),
DCL("ruser", PAM_RUSER),
DCL("prompt", PAM_USER_PROMPT),
DCL("password", PAM_AUTHTOK),
{ NULL }
};
static int
var_tok(const char *str, const char ** pvar, size_t *plen)
get_pam_var(char **ret, const char *var, size_t len, void *clos)
{
size_t len;
for (len = 0; str[len]; len++) {
if (str[len] == '}' || str[len] == ':') {
*pvar = str;
*plen = len;
return 0;
}
}
return 1;
}
static int
repl_tok(const char *str, const char ** pret, size_t *plen)
{
size_t len;
for (len = 0; str[len]; len++) {
if (str[len] == '}') {
*pret = str;
*plen = len;
return 0;
}
}
return 1;
}
#define ISKW(c) ((c) && (isalnum(c) || (c) == '_'))
static int
get_variable(pam_handle_t *pamh, const char *str, gray_slist_t slist,
const char **endp)
{
const char *name;
size_t namelen;
const char *repl = NULL;
size_t repllen = 0;
const char *val;
size_t vallen;
pam_handle_t *pamh = clos;
struct keyword *kw;
const char *end;
char const *val;
int rc;
char *s;
kw = gray_find_keyword(vartab, var, len);
if (!kw)
return WRDSE_UNDEF;
rc = pam_get_item(pamh, kw->code, (const void**) &val);
if (rc) {
_pam_log(LOG_ERR,
"cannot obtain variable %s: %s",
kw->name, pam_strerror(pamh, rc));
return WRDSE_UNDEF;
}
if (!val) {
val = "";
}
s = strdup(val);
if (!s)
return WRDSE_NOSPACE;
*ret = s;
return WRDSE_OK;
}
int
gray_expand_string(pam_handle_t *pamh, const char *str, char **output_ptr)
{
struct wordsplit ws;
int wsflags = WRDSF_NOSPLIT
| WRDSF_GETVAR
| WRDSF_CLOSURE
| WRDSF_NOCMD
| WRDSF_UNDEF;
int rc;
str++; /* Get past the initial $ */
if (*str == '{') {
str++;
if (var_tok(str, &name, &namelen))
return 1;
end = str + namelen;
if (*end == ':') {
end++;
if (*end == '-')
end++;
if (repl_tok(end, &repl, &repllen))
return 1;
end += repllen;
}
end++;
} else {
name = end = str;
for (namelen = 0; ISKW(*end); namelen++, end++)
;
}
kw = gray_find_keyword(vartab, name, namelen);
if (!kw) {
_pam_log(LOG_ERR,
"unknown PAM variable: %*.*s",
namelen, namelen, name);
return 1;
}
rc = pam_get_item(pamh, kw->code, (const void**) &val);
if (rc) {
_pam_log(LOG_ERR,
"cannot obtain variable %s: %s",
kw->name, pam_strerror(pamh, rc));
return 1;
}
if (!val) {
if (repl) {
val = repl;
vallen = repllen;
} else {
val = "";
vallen = 0;
}
} else
vallen = strlen(val);
gray_escape_string(slist, val, vallen);
*endp = end;
return 0;
}
void
gray_expand_argv(pam_handle_t *pamh, int argc, const char **argv,
gray_slist_t slist)
{
int i;
for (i = 0; i < argc; i++) {
if (i > 0)
gray_slist_append_char(slist, ' ');
if (strchr(argv[i], '$') == 0)
gray_slist_append(slist, argv[i], strlen(argv[i]));
else {
const char *p;
for (p = argv[i]; *p; p++) {
if (*p == '\\') {
p++;
gray_slist_append_char(slist, *p);
} else if (*p == '$') {
if (get_variable(pamh, p, slist, &p))
gray_slist_append_char(slist,
*p);
else
p--;
} else
gray_slist_append_char(slist, *p);
}
}
}
}
void
gray_expand_string(pam_handle_t *pamh, const char *str, gray_slist_t slist)
{
const char *p;
#define FLUSH() gray_slist_append(slist, str, p - str); str = p
ws.ws_getvar = get_pam_var;
ws.ws_closure = pamh;
for (p = str; *p; ) {
if (*p == '\\') {
FLUSH();
p++;
if (*p) {
gray_slist_append_char(slist, *p);
p++;
} else {
gray_slist_append_char(slist, '\\');
break;
}
str = p;
} else if (*p == '$') {
FLUSH();
if (get_variable(pamh, p, slist, &p)) {
gray_slist_append_char(slist, *p);
p++;
}
str = p;
} else
p++;
rc = wordsplit(str, &ws, wsflags);
if (rc) {
if (ws.ws_errctx)
_pam_log(LOG_ERR,
"string split: %s: %s",
wordsplit_strerror (&ws), ws.ws_errctx);
else
_pam_log(LOG_ERR,
"string split: %s",
wordsplit_strerror (&ws));
rc = (rc == WRDSE_NOSPACE) ? PAM_BUF_ERR : PAM_SERVICE_ERR;
} else {
*output_ptr = ws.ws_wordv[0];
ws.ws_wordv[0] = NULL;
}
FLUSH();
wordsplit_free(&ws);
return rc;
}

1
lib/wordsplit Submodule

@ -0,0 +1 @@
Subproject commit 66e5c112850e85e3a37efbec30a0d7b8d7cff83e

View file

@ -1576,10 +1576,8 @@ import_public_key(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
_pam_log(LOG_ERR, "configuration variable `filter' not set");
return PAM_SERVICE_ERR;
}
if (!attr) {
_pam_log(LOG_ERR, "configuration variable `attr' not set");
return PAM_SERVICE_ERR;
}
if (!attr)
attr = "sshPublicKey";
ld = ldap_connect(env);
if (!ld)
@ -1588,27 +1586,15 @@ import_public_key(pam_handle_t *pamh, struct passwd *pw, struct gray_env *env)
retval = PAM_SERVICE_ERR;
else {
char *filter;
gray_slist_t slist;
slist = gray_slist_create();
if (!slist)
retval = errno_to_pam(errno);
else {
gray_expand_string(pamh, filter_pat, slist);
gray_slist_append_char(slist, 0);
filter = gray_slist_finish(slist);
if (filter) {
char **keys = get_pubkeys(ld, base, filter,
attr);
if (keys) {
retval = store_pubkeys(keys, pw, env);
argcvz_free(keys);
} else
retval = PAM_SUCCESS;
} else {
retval = errno_to_pam(gray_slist_err(slist));
}
gray_slist_free(&slist);
retval = gray_expand_string(pamh, filter_pat, &filter);
if (retval == PAM_SUCCESS) {
char **keys = get_pubkeys(ld, base, filter, attr);
if (keys) {
retval = store_pubkeys(keys, pw, env);
argcvz_free(keys);
} else
retval = PAM_SUCCESS;
}
}
ldap_unbind(ld);

View file

@ -1,5 +1,5 @@
/* This file is part of pam-modules.
Copyright (C) 2006-2008, 2010-2012, 2014-2015, 2018 Sergey Poznyakoff
Copyright (C) 2006-2020 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@ -20,8 +20,6 @@
/* Command line parsing */
static long debug_level;
static int xargc;
static const char **xargv;
static int priority = LOG_INFO;
static int facility = LOG_AUTHPRIV;
static const char *syslog_tag = MODULE_NAME;
@ -103,7 +101,8 @@ struct pam_opt pam_opt[] = {
static int
_pam_parse(pam_handle_t *pamh, int argc, const char **argv)
_pam_parse(pam_handle_t *pamh, int argc, const char **argv,
int *ret_argc, char const ***ret_argv)
{
int i;
const char **targv;
@ -125,8 +124,8 @@ _pam_parse(pam_handle_t *pamh, int argc, const char **argv)
gray_parseopt(pam_opt, i, targv);
free(targv);
xargc = argc - i;
xargv = argv + i;
*ret_argc = argc - i;
*ret_argv = argv + i;
closelog();
gray_log_init(!do_open, syslog_tag, facility);
@ -136,7 +135,10 @@ _pam_parse(pam_handle_t *pamh, int argc, const char **argv)
static int
echo(pam_handle_t *pamh, const char *prefix, int argc, const char **argv)
{
if (_pam_parse(pamh, argc, argv) == PAM_SUCCESS) {
int xargc;
const char **xargv;
if (_pam_parse(pamh, argc, argv, &xargc, &xargv) == PAM_SUCCESS) {
gray_slist_t slist;
slist = gray_slist_create();
@ -144,20 +146,35 @@ echo(pam_handle_t *pamh, const char *prefix, int argc, const char **argv)
errno_to_pam(errno);
} else {
char *str;
int rc = PAM_SUCCESS;
int i;
if (prefix) {
gray_slist_append(slist, prefix,
strlen(prefix));
gray_slist_append(slist, ": ", 2);
}
gray_expand_argv(pamh, xargc, xargv, slist);
gray_slist_append_char(slist, 0);
str = gray_slist_finish(slist);
if (str)
_pam_log(priority, "%s", str);
else
errno_to_pam(gray_slist_err(slist));
for (i = 0; i < xargc; i++) {
char *s;
if (i)
gray_slist_append(slist, " ", 1);
rc = gray_expand_string(pamh, xargv[i], &s);
if (rc != PAM_SUCCESS)
break;
gray_slist_append(slist, s, strlen(s));
free(s);
}
if (rc == PAM_SUCCESS) {
gray_slist_append_char(slist, 0);
str = gray_slist_finish(slist);
if (str)
_pam_log(priority, "%s", str);
else
errno_to_pam(gray_slist_err(slist));
}
gray_slist_free(&slist);
}
gray_slist_free(&slist);
}
return PAM_IGNORE;
}

View file

@ -358,21 +358,16 @@ gpam_sql_verify_user_pass(pam_handle_t *pamh, const char *password,
rc = mysql_do_query(&mysql, query);
if (rc == PAM_SUCCESS) {
const char *q;
gray_slist_t slist;
char *q;
rc = check_query_result(&mysql, password);
/* FIXME: This comment is needed to pacify
`make check-sql-config' in doc:
gpam_sql_find_config("setenv-query") */
if (rc == PAM_SUCCESS) {
rc = gpam_sql_get_query(pamh, "setenv-query", 0,
&slist, &q);
rc = gpam_sql_get_query(pamh, "setenv-query", 0, &q);
if (rc == PAM_SUCCESS) {
if (q) {
mysql_setenv(pamh, &mysql, q);
gray_slist_free(&slist);
}
mysql_setenv(pamh, &mysql, q);
free(q);
}
}
mysql_close(&mysql);

View file

@ -114,8 +114,7 @@ gpam_sql_verify_user_pass(pam_handle_t *pamh, const char *password,
} else {
char *p;
int n;
gray_slist_t slist;
char const *query;
char *query;
n = PQntuples(res);
DEBUG(20,("Returned %d tuples", n));
@ -155,13 +154,10 @@ gpam_sql_verify_user_pass(pam_handle_t *pamh, const char *password,
`make check-sql-config' in doc:
gpam_sql_find_config("setenv-query") */
if (rc == PAM_SUCCESS) {
rc = gpam_sql_get_query(pamh, "setenv-query", 0,
&slist, &query);
rc = gpam_sql_get_query(pamh, "setenv-query", 0, &query);
if (rc == PAM_SUCCESS) {
if (query) {
pgsql_setenv(pamh, pgconn, query);
gray_slist_free(&slist);
}
pgsql_setenv(pamh, pgconn, query);
free(query);
}
}
}

View file

@ -1,5 +1,5 @@
/* This file is part of pam-modules.
Copyright (C) 2005-2008, 2010-2012, 2014-2015, 2018 Sergey Poznyakoff
Copyright (C) 2005-2020 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@ -161,11 +161,10 @@ gpam_sql_check_boolean_config(const char *name, int defval)
int
gpam_sql_get_query(pam_handle_t *pamh, const char *name, int required,
gray_slist_t *pslist, const char **retptr)
char **retptr)
{
gray_slist_t slist;
const char *query = gpam_sql_find_config(name);
char *retval;
int rc;
if (!query) {
if (required) {
@ -173,24 +172,13 @@ gpam_sql_get_query(pam_handle_t *pamh, const char *name, int required,
gpam_sql_config_file, name);
return PAM_AUTHINFO_UNAVAIL;
}
*pslist = NULL;
*retptr = NULL;
return PAM_SUCCESS;
}
slist = gray_slist_create();
if (!slist)
return errno_to_pam(errno);
gray_expand_string(pamh, query, slist);
gray_slist_append_char(slist, 0);
*pslist = slist;
retval = gray_slist_finish(slist);
if (gray_slist_err(slist)) {
int rc = errno_to_pam(gray_slist_err(slist));
gray_slist_free(&slist);
rc = gray_expand_string(pamh, query, retptr);
if (rc != PAM_SUCCESS)
return rc;
}
*retptr = retval;
return PAM_SUCCESS;
}
@ -226,18 +214,16 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
if (gray_env_read(gpam_sql_config_file, &config_env))
retval = PAM_SERVICE_ERR;
else {
gray_slist_t slist;
char const *query;
char *query;
/* FIXME: This comment is needed to pacify
`make check-sql-config' in doc:
gpam_sql_find_config("passwd-query") */
retval = gpam_sql_get_query(pamh, "passwd-query", 1,
&slist, &query);
retval = gpam_sql_get_query(pamh, "passwd-query", 1, &query);
if (retval == PAM_SUCCESS) {
retval = gpam_sql_verify_user_pass(pamh, password,
query);
gray_slist_free(&slist);
free(query);
}
}
@ -279,13 +265,13 @@ sql_session_mgmt(pam_handle_t *pamh, int flags,
retval = PAM_SERVICE_ERR;
else {
gray_slist_t slist;
char const *query;
char *query;
retval = gpam_sql_get_query(pamh, query_name, 0, &slist, &query);
retval = gpam_sql_get_query(pamh, query_name, 0, &query);
if (retval == PAM_SUCCESS) {
if (query) {
retval = gpam_sql_acct(pamh, query);
gray_slist_free(&slist);
free(query);
}
}
}

View file

@ -48,7 +48,6 @@ int gpam_sql_acct(pam_handle_t *pamh, const char *query);
char *gpam_sql_find_config(const char *name);
int gpam_sql_get_query(pam_handle_t *pamh, char const *var,
int required,
gray_slist_t *pslist, const char **query);
int required, char **query);
int gpam_sql_check_boolean_config(const char *name, int defval);

View file

@ -1,5 +1,5 @@
/* This file is part of pam-modules.
Copyright (C) 2012, 2014-2015, 2018 Sergey Poznyakoff
Copyright (C) 2012-2020 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
@ -314,11 +314,20 @@ exec_file(pam_handle_t *pamh, char **argv, const char *logfile)
return PAM_SUCCESS;
}
static void
argcv_free(int wc, char **wv)
{
int i;
for (i = 0; i < wc; i++)
free(wv[i]);
free(wv);
}
PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
int retval = PAM_IGNORE;
gray_slist_t slist;
cntl_flags = 0;
debug_level = 0;
@ -338,22 +347,14 @@ pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
if (motd_file_name) {
char *file;
slist = gray_slist_create();
if (!slist)
retval = errno_to_pam(errno);
else {
gray_expand_string(pamh, motd_file_name, slist);
gray_slist_append_char(slist, 0);
file = gray_slist_finish(slist);
if (file)
retval = read_file(pamh, file);
else
retval = errno_to_pam(errno);
retval = gray_expand_string(pamh, motd_file_name, &file);
if (retval == PAM_SUCCESS) {
retval = read_file(pamh, file);
free(file);
}
gray_slist_free(&slist);
} else if (optindex >= 0) {
char **xargv;
int i;
argc -= optindex;
argv += optindex;
@ -361,30 +362,21 @@ pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
_pam_log(LOG_INFO, "empty command line");
return retval;
}
xargv = calloc((argc + 1), sizeof (xargv[0]));
xargv = calloc((argc + 1), sizeof(xargv[0]));
if (!xargv)
return errno_to_pam(errno);
slist = gray_slist_create();
if (!slist)
retval = errno_to_pam(errno);
else {
int i;
int ec;
for (i = 0; i < argc; i++) {
gray_expand_string(pamh, argv[i], slist);
gray_slist_append_char(slist, 0);
xargv[i] = gray_slist_finish(slist);
for (i = 0; i < argc; i++) {
retval = gray_expand_string(pamh, argv[i], &xargv[i]);
if (retval != PAM_SUCCESS) {
argc = i;
break;
}
xargv[i] = NULL;
ec = gray_slist_err(slist);
if (ec)
retval = errno_to_pam(ec);
else
retval = exec_file(pamh, xargv, logfile_name);
free(xargv);
gray_slist_free(&slist);
}
if (retval == PAM_SUCCESS) {
xargv[i] = NULL;
retval = exec_file(pamh, xargv, logfile_name);
}
argcv_free(argc, xargv);
} else
_pam_log(LOG_ERR,
"invalid usage: either file or exec must be specified");