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(&regex, pin_regex, REG_EXTENDED);
358 		if (0 == result) {
359 			regex_compiled = 1;
360 			result = regexec(&regex, password, 0, NULL, 0);
361 		}
362 		if (result) {
363 			char regex_error[256];
364 			regerror(result, &regex, 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(&regex);
369 			}
370 			prompt(flags, pamh, PAM_ERROR_MSG, NULL, _("Invalid PIN"));
371 			goto err;
372 		}
373 		regfree(&regex);
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