1 /*
2 * libp11 PAM Login Module
3 * Copyright (C) 2003 Mario Strasser <mast@gmx.net>,
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 */
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18
19 #ifndef PACKAGE
20 #define PACKAGE "pam_p11"
21 #endif
22
23 #include <syslog.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <openssl/crypto.h>
32 #include <libp11.h>
33 #include <regex.h>
34 #include <stdlib.h>
35
36 /* openssl deprecated API emulation */
37 #ifndef HAVE_EVP_MD_CTX_NEW
38 #define EVP_MD_CTX_new() EVP_MD_CTX_create()
39 #endif
40 #ifndef HAVE_EVP_MD_CTX_FREE
41 #define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy((ctx))
42 #endif
43 #ifndef HAVE_EVP_MD_CTX_RESET
44 #define EVP_MD_CTX_reset(ctx) EVP_MD_CTX_cleanup((ctx))
45 #endif
46
47 #ifdef ENABLE_NLS
48 #include <libintl.h>
49 #include <locale.h>
50 #define _(string) gettext(string)
51 #ifndef LOCALEDIR
52 #define LOCALEDIR "/usr/share/locale"
53 #endif
54 #else
55 #define _(string) string
56 #endif
57
58 /* We have to make this definitions before we include the pam header files! */
59 #define PAM_SM_AUTH
60 #define PAM_SM_ACCOUNT
61 #define PAM_SM_SESSION
62 #define PAM_SM_PASSWORD
63 #include <security/pam_appl.h>
64 #include <security/pam_modules.h>
65 #ifdef HAVE_SECURITY_PAM_EXT_H
66 #include <security/pam_ext.h>
67 #else
68 #define pam_syslog(handle, level, msg...) syslog(level, ## msg)
69 #endif
70
71 #ifndef HAVE_PAM_VPROMPT
pam_vprompt(pam_handle_t * pamh,int style,char ** response,const char * fmt,va_list args)72 static int pam_vprompt(pam_handle_t *pamh, int style, char **response,
73 const char *fmt, va_list args)
74 {
75 int r = PAM_CRED_INSUFFICIENT;
76 const struct pam_conv *conv;
77 struct pam_message msg;
78 struct pam_response *resp = NULL;
79 struct pam_message *(msgp[1]);
80
81 char text[128];
82 vsnprintf(text, sizeof text, fmt, args);
83
84 msgp[0] = &msg;
85 msg.msg_style = style;
86 msg.msg = text;
87
88 if (PAM_SUCCESS != pam_get_item(pamh, PAM_CONV, (const void **) &conv)
89 || NULL == conv || NULL == conv->conv
90 || conv->conv(1, (const struct pam_message **) msgp, &resp, conv->appdata_ptr)
91 || NULL == resp) {
92 goto err;
93 }
94 if (NULL != response) {
95 if (resp[0].resp) {
96 *response = strdup(resp[0].resp);
97 if (NULL == *response) {
98 pam_syslog(pamh, LOG_CRIT, "strdup() failed: %s",
99 strerror(errno));
100 goto err;
101 }
102 } else {
103 *response = NULL;
104 }
105 }
106
107 r = PAM_SUCCESS;
108 err:
109 if (resp) {
110 OPENSSL_cleanse(&resp[0].resp, sizeof resp[0].resp);
111 free(&resp[0]);
112 }
113 return r;
114 }
115 #endif
116
117 #ifndef PAM_EXTERN
118 #define PAM_EXTERN extern
119 #endif
120
prompt(int flags,pam_handle_t * pamh,int style,char ** response,const char * fmt,...)121 int prompt(int flags, pam_handle_t *pamh, int style, char **response,
122 const char *fmt, ...)
123 {
124 int r;
125
126 if (PAM_SILENT == (flags & PAM_SILENT)
127 && style != PAM_TEXT_INFO
128 && style != PAM_PROMPT_ECHO_OFF) {
129 /* PAM_SILENT does not override the prompting of the user for passwords
130 * etc., it only stops informative messages from being generated. We
131 * use PAM_TEXT_INFO and PAM_PROMPT_ECHO_OFF exclusively for the
132 * password prompt. */
133 r = PAM_SUCCESS;
134 } else {
135 va_list args;
136 va_start (args, fmt);
137 if (!response) {
138 char *p = NULL;
139 r = pam_vprompt(pamh, style, &p, fmt, args);
140 free(p);
141 } else {
142 r = pam_vprompt(pamh, style, response, fmt, args);
143 }
144 va_end(args);
145 }
146
147 return r;
148 }
149
150 struct module_data {
151 PKCS11_CTX *ctx;
152 PKCS11_SLOT *slots;
153 unsigned int nslots;
154 int module_loaded;
155 };
156
157 #ifdef TEST
158 static struct module_data *global_module_data = NULL;
159 #endif
160
module_data_cleanup(pam_handle_t * pamh,void * data,int error_status)161 void module_data_cleanup(pam_handle_t *pamh, void *data, int error_status)
162 {
163 struct module_data *module_data = data;
164 if (module_data) {
165 if (1 == module_data->module_loaded) {
166 PKCS11_release_all_slots(module_data->ctx, module_data->slots, module_data->nslots);
167 PKCS11_CTX_unload(module_data->ctx);
168 }
169 PKCS11_CTX_free(module_data->ctx);
170 EVP_cleanup();
171 ERR_free_strings();
172 free(module_data);
173 }
174 }
175
module_initialize(pam_handle_t * pamh,int flags,int argc,const char ** argv,struct module_data ** module_data)176 static int module_initialize(pam_handle_t * pamh,
177 int flags, int argc, const char **argv,
178 struct module_data **module_data)
179 {
180 int r;
181 struct module_data *data = calloc(1, sizeof *data);
182 if (NULL == data) {
183 pam_syslog(pamh, LOG_CRIT, "calloc() failed: %s",
184 strerror(errno));
185 r = PAM_BUF_ERR;
186 goto err;
187 }
188
189 #ifdef ENABLE_NLS
190 setlocale(LC_ALL, "");
191 bindtextdomain(PACKAGE, LOCALEDIR);
192 textdomain(PACKAGE);
193 #endif
194
195 /* Initialize OpenSSL */
196 OpenSSL_add_all_algorithms();
197 ERR_load_crypto_strings();
198
199 /* Load and initialize PKCS#11 module */
200 data->ctx = PKCS11_CTX_new();
201 if (0 == argc || NULL == data->ctx
202 || 0 != PKCS11_CTX_load(data->ctx, argv[0])) {
203 pam_syslog(pamh, LOG_ALERT, "Loading PKCS#11 engine failed: %s\n",
204 ERR_reason_error_string(ERR_get_error()));
205 prompt(flags, pamh, PAM_ERROR_MSG , NULL, _("Error loading PKCS#11 module"));
206 r = PAM_NO_MODULE_DATA;
207 goto err;
208 }
209 data->module_loaded = 1;
210 if (0 != PKCS11_enumerate_slots(data->ctx, &data->slots, &data->nslots)) {
211 pam_syslog(pamh, LOG_ALERT, "Initializing PKCS#11 engine failed: %s\n",
212 ERR_reason_error_string(ERR_get_error()));
213 prompt(flags, pamh, PAM_ERROR_MSG , NULL, _("Error initializing PKCS#11 module"));
214 r = PAM_AUTHINFO_UNAVAIL;
215 goto err;
216 }
217
218 #ifdef TEST
219 /* pam_set_data() is reserved for actual modules. For testing this would
220 * return PAM_SYSTEM_ERR, so we're saving the module data in a static
221 * variable. */
222 r = PAM_SUCCESS;
223 global_module_data = data;
224 #else
225 r = pam_set_data(pamh, PACKAGE, data, module_data_cleanup);
226 if (PAM_SUCCESS != r) {
227 goto err;
228 }
229 #endif
230
231 *module_data = data;
232 data = NULL;
233
234 err:
235 module_data_cleanup(pamh, data, r);
236
237 return r;
238 }
239
module_refresh(pam_handle_t * pamh,int flags,int argc,const char ** argv,const char ** user,PKCS11_CTX ** ctx,PKCS11_SLOT ** slots,unsigned int * nslots,const char ** pin_regex)240 static int module_refresh(pam_handle_t *pamh,
241 int flags, int argc, const char **argv,
242 const char **user, PKCS11_CTX **ctx,
243 PKCS11_SLOT **slots, unsigned int *nslots,
244 const char **pin_regex)
245 {
246 int r;
247 struct module_data *module_data;
248
249 if (PAM_SUCCESS != pam_get_data(pamh, PACKAGE, (void *)&module_data)
250 || NULL == module_data) {
251 r = module_initialize(pamh, flags, argc, argv, &module_data);
252 if (PAM_SUCCESS != r) {
253 goto err;
254 }
255 } else {
256 /* refresh all known slots */
257 PKCS11_release_all_slots(module_data->ctx,
258 module_data->slots, module_data->nslots);
259 module_data->slots = NULL;
260 module_data->nslots = 0;
261 if (0 != PKCS11_enumerate_slots(module_data->ctx,
262 &module_data->slots, &module_data->nslots)) {
263 pam_syslog(pamh, LOG_ALERT, "Initializing PKCS#11 engine failed: %s\n",
264 ERR_reason_error_string(ERR_get_error()));
265 prompt(flags, pamh, PAM_ERROR_MSG , NULL, _("Error initializing PKCS#11 module"));
266 r = PAM_AUTHINFO_UNAVAIL;
267 goto err;
268 }
269 }
270
271 if (1 < argc) {
272 *pin_regex = argv[1];
273 } else {
274 #ifdef __APPLE__
275 /* If multiple PAMs are allowed for macOS' login, then the captured
276 * password is used for all possible modules. To not block the token's
277 * PIN if the user enters his standard password, we're refusing to use
278 * anything that doesn't look like a PIN. */
279 *pin_regex = "^[[:digit:]]*$";
280 #else
281 *pin_regex = NULL;
282 #endif
283 }
284
285 r = pam_get_user(pamh, user, NULL);
286 if (PAM_SUCCESS != r) {
287 pam_syslog(pamh, LOG_ERR, "pam_get_user() failed %s",
288 pam_strerror(pamh, r));
289 r = PAM_USER_UNKNOWN;
290 goto err;
291 }
292
293 *ctx = module_data->ctx;
294 *nslots = module_data->nslots;
295 *slots = module_data->slots;
296
297 err:
298 return r;
299 }
300
301 extern int match_user_opensc(EVP_PKEY *authkey, const char *login);
302 extern int match_user_openssh(EVP_PKEY *authkey, const char *login);
303
key_login(pam_handle_t * pamh,int flags,PKCS11_SLOT * slot,const char * pin_regex)304 static int key_login(pam_handle_t *pamh, int flags, PKCS11_SLOT *slot, const char *pin_regex)
305 {
306 char *password = NULL;
307 int ok;
308
309 if (0 == slot->token->loginRequired
310 #ifdef HAVE_PKCS11_IS_LOGGED_IN
311 || (0 == PKCS11_is_logged_in(slot, 0, &ok)
312 && ok == 1)
313 #endif
314 ) {
315 ok = 1;
316 goto err;
317 }
318 ok = 0;
319
320 /* try to get stored item */
321 if (PAM_SUCCESS == pam_get_item(pamh, PAM_AUTHTOK, (void *)&password)
322 && NULL != password) {
323 password = strdup(password);
324 if (NULL == password) {
325 pam_syslog(pamh, LOG_CRIT, "strdup() failed: %s",
326 strerror(errno));
327 goto err;
328 }
329 } else {
330 const char *pin_info;
331
332 if (slot->token->userPinFinalTry) {
333 pin_info = _(" (last try)");
334 } else {
335 pin_info = "";
336 }
337
338 if (0 != slot->token->secureLogin) {
339 prompt(flags, pamh, PAM_TEXT_INFO, NULL,
340 _("Login on PIN pad with %s%s"),
341 slot->token->label, pin_info);
342 } else {
343 /* ask the user for the password if variable text is set */
344 if (PAM_SUCCESS != prompt(flags, pamh,
345 PAM_PROMPT_ECHO_OFF, &password,
346 _("Login with %s%s: "),
347 slot->token->label, pin_info)) {
348 goto err;
349 }
350 }
351 }
352
353 if (NULL != password && NULL != pin_regex && 0 < strlen(pin_regex)) {
354 regex_t regex;
355 int regex_compiled = 0;
356 int result = 0;
357 result = regcomp(®ex, pin_regex, REG_EXTENDED);
358 if (0 == result) {
359 regex_compiled = 1;
360 result = regexec(®ex, password, 0, NULL, 0);
361 }
362 if (result) {
363 char regex_error[256];
364 regerror(result, ®ex, regex_error, sizeof regex_error);
365 pam_syslog(pamh, LOG_CRIT, "PIN regex didn't match: %s",
366 regex_error);
367 if (1 == regex_compiled) {
368 regfree(®ex);
369 }
370 prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("Invalid PIN"));
371 goto err;
372 }
373 regfree(®ex);
374 }
375
376 if (0 != PKCS11_login(slot, 0, password)) {
377 if (slot->token->userPinLocked) {
378 prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("PIN not verified; PIN locked"));
379 } else if (slot->token->userPinFinalTry) {
380 prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("PIN not verified; one try remaining"));
381 } else {
382 prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("PIN not verified"));
383 }
384 goto err;
385 }
386
387 pam_set_item(pamh, PAM_AUTHTOK, password);
388
389 ok = 1;
390
391 err:
392 if (NULL != password) {
393 OPENSSL_cleanse(password, strlen(password));
394 free(password);
395 }
396
397 return ok;
398 }
399
key_change_login(pam_handle_t * pamh,int flags,PKCS11_SLOT * slot,const char * pin_regex)400 static int key_change_login(pam_handle_t *pamh, int flags, PKCS11_SLOT *slot, const char *pin_regex)
401 {
402 char *old = NULL, *new = NULL, *retyped = NULL;
403 int ok;
404
405 if (0 == slot->token->loginRequired) {
406 /* we can't change a PIN */
407 ok = 0;
408 goto err;
409 }
410 ok = 0;
411
412 /* We need a R/W public session to change the PIN via PUK or
413 * a R/W user session to change the PIN via PIN */
414 if (0 != PKCS11_open_session(slot, 1)
415 || (0 == slot->token->userPinLocked
416 && 1 != key_login(pamh, flags, slot, pin_regex))) {
417 goto err;
418 }
419
420 /* prompt for new PIN (and PUK if needed) */
421 if (0 != slot->token->secureLogin) {
422 if (0 != slot->token->userPinLocked) {
423 prompt(flags, pamh, PAM_TEXT_INFO, NULL,
424 _("Change PIN with PUK on PIN pad for %s"),
425 slot->token->label);
426 } else {
427 prompt(flags, pamh, PAM_TEXT_INFO, NULL,
428 _("Change PIN on PIN pad for %s"),
429 slot->token->label);
430 }
431 } else {
432 if (0 != slot->token->userPinLocked) {
433 if (PAM_SUCCESS == prompt(flags, pamh,
434 PAM_PROMPT_ECHO_OFF, &old,
435 _("PUK for %s: "),
436 slot->token->label)) {
437 goto err;
438 }
439 } else {
440 #ifdef TEST
441 /* In the tests we're calling pam_sm_chauthtok() directly, so
442 * pam_get_item(PAM_AUTHTOK) will return PAM_BAD_ITEM. As
443 * workaround, we simply enter the current PIN twice. */
444 if (PAM_SUCCESS != prompt(flags, pamh,
445 PAM_PROMPT_ECHO_OFF, &old,
446 _("Current PIN: "))) {
447 goto err;
448 }
449 #else
450 if (PAM_SUCCESS != pam_get_item(pamh, PAM_AUTHTOK, (void *)&old)
451 || NULL == old) {
452 goto err;
453 }
454 old = strdup(old);
455 if (NULL == old) {
456 pam_syslog(pamh, LOG_CRIT, "strdup() failed: %s",
457 strerror(errno));
458 goto err;
459 }
460 #endif
461 }
462 if (PAM_SUCCESS != prompt(flags, pamh,
463 PAM_PROMPT_ECHO_OFF, &new,
464 _("Enter new PIN: "))
465 || PAM_SUCCESS != prompt(flags, pamh,
466 PAM_PROMPT_ECHO_OFF, &retyped,
467 _("Retype new PIN: "))) {
468 goto err;
469 }
470 if (0 != strcmp(new, retyped)) {
471 prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("PINs don't match"));
472 goto err;
473 }
474 }
475
476 if (0 != PKCS11_change_pin(slot, old, new)) {
477 if (slot->token->userPinLocked) {
478 prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("PIN not changed; PIN locked"));
479 } else if (slot->token->userPinFinalTry) {
480 prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("PIN not changed; one try remaining"));
481 } else {
482 prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("PIN not changed"));
483 }
484 goto err;
485 }
486
487 pam_set_item(pamh, PAM_AUTHTOK, new);
488
489 ok = 1;
490
491 err:
492 if (NULL != retyped) {
493 OPENSSL_cleanse(retyped, strlen(retyped));
494 free(retyped);
495 }
496 if (NULL != new) {
497 OPENSSL_cleanse(new, strlen(new));
498 free(new);
499 }
500 if (NULL != old) {
501 OPENSSL_cleanse(old, strlen(old));
502 free(old);
503 }
504
505 return ok;
506 }
507
key_find(pam_handle_t * pamh,int flags,const char * user,PKCS11_CTX * ctx,PKCS11_SLOT * slots,unsigned int nslots,PKCS11_SLOT ** authslot,PKCS11_KEY ** authkey)508 static int key_find(pam_handle_t *pamh, int flags, const char *user,
509 PKCS11_CTX *ctx, PKCS11_SLOT *slots, unsigned int nslots,
510 PKCS11_SLOT **authslot, PKCS11_KEY **authkey)
511 {
512 int token_found = 0;
513
514 if (NULL == authslot || NULL == authkey) {
515 return 0;
516 }
517
518 *authkey = NULL;
519 *authslot = NULL;
520
521 /* search all valuable slots for a key that is authorized by the user */
522 while (0 < nslots) {
523 PKCS11_SLOT *slot = NULL;
524 PKCS11_CERT *certs = NULL;
525 #ifdef HAVE_PKCS11_ENUMERATE_PUBLIC_KEYS
526 PKCS11_KEY *keys = NULL;
527 #endif
528 unsigned int count = 0;
529
530 slot = PKCS11_find_token(ctx, slots, nslots);
531 if (NULL == slot || NULL == slot->token) {
532 break;
533 }
534 token_found = 1;
535
536 if (slot->token->loginRequired && slot->token->userPinLocked) {
537 pam_syslog(pamh, LOG_DEBUG, "%s: PIN locked",
538 slot->token->label);
539 continue;
540 }
541 pam_syslog(pamh, LOG_DEBUG, "Searching %s for keys",
542 slot->token->label);
543
544 #ifdef HAVE_PKCS11_ENUMERATE_PUBLIC_KEYS
545 /* First, search all available public keys to allow using tokens
546 * without certificates (e.g. OpenPGP card) */
547 if (0 == PKCS11_enumerate_public_keys(slot->token, &keys, &count)) {
548 while (0 < count && NULL != keys) {
549 EVP_PKEY *pubkey = PKCS11_get_public_key(keys);
550 int r = match_user_opensc(pubkey, user);
551 if (1 != r) {
552 r = match_user_openssh(pubkey, user);
553 }
554 if (NULL != pubkey) {
555 EVP_PKEY_free(pubkey);
556 }
557 if (1 == r) {
558 *authkey = keys;
559 *authslot = slot;
560 pam_syslog(pamh, LOG_DEBUG, "Found %s",
561 keys->label);
562 return 1;
563 }
564
565 /* Try the next possible public key */
566 keys++;
567 count--;
568 }
569 }
570 #endif
571
572 /* Next, search all certificates */
573 if (0 == PKCS11_enumerate_certs(slot->token, &certs, &count)) {
574 while (0 < count && NULL != certs) {
575 EVP_PKEY *pubkey = X509_get_pubkey(certs->x509);
576 int r = match_user_opensc(pubkey, user);
577 if (1 != r) {
578 r = match_user_openssh(pubkey, user);
579 }
580 if (NULL != pubkey) {
581 EVP_PKEY_free(pubkey);
582 }
583 if (1 == r) {
584 *authkey = PKCS11_find_key(certs);
585 if (NULL == *authkey) {
586 continue;
587 }
588 *authslot = slot;
589 pam_syslog(pamh, LOG_DEBUG, "Found %s",
590 certs->label);
591 return 1;
592 }
593
594 /* Try the next possible certificate */
595 certs++;
596 count--;
597 }
598 }
599
600 /* Try the next possible slot: PKCS11 slots are implemented as array,
601 * so starting to look at slot++ and decrementing nslots accordingly
602 * will search the rest of slots. */
603 slot++;
604 nslots -= (slot - slots);
605 slots = slot;
606 pam_syslog(pamh, LOG_DEBUG, "No authorized key found");
607 }
608
609 if (0 == token_found) {
610 prompt(flags, pamh, PAM_ERROR_MSG , NULL, _("No token found"));
611 } else {
612 prompt(flags, pamh, PAM_ERROR_MSG , NULL, _("No authorized keys on token"));
613 }
614
615 return 0;
616 }
617
randomize(pam_handle_t * pamh,unsigned char * r,unsigned int r_len)618 static int randomize(pam_handle_t *pamh, unsigned char *r, unsigned int r_len)
619 {
620 int ok = 0;
621 int fd = open("/dev/urandom", O_RDONLY);
622 if (0 <= fd && read(fd, r, r_len) == (ssize_t)r_len) {
623 ok = 1;
624 } else {
625 pam_syslog(pamh, LOG_CRIT, "Error reading from /dev/urandom: %s",
626 strerror(errno));
627 }
628 if (0 <= fd) {
629 close(fd);
630 }
631 return ok;
632 }
633
key_verify(pam_handle_t * pamh,int flags,PKCS11_KEY * authkey)634 static int key_verify(pam_handle_t *pamh, int flags, PKCS11_KEY *authkey)
635 {
636 int ok = 0;
637 unsigned char challenge[30];
638 unsigned char *signature = NULL;
639 unsigned int siglen;
640 const EVP_MD *md = EVP_sha1();
641 EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
642 EVP_PKEY *privkey = PKCS11_get_private_key(authkey);
643 EVP_PKEY *pubkey = PKCS11_get_public_key(authkey);
644
645 if (NULL == privkey)
646 goto err;
647 siglen = EVP_PKEY_size(privkey);
648 if (siglen <= 0)
649 goto err;
650 signature = malloc(siglen);
651 if (NULL == signature)
652 goto err;
653
654 /* Verify a SHA-1 hash of random data, signed by the key.
655 *
656 * Note that this will not work keys that aren't eligible for signing.
657 * Unfortunately, libp11 currently has no way of checking
658 * C_GetAttributeValue(CKA_SIGN), see
659 * https://github.com/OpenSC/libp11/issues/219. Since we don't want to
660 * implement try and error, we live with this limitation */
661 if (1 != randomize(pamh, challenge, sizeof challenge)) {
662 goto err;
663 }
664 if (NULL == pubkey || NULL == privkey || NULL == md_ctx || NULL == md
665 || !EVP_SignInit(md_ctx, md)
666 || !EVP_SignUpdate(md_ctx, challenge, sizeof challenge)
667 || !EVP_SignFinal(md_ctx, signature, &siglen, privkey)
668 || !EVP_MD_CTX_reset(md_ctx)
669 || !EVP_VerifyInit(md_ctx, md)
670 || !EVP_VerifyUpdate(md_ctx, challenge, sizeof challenge)
671 || 1 != EVP_VerifyFinal(md_ctx, signature, siglen, pubkey)) {
672 pam_syslog(pamh, LOG_DEBUG, "Error verifying key: %s\n",
673 ERR_reason_error_string(ERR_get_error()));
674 prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("Error verifying key"));
675 goto err;
676 }
677 ok = 1;
678
679 err:
680 free(signature);
681 if (NULL != pubkey)
682 EVP_PKEY_free(pubkey);
683 if (NULL != privkey)
684 EVP_PKEY_free(privkey);
685 if (NULL != md_ctx) {
686 EVP_MD_CTX_free(md_ctx);
687 }
688 return ok;
689 }
690
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)691 PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc,
692 const char **argv)
693 {
694 int r;
695 PKCS11_CTX *ctx;
696 unsigned int nslots;
697 PKCS11_KEY *authkey;
698 PKCS11_SLOT *slots, *authslot;
699 const char *user;
700 const char *pin_regex;
701
702 r = module_refresh(pamh, flags, argc, argv,
703 &user, &ctx, &slots, &nslots, &pin_regex);
704 if (PAM_SUCCESS != r) {
705 goto err;
706 }
707
708 if (1 != key_find(pamh, flags, user, ctx, slots, nslots,
709 &authslot, &authkey)) {
710 r = PAM_AUTHINFO_UNAVAIL;
711 goto err;
712 }
713 if (1 != key_login(pamh, flags, authslot, pin_regex)
714 || 1 != key_verify(pamh, flags, authkey)) {
715 if (authslot->token->userPinLocked) {
716 r = PAM_MAXTRIES;
717 } else {
718 r = PAM_AUTH_ERR;
719 }
720 goto err;
721 }
722
723 r = PAM_SUCCESS;
724
725 err:
726 #ifdef TEST
727 module_data_cleanup(pamh, global_module_data, r);
728 #endif
729 return r;
730 }
731
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)732 PAM_EXTERN int pam_sm_setcred(pam_handle_t * pamh, int flags, int argc,
733 const char **argv)
734 {
735 /* Actually, we should return the same value as pam_sm_authenticate(). */
736 return PAM_SUCCESS;
737 }
738
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv)739 PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc,
740 const char **argv)
741 {
742 /* if the user has been authenticated (precondition of this call), then
743 * everything is OK. Yes, we explicitly don't want to check CRLs, OCSP or
744 * exparation of certificates (use pam_pkcs11 for this). */
745 return PAM_SUCCESS;
746 }
747
pam_sm_open_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)748 PAM_EXTERN int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc,
749 const char **argv)
750 {
751 pam_syslog(pamh, LOG_DEBUG,
752 "Function pam_sm_open_session() is not implemented in this module");
753 return PAM_SERVICE_ERR;
754 }
755
pam_sm_close_session(pam_handle_t * pamh,int flags,int argc,const char ** argv)756 PAM_EXTERN int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc,
757 const char **argv)
758 {
759 pam_syslog(pamh, LOG_DEBUG,
760 "Function pam_sm_close_session() is not implemented in this module");
761 return PAM_SERVICE_ERR;
762 }
763
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)764 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
765 const char **argv)
766 {
767 int r;
768 PKCS11_CTX *ctx;
769 unsigned int nslots;
770 PKCS11_KEY *authkey;
771 PKCS11_SLOT *slots, *authslot;
772 const char *user, *pin_regex;
773
774 r = module_refresh(pamh, flags, argc, argv,
775 &user, &ctx, &slots, &nslots, &pin_regex);
776 if (PAM_SUCCESS != r) {
777 goto err;
778 }
779
780 if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) {
781 /* Yes, we explicitly don't want to check CRLs, OCSP or exparation of
782 * certificates (use pam_pkcs11 for this). */
783 r = PAM_SUCCESS;
784 goto err;
785 }
786
787 if (1 != key_find(pamh, flags, user, ctx, slots, nslots,
788 &authslot, &authkey)) {
789 r = PAM_AUTHINFO_UNAVAIL;
790 goto err;
791 }
792
793 if (flags & PAM_PRELIM_CHECK) {
794 r = PAM_TRY_AGAIN;
795 goto err;
796 }
797
798 if (flags & PAM_UPDATE_AUTHTOK) {
799 if (1 != key_change_login(pamh, flags, authslot, pin_regex)) {
800 if (authslot->token->userPinLocked) {
801 r = PAM_MAXTRIES;
802 } else {
803 r = PAM_AUTH_ERR;
804 }
805 goto err;
806 }
807 }
808
809 r = PAM_SUCCESS;
810
811 err:
812 #ifdef TEST
813 module_data_cleanup(pamh, global_module_data, r);
814 #endif
815 return r;
816 }
817
818 #ifdef PAM_STATIC
819 /* static module data */
820 struct pam_module _pam_group_modstruct = {
821 PACKAGE,
822 pam_sm_authenticate,
823 pam_sm_setcred,
824 pam_sm_acct_mgmt,
825 pam_sm_open_session,
826 pam_sm_close_session,
827 pam_sm_chauthtok
828 };
829 #endif
830