From 3978f3889dcd90b99ba442d914defea3af45a177 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Mon, 9 Dec 2013 09:33:31 +0400 Subject: [PATCH 01/16] colorize svg picture --- auth-data-structure.svg | 298 +++++++++++++++++++++------------------- 1 file changed, 153 insertions(+), 145 deletions(-) diff --git a/auth-data-structure.svg b/auth-data-structure.svg index e3e1e3f..30214f5 100644 --- a/auth-data-structure.svg +++ b/auth-data-structure.svg @@ -14,7 +14,7 @@ id="svg2985" version="1.1" inkscape:version="0.48.4 r9939" - sodipodi:docname="New document 2"> + sodipodi:docname="auth-data-structure.svg"> image/svg+xml - + @@ -152,46 +152,20 @@ id="tspan3815" x="750" y="60">) - - - Secret+Payload+SHA1(Secret+Payload) - + - HMAC-SHA1( ) + + + + + + + + + + + + Key + Data + Data + id="tspan3835" + x="40" + y="160">Secret+Payload+SHA1(Secret+Payload) + HMAC-SHA1( + + Data Key - - - Data - - - - Key - - - - - SHA1(userid+password+nonce) - - Secret - -- 2.39.2 From 2b714d273b08b945ef677e0fbbf2c8d3d4437980 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Mon, 9 Dec 2013 15:07:57 +0400 Subject: [PATCH 02/16] html includes svg inside, adjust make rule --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 056f2eb..309aa12 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,7 +37,7 @@ TESTS = test_auth test_serial test_crypto test_chalresp test_base64 XFAIL_TESTS = test_chalresp html: README.html -README.html: README.md +README.html: README.md auth-data-structure.svg pandoc -f markdown -t html5 --self-contained -o README.html README.md clean-local: rm -f README.html -- 2.39.2 From b64eb98bcc732177ce73b580f0ecf839fe35d843 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Thu, 12 Dec 2013 23:59:41 +0400 Subject: [PATCH 03/16] a bit cleaner path construction --- authfile.c | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/authfile.c b/authfile.c index bb2d593..0880360 100644 --- a/authfile.c +++ b/authfile.c @@ -58,49 +58,42 @@ void authfile_template(const char *str) template = str; } -static int path_size(const char *tokenid, const char *userid) +/* + I know using these two functions and alloca() in between it ugly, but + I like the alternatives even less. =ec +*/ + +static int path_size(const char *tokenid, const struct passwd *pw) { const char *usub; const char *p, *q; - struct passwd *pw; if ((p = strchr(template, '~')) != strrchr(template, '~')) return 0; if ((q = strchr(template, '?')) != strrchr(template, '?')) return 0; - if (p && !userid) return 0; + if (p && !pw) return 0; if (q && !tokenid) return 0; - if (p == template) { - pw = getpwnam(userid); - if (!pw) return 0; - usub = pw->pw_dir; - } else { - usub = userid; - } + if (p == template) usub = pw->pw_dir; + else usub = pw->pw_name; return strlen(template)+(p?strlen(usub):0)+(q?strlen(tokenid):0)+1; } -static void -make_path(char * const path, const char *tokenid, const char *userid) +static int +make_path(char * const path, const char *tokenid, const struct passwd *pw) { - const char *usub; const char *p; char *q; - struct passwd *pw; path[0] = '\0'; - if (template[0] == '~') { - pw = getpwnam(userid); - if (!pw) return; - usub = pw->pw_dir; - } else { - usub = userid; - } q = path; for (p = template; *p; p++) switch (*p) { case '~': - strcpy(q, usub); + if (!pw) return 1; + if (p == template) strcpy(q, pw->pw_dir); + else strcpy(q, pw->pw_name); while (*q) q++; break; case '?': + if (!tokenid) return 1; strcpy(q, tokenid); while (*q) q++; break; @@ -109,6 +102,7 @@ make_path(char * const path, const char *tokenid, const char *userid) break; } *q = '\0'; + return 0; } int parse(char * const buf, const int argc, const char *argv[const]) @@ -133,6 +127,7 @@ struct _auth_obj authfile(const char *tokenid, const int csize)) { struct _auth_obj ret = {0}; + const struct passwd *pw = NULL; mode_t oldmask; FILE *fp = NULL; char *fn, *nfn; @@ -151,12 +146,16 @@ struct _auth_obj authfile(const char *tokenid, int nonsize; struct _auth_obj ao; - if ((fnl = path_size(tokenid, userid)) == 0) { - ret.err = "authfile path impossible to build"; + if (userid) pw = getpwnam(userid); + if ((fnl = path_size(tokenid, pw)) == 0) { + ret.err = "authfile path_size failed"; return ret; } fn = alloca(fnl); - make_path(fn, tokenid, userid); + if (make_path(fn, tokenid, pw)) { + ret.err = "authfile make_path failed"; + return ret; + } nfn = alloca(fnl+32); snprintf(nfn, fnl+32, "%s.%d.%ld", fn, (int)getpid(), (long)time(NULL)); fp = fopen(fn, "r"); -- 2.39.2 From f6b4fd086473f179295386fb47fd422f47015d16 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Fri, 13 Dec 2013 00:00:19 +0400 Subject: [PATCH 04/16] version 0.9.2 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1a14878..6a86227 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([pam_pcsc_cr], 0.9.1) +AC_INIT([pam_pcsc_cr], 0.9.2) AC_CONFIG_SRCDIR([pam_pcsc_cr.c]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE -- 2.39.2 From f181efdbcc869bf1f13c802275a461dfeee92c36 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Wed, 18 Dec 2013 00:38:34 +0400 Subject: [PATCH 05/16] get rid of tokenid altogether --- README.md | 7 +------ authfile.c | 44 +++++++++++++++----------------------------- authfile.h | 3 +-- pam_cr_setup.c | 9 ++------- pam_pcsc_cr.c | 15 +++++---------- 5 files changed, 24 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index d7b91ac..2ebc86f 100644 --- a/README.md +++ b/README.md @@ -84,12 +84,7 @@ according to template that can be provided both to PAM module and to the setup program (and must be the same, obviously). In the template string, character '~' in the first position is substituted with the userid's home directory, '~' in a position other than first - with the userid -itself, and character '?' - with the "tokenid". This latter is just an -arbitrary string that is not involved in the authentication process. -But, if the template contains '?' but not '~', login process can start -without the knowlege of the userid. Userid will be picked from the file -and injected into the PAM environment, given that tokenid is known from -the start. +itself. Default template string is `~/.pam_cr/auth`, i.e. the file lives in the user's home directory, in the subdirectory `.pam_cr`. diff --git a/authfile.c b/authfile.c index 0880360..93157a4 100644 --- a/authfile.c +++ b/authfile.c @@ -42,13 +42,11 @@ freely, subject to the following restrictions: * Template string may contain zero or one '~' and zero or one '?'. * '~' at the beginning of the template string is substituted with * the home directory of the userid. In any other position it is - * substituted with the userid itself. '?' is substituted with the - * tokenid. There is no way to make the resulting path contain '~' - * or '?'. If there is more than one '~' or '?', or if the '~' is - * at the beginning but userid does not resolve via getpwnam, or - * the character to substitute is present but the argument is NULL, - * NULL is returned. Otherwise, malloc()'ed area containg the path - * string. + * substituted with the userid itself. There is no way to make the + * resulting path contain '~'. If there is more than one '~', or if + * the '~' is at the beginning but userid does not resolve via + * getpwnam, or '~' is present but the argument is NULL, path_size + * returns 0, and make_path returns 1. */ static const char *template = "~/.pam_cr/auth"; @@ -63,22 +61,18 @@ void authfile_template(const char *str) I like the alternatives even less. =ec */ -static int path_size(const char *tokenid, const struct passwd *pw) +static int path_size(const struct passwd *pw) { - const char *usub; - const char *p, *q; + const char *p; if ((p = strchr(template, '~')) != strrchr(template, '~')) return 0; - if ((q = strchr(template, '?')) != strrchr(template, '?')) return 0; if (p && !pw) return 0; - if (q && !tokenid) return 0; - if (p == template) usub = pw->pw_dir; - else usub = pw->pw_name; - return strlen(template)+(p?strlen(usub):0)+(q?strlen(tokenid):0)+1; + if (p == template) return strlen(template)+strlen(pw->pw_dir)+1; + else return strlen(template)+strlen(pw->pw_name)+1; } static int -make_path(char * const path, const char *tokenid, const struct passwd *pw) +make_path(char * const path, const struct passwd *pw) { const char *p; char *q; @@ -92,11 +86,6 @@ make_path(char * const path, const char *tokenid, const struct passwd *pw) else strcpy(q, pw->pw_name); while (*q) q++; break; - case '?': - if (!tokenid) return 1; - strcpy(q, tokenid); - while (*q) q++; - break; default: *q++ = *p; break; @@ -118,8 +107,7 @@ int parse(char * const buf, const int argc, const char *argv[const]) return i != argc; } -struct _auth_obj authfile(const char *tokenid, - const char *userid, const char *password, +struct _auth_obj authfile(const char *userid, const char *password, void (*update_nonce)(char *nonce, const int nonsize), const unsigned char *secret, const int secsize, const unsigned char *payload, const int paylsize, @@ -135,11 +123,10 @@ struct _auth_obj authfile(const char *tokenid, struct stat st = {0}; char *buf = NULL; struct { - const char *tokenid; const char *userid; const char *nonce; const char *hablob; - } w = {"", NULL, NULL, NULL}; + } w = {NULL, NULL, NULL}; unsigned char *ablob = NULL; int blobsize = 0; char *newnonce; @@ -147,12 +134,12 @@ struct _auth_obj authfile(const char *tokenid, struct _auth_obj ao; if (userid) pw = getpwnam(userid); - if ((fnl = path_size(tokenid, pw)) == 0) { + if ((fnl = path_size(pw)) == 0) { ret.err = "authfile path_size failed"; return ret; } fn = alloca(fnl); - if (make_path(fn, tokenid, pw)) { + if (make_path(fn, pw)) { ret.err = "authfile make_path failed"; return ret; } @@ -208,8 +195,7 @@ struct _auth_obj authfile(const char *tokenid, if (b64_encode(ao.data, ao.datasize, b64, &bsize)) { ret.err = "error: could not encode auth string"; - } else if (fprintf(fp, "%s:%s:%s:%s\n", - tokenid?tokenid:w.tokenid, + } else if (fprintf(fp, "%s:%s:%s\n", userid?userid:w.userid, newnonce, b64) < 0) { ret.err = strerror(errno); } diff --git a/authfile.h b/authfile.h index 6ac535a..ad6fbc1 100644 --- a/authfile.h +++ b/authfile.h @@ -26,8 +26,7 @@ freely, subject to the following restrictions: void authfile_template(const char *template); -struct _auth_obj authfile(const char *tokenid, - const char *userid, const char *password, +struct _auth_obj authfile(const char *userid, const char *password, void (*update_nonce)(char *nonce, const int nonsize), const unsigned char *secret, const int secsize, const unsigned char *payload, const int paysize, diff --git a/pam_cr_setup.c b/pam_cr_setup.c index af4e116..612747a 100644 --- a/pam_cr_setup.c +++ b/pam_cr_setup.c @@ -70,7 +70,6 @@ static void usage(const char * const cmd) " -f template - template for auth state filepath\n" " -a secret | -A file-with-secret | -A -\n" " - 40-character hexadecimal secret\n" - " -s token-serial - public I.D. of the token\n" " -n nonce - initial nonce\n" " -l payload - keyring unlock password\n" " -p password - login password\n" @@ -89,12 +88,11 @@ int main(int argc, char *argv[]) unsigned char bsecret[20]; unsigned char *secret = NULL; int i; - char *tokenid = NULL; char *userid = getlogin(); char *payload = NULL; char *password = ""; - while ((c = getopt(argc, argv, "ho:f:a:A:s:n:l:p:v")) != -1) + while ((c = getopt(argc, argv, "ho:f:a:A:n:l:p:v")) != -1) switch (c) { case 'h': usage(argv[0]); @@ -124,9 +122,6 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } break; - case 's': - tokenid = optarg; - break; case 'n': mynonce = optarg; break; @@ -192,7 +187,7 @@ int main(int argc, char *argv[]) sscanf(hsecret + i * 2, "%2hhx", &bsecret[i]); secret = bsecret; } - ao = authfile(tokenid, userid, password, update_nonce, + ao = authfile(userid, password, update_nonce, secret, secret ? sizeof(bsecret) : 0, (unsigned char *)payload, payload ? strlen(payload) : 0, token_key); diff --git a/pam_pcsc_cr.c b/pam_pcsc_cr.c index 9504415..f729fad 100644 --- a/pam_pcsc_cr.c +++ b/pam_pcsc_cr.c @@ -156,7 +156,6 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { struct _cfg cfg = {0}; - const char *tokenid = NULL; const char *user; const char *password; struct _auth_obj ao; @@ -171,12 +170,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, pam_strerror(pamh, pam_err)); return pam_err; } - if (strspn(user, "0123456789") == strlen(user)) { - tokenid = user; - user = NULL; - } - if (cfg.verbose) syslog(LOG_DEBUG, "tokenid=\"%s\", user=\"%s\"", - tokenid?tokenid:"", user?user:""); + if (cfg.verbose) syslog(LOG_DEBUG, "user=\"%s\"", user?user:""); if (!cfg.noaskpass) { if ((pam_err = pam_get_authtok(pamh, PAM_AUTHTOK, @@ -191,14 +185,15 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, password = ""; } - ao = authfile(tokenid, user, password, update_nonce, + ao = authfile(user, password, update_nonce, NULL, 0, NULL, 0, token_key); if (ao.err) { if (cfg.verbose) syslog(LOG_INFO, "authfile: %s", ao.err); return PAM_AUTH_ERR; } else { - if (!user) - pam_set_item(pamh, PAM_USER, ao.data); + /* Just because we can. Probably not much use for that. */ + /* Userid written in authfile may differ from the login one. */ + pam_set_item(pamh, PAM_USER, ao.data); if (cfg.injectauth && ao.payload && ao.payload[0]) pam_set_item(pamh, PAM_AUTHTOK, ao.payload); if (cfg.verbose) syslog(LOG_DEBUG, "authenticated"); -- 2.39.2 From f59d631a83fac2ded45522f10fc0e800967ebe88 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Wed, 18 Dec 2013 01:46:13 +0400 Subject: [PATCH 06/16] add man page for pam_cr_setup --- Makefile.am | 2 + pam_cr_setup.8 | 103 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 pam_cr_setup.8 diff --git a/Makefile.am b/Makefile.am index 309aa12..80ab22d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,6 +24,8 @@ pam_pcsc_cr_la_LIBADD = libpcsc_cr.la bin_PROGRAMS = pam_cr_setup pam_cr_setup_LDADD = libpcsc_cr.la +man_MANS = pam_cr_setup.8 + check_PROGRAMS = test_auth test_serial test_crypto test_chalresp test_base64 test_auth_LDADD = libpcsc_cr.la test_serial_LDADD = libpcsc_cr.la diff --git a/pam_cr_setup.8 b/pam_cr_setup.8 new file mode 100644 index 0000000..2417064 --- /dev/null +++ b/pam_cr_setup.8 @@ -0,0 +1,103 @@ +.\"Copyright (c) 2013 Eugene Crosser +.\" +.\"This software is provided 'as-is', without any express or implied +.\"warranty. In no event will the authors be held liable for any damages +.\"arising from the use of this software. +.\" +.\"Permission is granted to anyone to use this software for any purpose, +.\"including commercial applications, and to alter it and redistribute it +.\"freely, subject to the following restrictions: +.\" +.\" 1. The origin of this software must not be misrepresented; you must +.\" not claim that you wrote the original software. If you use this +.\" software in a product, an acknowledgment in the product documentation +.\" would be appreciated but is not required. +.\" +.\" 2. Altered source versions must be plainly marked as such, and must +.\" not be misrepresented as being the original software. +.\" +.\" 3. This notice may not be removed or altered from any source +.\" distribution. +.\" +.TH PAM_CR_SETUP 8 "18 Dec 2013" PAM_PCSC_CR PAM_PCSC_CR +.SH NAME +pam_cr_setup \- manipulate user auth file for pam_pcsc_cr +.SH SYNOPSYS +.B pam_cr_setup +[options] [username] +.SH DESCRIPTION +.B pam_cr_setup +creates and modifies the file with the shared secret that is used by +.B pam_pcsc_cr +PAM module for crypto-token based authentication. To initially create +the file, you must provide the shared secret that is also installed in +the token. Later on, the command may be used to update the payload +which may be the keyring unlock key. If used in the latter mode, and +if the crypto-token is present, specifying the shared secret is not +necessary. +.SH OPTIONS +.B \-h +\- show short description and exit. +.sp +.B \-o backend-option +\- option specific to the crypto-token. +The format is +.B backend:key=value. +At present, only Yubikey Neo +crypto-token is supported, and the only option is +.B ykneo:slot=[1|2]. +.sp +.B \-f template +\- template for the auth file path. It may contain one character +.B '~' +which, if in the first position, is replaced with the userid's +home directory path, and if in any other position - with the userid +itself. +.sp +.B \-a secret +or +.B \-A file-with-secret +or +.B \-A - +\- 40-character hexadecimal representation of the shared secret. +It must be provided when first creating the file, and when updating +the payload in the absense of the crypto-token. +.B \-A - +means that the 40-character string is read from +.B stdin. +.sp +.B \-n nonce +\- initial nonce. Currently this must be a decimal representation of an +integer. It is subsequently incremented by one on every successful +authentication session. +.sp +.B \-l payload +\- a string that will be injected into the PAM framework as +.B AUTH_TOKEN +upon successful authentication. It is useful to have the keyring +unlock password there. The payload is encrypted in the file, and only +exists in memory in decrypted form for a short period (unless leaked +by other PAM modules). +.sp +.B \-p password +\- login password that is used to create the challenge (not the one +from +.BR /etc/shadow "). +If not specified, an empty string is used, which is the same as the +.B pam_pcsc_cr +module uses when invoked with +.B noaskpass +argument. With empty password, login process requires only the presence +of the crypto-token, and does not involve any input from the user. +.sp +.B \-v +\- output the userid and payload from the auth file. Note that displaying +the payload on screen to be seen by passers by may not be a good idea. +.sp + +.SH COPYRIGHT +2013 Eugene G. Crosser +.br +Released under zlib Open Source license. +.SH SEE ALSO +.BR pam "(3), "ykpersonalize "(1) -- 2.39.2 From 852dfc60feda9313c12310d646e66baef9fb83b2 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Thu, 19 Dec 2013 19:04:52 +0400 Subject: [PATCH 07/16] add manpage for the module --- .gitignore | 1 + pam_cr_setup.8 | 18 +++++++------- pam_pcsc_cr.8 | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 pam_pcsc_cr.8 diff --git a/.gitignore b/.gitignore index a316a66..0fb0193 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.log *.trs *.tar.xz +*.html .deps/ .libs/ Makefile diff --git a/pam_cr_setup.8 b/pam_cr_setup.8 index 2417064..d09f445 100644 --- a/pam_cr_setup.8 +++ b/pam_cr_setup.8 @@ -38,7 +38,7 @@ necessary. .SH OPTIONS .B \-h \- show short description and exit. -.sp +.PP .B \-o backend-option \- option specific to the crypto-token. The format is @@ -46,14 +46,14 @@ The format is At present, only Yubikey Neo crypto-token is supported, and the only option is .B ykneo:slot=[1|2]. -.sp +.PP .B \-f template \- template for the auth file path. It may contain one character .B '~' which, if in the first position, is replaced with the userid's home directory path, and if in any other position - with the userid itself. -.sp +.PP .B \-a secret or .B \-A file-with-secret @@ -65,12 +65,12 @@ the payload in the absense of the crypto-token. .B \-A - means that the 40-character string is read from .B stdin. -.sp +.PP .B \-n nonce \- initial nonce. Currently this must be a decimal representation of an integer. It is subsequently incremented by one on every successful authentication session. -.sp +.PP .B \-l payload \- a string that will be injected into the PAM framework as .B AUTH_TOKEN @@ -78,7 +78,7 @@ upon successful authentication. It is useful to have the keyring unlock password there. The payload is encrypted in the file, and only exists in memory in decrypted form for a short period (unless leaked by other PAM modules). -.sp +.PP .B \-p password \- login password that is used to create the challenge (not the one from @@ -89,15 +89,15 @@ module uses when invoked with .B noaskpass argument. With empty password, login process requires only the presence of the crypto-token, and does not involve any input from the user. -.sp +.PP .B \-v \- output the userid and payload from the auth file. Note that displaying the payload on screen to be seen by passers by may not be a good idea. -.sp +.PP .SH COPYRIGHT 2013 Eugene G. Crosser .br Released under zlib Open Source license. .SH SEE ALSO -.BR pam "(3), "ykpersonalize "(1) +.BR pam "(3), "ykpersonalize "(1), "pam_pcsc_cr "(8) diff --git a/pam_pcsc_cr.8 b/pam_pcsc_cr.8 new file mode 100644 index 0000000..431e2a2 --- /dev/null +++ b/pam_pcsc_cr.8 @@ -0,0 +1,67 @@ +.\"Copyright (c) 2013 Eugene Crosser +.\" +.\"This software is provided 'as-is', without any express or implied +.\"warranty. In no event will the authors be held liable for any damages +.\"arising from the use of this software. +.\" +.\"Permission is granted to anyone to use this software for any purpose, +.\"including commercial applications, and to alter it and redistribute it +.\"freely, subject to the following restrictions: +.\" +.\" 1. The origin of this software must not be misrepresented; you must +.\" not claim that you wrote the original software. If you use this +.\" software in a product, an acknowledgment in the product documentation +.\" would be appreciated but is not required. +.\" +.\" 2. Altered source versions must be plainly marked as such, and must +.\" not be misrepresented as being the original software. +.\" +.\" 3. This notice may not be removed or altered from any source +.\" distribution. +.\" +.TH PAM_PCSC_CR 8 "18 Dec 2013" PAM_PCSC_CR PAM_PCSC_CR +.SH NAME +pam_pcsc_cr \- Module for challenge-response authentication +.SH SYNOPSYS +.B pam_pcsc_cr.so [options] +.SH DESCRIPTION +This is a PAM module for crypto-token based authentication. +It only provides authentication component, the rest are stubs. +The module uses the contents of the auth file created with the +.B pam_cr_setup +command and optionally a password provided by the user to construct +challenge that is sent to the crypto-token over +.B pcsclite +framework. The token's response is used to decipher the encrypted part +of the file. If decryption is successful, then the extracted shared +secret is used to produce ithe expected response to the future +(different) challenge, encrypted again with the expected response, +and stowed into the file. Additional payload that was decrypted on +the way is optionally injected into the PAM framework as AUTH_TOKEN +to be later used by keyring-unlocking module. +.SH OPTIONS +.B verbose +\- write more error messages to syslog. +.PP +.B noaskpass +\- do not try to ask the user for the challenge password, use empty +string for the password. +.PP +.B injectauth +\- inject payload as PAM_AUTHTOK for the benefit of subsequent PAM modules. +.PP +.B path= +\- template used to find the file. +.PP +.B backend:key=value +\- option specific to the crypto-token. At present, only Yubikey Neo +crypto-token is supported, and the only option is +.B ykneo:slot=[1|2]. +.PP + +.SH COPYRIGHT +2013 Eugene G. Crosser +.br +Released under zlib Open Source license. +.SH SEE ALSO +.BR pam "(3), "ykpersonalize "(1), "pam_cr_setup "(8) -- 2.39.2 From e3a138187804ed3ee42339bff6a895754ea8e1f8 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Thu, 19 Dec 2013 19:17:58 +0400 Subject: [PATCH 08/16] include man pages in Makefile --- Makefile.am | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 80ab22d..c72f585 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,7 +24,7 @@ pam_pcsc_cr_la_LIBADD = libpcsc_cr.la bin_PROGRAMS = pam_cr_setup pam_cr_setup_LDADD = libpcsc_cr.la -man_MANS = pam_cr_setup.8 +man_MANS = pam_cr_setup.8 pam_pcsc_cr.8 check_PROGRAMS = test_auth test_serial test_crypto test_chalresp test_base64 test_auth_LDADD = libpcsc_cr.la @@ -33,7 +33,8 @@ test_crypto_LDADD = libpcsc_cr.la test_chalresp_LDADD = libpcsc_cr.la test_base64_LDADD = libpcsc_cr.la -EXTRA_DIST = autogen.sh README.md auth-data-structure.svg +EXTRA_DIST = autogen.sh README.md auth-data-structure.svg \ + pam_cr_setup.8 pam_pcsc_cr.8 TESTS = test_auth test_serial test_crypto test_chalresp test_base64 XFAIL_TESTS = test_chalresp -- 2.39.2 From f495853a9be5d76f0a4e8ee6eec0eb4e25cb0fa7 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Thu, 19 Dec 2013 21:43:27 +0400 Subject: [PATCH 09/16] bump version in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ebc86f..a7d862d 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ PAM module has the following parameters: Check the [project homepage](http://www.average.org/chal-resp-auth/). Pick the source tarball -[here](http://www.average.org/chal-resp-auth/pam_pcsc_cr-0.9.1.tar.xz), +[here](http://www.average.org/chal-resp-auth/pam_pcsc_cr-0.9.2.tar.xz), or you can [clone](git://git.average.org/git/pam_pcsc_cr.git) or [browse](http://www.average.org/gitweb/?p=pam_pcsc_cr.git;a=summary) the git repo. -- 2.39.2 From eaaae6374b2e8949fd2d13b45e3b9cbdb95f5299 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Wed, 25 Dec 2013 14:01:58 +0400 Subject: [PATCH 10/16] update README and .svg remove tokenid from the picture, improve style. --- README.md | 38 ++++++++++++++++++++------------------ auth-data-structure.svg | 20 +------------------- 2 files changed, 21 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index a7d862d..fbd355e 100644 --- a/README.md +++ b/README.md @@ -31,33 +31,35 @@ This package provides a UNIX [PAM](http://en.wikipedia.org/wiki/Pluggable_Authentication_Modules) module and accompanying setup program implementing [HMAC-SHA1](http://en.wikipedia.org/wiki/HMAC-SHA1) challenge-response -user authentication with hardware crypto token supporting +user authentication with a hardware crypto token supporting [PC/SC](http://en.wikipedia.org/wiki/PC/SC) (Smartcard) interface. At the time of writing, I know of just one such hardware token, Yubikey Neo from [Yubico](http://www.yubico.com/). [Pcsclite](http://pcsclite.alioth.debian.org/) infrastructure (i.e. -library and a daemon) is used to communicate with the token over +the library and the daemon) is used to communicate with the token over [CCID](http://en.wikipedia.org/wiki/Integrated_Circuit_Card_Interface_Device) (i.e. PC/SC over USB) or [NFC](http://en.wikipedia.org/wiki/Near_field_communication). It means that it works equally well when you plug the token in a USB slot and if -you put it on an NFC reader. +you put it on the NFC reader. ## Theory of Challenge-Response Authentication There are two ways to do challenge-response authentication: with shared -secret and with pre-produced response. In pre-produced response, the +secret and with pre-produced response. With pre-produced response, the host does not need to store the token's HMAC secret; on every session conversation with the token is performed twice with different challenges. The first response is used to decrypt stored encrypted challenge and compare it with cleartext challenge. A new challenge is then sent to the token, and response is used to encrypt it and store for the -future authentication session. The advantage is that the secret is not -kept anywhere except the token, so it's less chance of compromise. The -drawback is that the response is transferred in cleartext long before -being used, and can be eavesdropped on and used in a replay attack. This -is of particular concern when using NFC. This approach is used by the +future authentication session. The advantage of this approach is that +the secret is not kept anywhere other than inside the token, so the only +way to leak the secret is together with the token. The drawback is that +the response that will be expected in the next session is transferred in +cleartext in the current session, can be eavesdropped on and used in a +replay attack. This is of particular concern when using NFC. This +approach is used by the [PAM module provided by Yubico](https://github.com/Yubico/yubico-pam). My module uses the second approach, under which the HMAC secret is @@ -66,11 +68,11 @@ compromise, the host copy of the shared secret is encrypted by the key which is the expected response from the token. In the process of authentication, token's response is used to decrypt the secret, then this secret is used to compute the next expected token's response, and -this expected response is used to encrypt the secret again. This next +the expected response is used to encrypt the secret again. This next expected response is not transferred over the air, and the shared secret stays in unencrypted form in the RAM (unless paged out) for a very short period. The downside is that if the token is used against multiple -hosts, and one of them leaks the secret to an adversary, all hosts are +hosts, and the secret is leakd from one of them, all the hosts are now compromised. This is not the case with the first approach. The particular data structure is outlined in the picture: @@ -80,11 +82,11 @@ The particular data structure is outlined in the picture: Authentication file, containing nonce, encrypted shared secret, encrypted additional payload, and anciliary information, is named -according to template that can be provided both to PAM module and to the -setup program (and must be the same, obviously). In the template string, -character '~' in the first position is substituted with the userid's -home directory, '~' in a position other than first - with the userid -itself. +according to template that can be provided both to the PAM module and +to the setup program (and must be the same, obviously). In the template +string, character '~' in the first position is substituted with the +userid's home directory, '~' in a position other than first - with the +userid itself. Default template string is `~/.pam_cr/auth`, i.e. the file lives in the user's home directory, in the subdirectory `.pam_cr`. @@ -111,8 +113,8 @@ Slot 2 is the default. Secret must be supplied when creating the file, and when modifying the file in the absense of the token. Password is used to construct the challenge. If not supplied empty string is used. The pam module also uses empty string when given "noaskpass" argument, -so this can be used for "one factor" authentication mode with token -only. Payload is a string that can be optionally injected as the PAM +so this can be used for "one factor" authentication mode (with the token +only). Payload is a string that can be optionally injected as the PAM authentication token after successful authentication; subsequent PAM modules like gnome keyring unlocker module will pick it up. Note that this keyring unlocker password may be different from the login diff --git a/auth-data-structure.svg b/auth-data-structure.svg index 30214f5..8b47a45 100644 --- a/auth-data-structure.svg +++ b/auth-data-structure.svg @@ -13,7 +13,7 @@ height="464.16547" id="svg2985" version="1.1" - inkscape:version="0.48.4 r9939" + inkscape:version="0.48.3.1 r9886" sodipodi:docname="auth-data-structure.svg"> @@ -69,17 +69,6 @@ inkscape:label="Layer 1" inkscape:groupmode="layer" transform="translate(-17.858739,-17.834537)"> - tokenid - Date: Wed, 25 Dec 2013 14:15:38 +0400 Subject: [PATCH 11/16] expand manpage --- pam_pcsc_cr.8 | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/pam_pcsc_cr.8 b/pam_pcsc_cr.8 index 431e2a2..8af304d 100644 --- a/pam_pcsc_cr.8 +++ b/pam_pcsc_cr.8 @@ -58,7 +58,35 @@ string for the password. crypto-token is supported, and the only option is .B ykneo:slot=[1|2]. .PP - +.SH "MODULE TYPES PROVIDED" +.PP +All module types (\fBaccount\fR, +\fBauth\fR, +\fBpassword\fR +and +\fBsession\fR) are provided, but only \fBauth\fR is not a stub\&. +.SH "RETURN VALUES" +.PP +PAM_SUCCESS on successful authentication, error indication otherwise. +.RE +.SH "EXAMPLES" +.PP +An example usage for +/etc/pam\&.d/login +would be: +.sp +.if n \{\ +.RS 4 +.\} +.nf +# Authenticate the user +auth required pam_pcsc_cr\&.so injectauth + +.fi +.if n \{\ +.RE +.\} +.sp .SH COPYRIGHT 2013 Eugene G. Crosser .br -- 2.39.2 From 91db7a622b66e0219c6dbce650aaa74272493a08 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Wed, 25 Dec 2013 14:16:53 +0400 Subject: [PATCH 12/16] bump version --- README.md | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fbd355e..0691ef2 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ PAM module has the following parameters: Check the [project homepage](http://www.average.org/chal-resp-auth/). Pick the source tarball -[here](http://www.average.org/chal-resp-auth/pam_pcsc_cr-0.9.2.tar.xz), +[here](http://www.average.org/chal-resp-auth/pam_pcsc_cr-0.9.3.tar.xz), or you can [clone](git://git.average.org/git/pam_pcsc_cr.git) or [browse](http://www.average.org/gitweb/?p=pam_pcsc_cr.git;a=summary) the git repo. diff --git a/configure.ac b/configure.ac index 6a86227..0fc618e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([pam_pcsc_cr], 0.9.2) +AC_INIT([pam_pcsc_cr], 0.9.3) AC_CONFIG_SRCDIR([pam_pcsc_cr.c]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE -- 2.39.2 From 94cf335f2ec8f4e19250b873aba7ee4eddd7c0d2 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Sun, 5 Nov 2017 20:25:20 +0100 Subject: [PATCH 13/16] Initialize pad memory to pacify stack protectors - Initialize memory area that is being encrypted, because it is allocated rounded up to CBLKSIZE, and the last bytes are not used. If stack protector is active, it detects access to uninitialized memory. - Change default pamdir to /lib/${host_cpu}-${host_os}/security. - Fix a couple of compilation warnings. --- .gitignore | 1 + authobj.c | 9 +++++++++ configure.ac | 9 +++++---- pcsc_cr.c | 2 +- test_chalresp.c | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0fb0193..9bad79d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ Makefile Makefile.in aclocal.m4 autom4te.cache/ +compile config.guess config.h config.h.in diff --git a/authobj.c b/authobj.c index b402094..deba028 100644 --- a/authobj.c +++ b/authobj.c @@ -126,6 +126,15 @@ make_authobj(const char *userid, const char *password, const char *nonce, datasize = ((secsize + paylsize + HASHSIZE + 4 * sizeof(short) - 1) / CBLKSIZE + 1) * CBLKSIZE; data = alloca(datasize); + /* + We allocate memory rounded up to CBLKSIZE on the stack, but do not + use the last bytes. Stack protectors, if enabled, fill this memory + with `canary` value. Later, when encryption function is called, + stack protector detects that it tries to access "uninitialized + memory". Which, while technically true, is not an error. Still, + let us make stack protector happy by initializing the whole area: + */ + memset(data, 0, datasize); serial_init(&srl, data, datasize); if (serial_put(&srl, secret, secsize) != secsize) { ao.err = "authobj: serialization of secret failed"; diff --git a/configure.ac b/configure.ac index 0fc618e..e6bd59a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([pam_pcsc_cr], 0.9.3) +AC_INIT([pam_pcsc_cr], 0.9.4) AC_CONFIG_SRCDIR([pam_pcsc_cr.c]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE @@ -28,13 +28,14 @@ AC_CHECK_HEADERS([security/pam_modules.h security/pam_ext.h], [], [], [ ]) AC_CHECK_LIB([pam], [pam_start]) AC_CHECK_FUNCS([pam_get_authtok]) +pamdir=/lib/${host_cpu}-${host_os}/security AC_ARG_WITH(pam-dir, - [ --with-pam-dir=DIR path to install the PAM module (/lib/security)], + [ --with-pam-dir=DIR path to install the PAM module (${pamdir})], [PAMDIR="$withval"], [ case $prefix in -*/_inst) PAMDIR='${exec_prefix}/lib/security' ;; -*) PAMDIR=/lib/security ;; +*/_inst) PAMDIR="${prefix}${pamdir}" ;; +*) PAMDIR=${pamdir} ;; esac ]) AC_SUBST(PAMDIR) diff --git a/pcsc_cr.c b/pcsc_cr.c index 984f73b..7f2ccff 100644 --- a/pcsc_cr.c +++ b/pcsc_cr.c @@ -132,7 +132,7 @@ free_out: return rc; } -char *pcsc_errstr(long err) { +const char *pcsc_errstr(long err) { return pcsc_stringify_error(err); } diff --git a/test_chalresp.c b/test_chalresp.c index 5fda704..4f0c3e0 100644 --- a/test_chalresp.c +++ b/test_chalresp.c @@ -30,7 +30,7 @@ freely, subject to the following restrictions: #include #include "pcsc_cr.h" -static void usage(const char const *cmd) +static void usage(const char *const cmd) { fprintf(stderr, "usage: %s [-o backend:name=value] ... \"challenge\"\n", -- 2.39.2 From 14c33fc7940be40d5da2b527474e59bfbabf5f71 Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Wed, 15 Aug 2018 17:55:24 +0200 Subject: [PATCH 14/16] configure: add option "--disable-openssl" As of openssl 1.1.0, HMAC_CTX is an opaque structure and as such cannot be allocated in the stack. One has to use HMAC_CTX_new() that ultimately uses OPENSSL_zmalloc(), so the user has to free it afterwards. Not something I want to do. What they where thinking?! Richard Levitte, I am looking at you! Signed-off-by: Eugene Crosser --- configure.ac | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index e6bd59a..8adf940 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([pam_pcsc_cr], 0.9.4) +AC_INIT([pam_pcsc_cr], 0.9.5) AC_CONFIG_SRCDIR([pam_pcsc_cr.c]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE @@ -45,7 +45,13 @@ PKG_CHECK_MODULES([PCSC], [libpcsclite]) CPPFLAGS="$PCSC_CFLAGS $CPPFLAGS" LIBS="$PCSC_LIBS $LIBS" -PKG_CHECK_MODULES([OPENSSL], [libcrypto], [], [:]) +AC_ARG_ENABLE(openssl, + [ --disable-openssl do not use openssl even when it is present], + [], [enable_openssl=yes] +) +AS_IF([test "x$enable_openssl" = "xyes"], [ + PKG_CHECK_MODULES([OPENSSL], [libcrypto], [], [:]) +]) AS_IF([test "x$OPENSSL_CFLAGS" != "x" -o "x$OPENSSL_LIBS" != "x" ], [ use_openssl=yes ]) -- 2.39.2 From f362aa1f9cfeedd86f89cf1cdf7558aa6782ceba Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Wed, 15 Aug 2018 18:31:37 +0200 Subject: [PATCH 15/16] openssl: make it use 'shortcut' function HMAC() Actually, we may avoid the complexity of managing HMAC_CTX by using "convenience" function HMAC() instead of CTX-based family. In this form, the program compiles against openssl 1.1.x (but the configure option "--disable-openssl" is kept in case someone dislikes openssl). Signed-off-by: Eugene Crosser --- ossl_crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ossl_crypto.c b/ossl_crypto.c index e0e10bc..4cf0afa 100644 --- a/ossl_crypto.c +++ b/ossl_crypto.c @@ -75,7 +75,7 @@ static unsigned long ossl_hmac(const void *key, int const keylen, const void *pt, const int tlen, void *tag, int *taglen) { -#if 1 +#if 0 HMAC_CTX hctx; HMAC_CTX_init(&hctx); -- 2.39.2 From bcd4173e47de642dd5fa70e1d6019d542ecaf76c Mon Sep 17 00:00:00 2001 From: Eugene Crosser Date: Fri, 10 Nov 2017 02:27:01 +0100 Subject: [PATCH 16/16] check that old data exists when it is necessary --- authobj.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/authobj.c b/authobj.c index deba028..b4a1f2b 100644 --- a/authobj.c +++ b/authobj.c @@ -241,6 +241,10 @@ struct _auth_obj authobj(const char *userid, const char *password, struct _auth_obj new_ao = {0}; if (!secret || !secsize || !payload) { + if (!ablob || !blobsize) { + new_ao.err = "authobj: previous data not supplied"; + return new_ao; + } old_ao = parse_authobj(userid, password, oldnonce, secret, secsize, ablob, blobsize, fetch_key); -- 2.39.2