Line data Source code
1 : /*
2 : SSSD
3 :
4 : PAM Responder - certificate realted requests
5 :
6 : Copyright (C) Sumit Bose <sbose@redhat.com> 2015
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include <time.h>
23 :
24 : #include "util/util.h"
25 : #include "providers/data_provider.h"
26 : #include "util/child_common.h"
27 : #include "util/strtonum.h"
28 : #include "responder/pam/pamsrv.h"
29 :
30 :
31 : #ifndef SSSD_LIBEXEC_PATH
32 : #error "SSSD_LIBEXEC_PATH not defined"
33 : #endif /* SSSD_LIBEXEC_PATH */
34 :
35 : #define P11_CHILD_LOG_FILE "p11_child"
36 : #define P11_CHILD_PATH SSSD_LIBEXEC_PATH"/p11_child"
37 :
38 0 : errno_t p11_child_init(struct pam_ctx *pctx)
39 : {
40 0 : return child_debug_init(P11_CHILD_LOG_FILE, &pctx->p11_child_debug_fd);
41 : }
42 :
43 61 : bool may_do_cert_auth(struct pam_ctx *pctx, struct pam_data *pd)
44 : {
45 : size_t c;
46 61 : const char *sc_services[] = { "login", "su", "su-l", "gdm-smartcard",
47 : "gdm-password", "kdm", "sudo", "sudo-i",
48 : NULL };
49 61 : if (!pctx->cert_auth) {
50 45 : return false;
51 : }
52 :
53 16 : if (pd->cmd != SSS_PAM_PREAUTH && pd->cmd != SSS_PAM_AUTHENTICATE) {
54 0 : return false;
55 : }
56 :
57 16 : if (pd->cmd == SSS_PAM_AUTHENTICATE
58 2 : && sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_SC_PIN
59 0 : && sss_authtok_get_type(pd->authtok) != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
60 0 : return false;
61 : }
62 :
63 : /* TODO: make services configurable */
64 16 : if (pd->service == NULL || *pd->service == '\0') {
65 0 : return false;
66 : }
67 16 : for (c = 0; sc_services[c] != NULL; c++) {
68 16 : if (strcmp(pd->service, sc_services[c]) == 0) {
69 16 : break;
70 : }
71 : }
72 16 : if (sc_services[c] == NULL) {
73 0 : DEBUG(SSSDBG_CRIT_FAILURE,
74 : "Smartcard authentication for service [%s] not supported.\n",
75 : pd->service);
76 0 : return false;
77 : }
78 :
79 16 : return true;
80 : }
81 :
82 1 : static errno_t get_p11_child_write_buffer(TALLOC_CTX *mem_ctx,
83 : struct pam_data *pd,
84 : uint8_t **_buf, size_t *_len)
85 : {
86 : int ret;
87 : uint8_t *buf;
88 : size_t len;
89 1 : const char *pin = NULL;
90 :
91 1 : if (pd == NULL || pd->authtok == NULL) {
92 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing authtok.\n");
93 0 : return EINVAL;
94 : }
95 :
96 1 : switch (sss_authtok_get_type(pd->authtok)) {
97 : case SSS_AUTHTOK_TYPE_SC_PIN:
98 1 : ret = sss_authtok_get_sc_pin(pd->authtok, &pin, &len);
99 1 : if (ret != EOK) {
100 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc_pin failed.\n");
101 0 : return ret;
102 : }
103 1 : if (pin == NULL || len == 0) {
104 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing PIN.\n");
105 0 : return EINVAL;
106 : }
107 :
108 1 : buf = talloc_size(mem_ctx, len);
109 1 : if (buf == NULL) {
110 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
111 0 : return ENOMEM;
112 : }
113 :
114 1 : safealign_memcpy(buf, pin, len, NULL);
115 :
116 1 : break;
117 : case SSS_AUTHTOK_TYPE_SC_KEYPAD:
118 : /* Nothing to send */
119 0 : len = 0;
120 0 : buf = NULL;
121 0 : break;
122 : default:
123 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported authtok type [%d].\n",
124 : sss_authtok_get_type(pd->authtok));
125 0 : return EINVAL;
126 : }
127 :
128 1 : *_len = len;
129 1 : *_buf = buf;
130 :
131 1 : return EOK;
132 : }
133 :
134 8 : static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
135 : ssize_t buf_len, char **_cert,
136 : char **_token_name)
137 : {
138 : int ret;
139 8 : TALLOC_CTX *tmp_ctx = NULL;
140 : uint8_t *p;
141 : uint8_t *pn;
142 8 : char *cert = NULL;
143 8 : char *token_name = NULL;
144 :
145 8 : if (buf_len < 0) {
146 0 : DEBUG(SSSDBG_CRIT_FAILURE,
147 : "Error occured while reading data from p11_child.\n");
148 0 : return EIO;
149 : }
150 :
151 8 : if (buf_len == 0) {
152 2 : DEBUG(SSSDBG_TRACE_LIBS, "No certificate found.\n");
153 2 : ret = EOK;
154 2 : goto done;
155 : }
156 :
157 6 : p = memchr(buf, '\n', buf_len);
158 6 : if (p == NULL) {
159 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing new-line in p11_child response.\n");
160 0 : return EINVAL;
161 : }
162 6 : if (p == buf) {
163 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing counter in p11_child response.\n");
164 0 : return EINVAL;
165 : }
166 :
167 6 : tmp_ctx = talloc_new(NULL);
168 6 : if (tmp_ctx == NULL) {
169 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
170 0 : return ENOMEM;
171 : }
172 :
173 6 : token_name = talloc_strndup(tmp_ctx, (char*) buf, (p - buf));
174 6 : if (token_name == NULL) {
175 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
176 0 : ret = ENOMEM;
177 0 : goto done;
178 : }
179 :
180 6 : p++;
181 6 : pn = memchr(p, '\n', buf_len - (p - buf));
182 6 : if (pn == NULL) {
183 0 : DEBUG(SSSDBG_OP_FAILURE,
184 : "Missing new-line in p11_child response.\n");
185 0 : ret = EINVAL;
186 0 : goto done;
187 : }
188 :
189 6 : if (pn == p) {
190 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing cert in p11_child response.\n");
191 0 : ret = EINVAL;
192 0 : goto done;
193 : }
194 :
195 6 : cert = talloc_strndup(tmp_ctx, (char *) p, (pn - p));
196 6 : if(cert == NULL) {
197 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strndup failed.\n");
198 0 : ret = ENOMEM;
199 0 : goto done;
200 : }
201 6 : DEBUG(SSSDBG_TRACE_ALL, "Found cert [%s].\n", cert);
202 :
203 6 : ret = EOK;
204 :
205 : done:
206 8 : if (ret == EOK) {
207 8 : *_token_name = talloc_steal(mem_ctx, token_name);
208 8 : *_cert = talloc_steal(mem_ctx, cert);
209 : }
210 :
211 8 : talloc_free(tmp_ctx);
212 :
213 8 : return ret;
214 : }
215 :
216 : struct pam_check_cert_state {
217 : int child_status;
218 : struct sss_child_ctx_old *child_ctx;
219 : struct tevent_timer *timeout_handler;
220 : struct tevent_context *ev;
221 :
222 : int write_to_child_fd;
223 : int read_from_child_fd;
224 : char *cert;
225 : char *token_name;
226 : };
227 :
228 : static void p11_child_write_done(struct tevent_req *subreq);
229 : static void p11_child_done(struct tevent_req *subreq);
230 : static void p11_child_timeout(struct tevent_context *ev,
231 : struct tevent_timer *te,
232 : struct timeval tv, void *pvt);
233 :
234 8 : struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
235 : struct tevent_context *ev,
236 : int child_debug_fd,
237 : const char *nss_db,
238 : time_t timeout,
239 : struct pam_data *pd)
240 : {
241 : errno_t ret;
242 : struct tevent_req *req;
243 : struct tevent_req *subreq;
244 : struct pam_check_cert_state *state;
245 : pid_t child_pid;
246 : struct timeval tv;
247 : int pipefd_to_child[2];
248 : int pipefd_from_child[2];
249 8 : const char *extra_args[5] = {NULL, NULL, NULL, NULL, NULL};
250 8 : uint8_t *write_buf = NULL;
251 8 : size_t write_buf_len = 0;
252 :
253 8 : req = tevent_req_create(mem_ctx, &state, struct pam_check_cert_state);
254 8 : if (req == NULL) {
255 0 : return NULL;
256 : }
257 :
258 8 : if (nss_db == NULL) {
259 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing NSS DB.\n");
260 0 : ret = EINVAL;
261 0 : goto done;
262 : }
263 :
264 : /* extra_args are added in revers order */
265 8 : extra_args[1] = "--nssdb";
266 8 : extra_args[0] = nss_db;
267 8 : if (pd->cmd == SSS_PAM_AUTHENTICATE) {
268 1 : extra_args[2] = "--auth";
269 1 : switch (sss_authtok_get_type(pd->authtok)) {
270 : case SSS_AUTHTOK_TYPE_SC_PIN:
271 1 : extra_args[3] = "--pin";
272 1 : break;
273 : case SSS_AUTHTOK_TYPE_SC_KEYPAD:
274 0 : extra_args[3] = "--keypad";
275 0 : break;
276 : default:
277 0 : DEBUG(SSSDBG_OP_FAILURE, "Unsupported authtok type.\n");
278 0 : ret = EINVAL;
279 0 : goto done;
280 : }
281 7 : } else if (pd->cmd == SSS_PAM_PREAUTH) {
282 7 : extra_args[2] = "--pre";
283 : } else {
284 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected PAM command [%d}.\n", pd->cmd);
285 0 : ret = EINVAL;
286 0 : goto done;
287 : }
288 :
289 8 : state->ev = ev;
290 8 : state->child_status = EFAULT;
291 8 : state->read_from_child_fd = -1;
292 8 : state->write_to_child_fd = -1;
293 8 : state->cert = NULL;
294 8 : state->token_name = NULL;
295 :
296 8 : ret = pipe(pipefd_from_child);
297 8 : if (ret == -1) {
298 0 : ret = errno;
299 0 : DEBUG(SSSDBG_CRIT_FAILURE,
300 : "pipe failed [%d][%s].\n", ret, strerror(ret));
301 0 : goto done;
302 : }
303 8 : ret = pipe(pipefd_to_child);
304 8 : if (ret == -1) {
305 0 : ret = errno;
306 0 : DEBUG(SSSDBG_CRIT_FAILURE,
307 : "pipe failed [%d][%s].\n", ret, strerror(ret));
308 0 : goto done;
309 : }
310 :
311 8 : if (child_debug_fd == -1) {
312 0 : child_debug_fd = STDERR_FILENO;
313 : }
314 :
315 8 : child_pid = fork();
316 16 : if (child_pid == 0) { /* child */
317 8 : ret = exec_child_ex(state, pipefd_to_child, pipefd_from_child,
318 : P11_CHILD_PATH, child_debug_fd, extra_args,
319 : STDIN_FILENO, STDOUT_FILENO);
320 0 : if (ret != EOK) {
321 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec p11 child: [%d][%s].\n",
322 : ret, strerror(ret));
323 0 : goto done;
324 : }
325 8 : } else if (child_pid > 0) { /* parent */
326 :
327 8 : state->read_from_child_fd = pipefd_from_child[0];
328 8 : close(pipefd_from_child[1]);
329 8 : sss_fd_nonblocking(state->read_from_child_fd);
330 :
331 8 : state->write_to_child_fd = pipefd_to_child[1];
332 8 : close(pipefd_to_child[0]);
333 8 : sss_fd_nonblocking(state->write_to_child_fd);
334 :
335 : /* Set up SIGCHLD handler */
336 8 : ret = child_handler_setup(ev, child_pid, NULL, NULL, &state->child_ctx);
337 8 : if (ret != EOK) {
338 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
339 : ret, sss_strerror(ret));
340 0 : ret = ERR_P11_CHILD;
341 0 : goto done;
342 : }
343 :
344 : /* Set up timeout handler */
345 8 : tv = tevent_timeval_current_ofs(timeout, 0);
346 8 : state->timeout_handler = tevent_add_timer(ev, req, tv,
347 : p11_child_timeout, req);
348 8 : if(state->timeout_handler == NULL) {
349 0 : ret = ERR_P11_CHILD;
350 0 : goto done;
351 : }
352 :
353 8 : if (pd->cmd == SSS_PAM_AUTHENTICATE) {
354 1 : ret = get_p11_child_write_buffer(state, pd, &write_buf,
355 : &write_buf_len);
356 1 : if (ret != EOK) {
357 0 : DEBUG(SSSDBG_OP_FAILURE,
358 : "get_p11_child_write_buffer failed.\n");
359 0 : goto done;
360 : }
361 : }
362 :
363 8 : if (write_buf_len != 0) {
364 1 : subreq = write_pipe_send(state, ev, write_buf, write_buf_len,
365 1 : state->write_to_child_fd);
366 1 : if (subreq == NULL) {
367 0 : DEBUG(SSSDBG_OP_FAILURE, "write_pipe_send failed.\n");
368 0 : ret = ERR_P11_CHILD;
369 0 : goto done;
370 : }
371 1 : tevent_req_set_callback(subreq, p11_child_write_done, req);
372 : } else {
373 7 : subreq = read_pipe_send(state, ev, state->read_from_child_fd);
374 7 : if (subreq == NULL) {
375 0 : DEBUG(SSSDBG_OP_FAILURE, "read_pipe_send failed.\n");
376 0 : ret = ERR_P11_CHILD;
377 0 : goto done;
378 : }
379 7 : tevent_req_set_callback(subreq, p11_child_done, req);
380 : }
381 :
382 : /* Now either wait for the timeout to fire or the child
383 : * to finish
384 : */
385 : } else { /* error */
386 0 : ret = errno;
387 0 : DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n",
388 : ret, sss_strerror(ret));
389 0 : goto done;
390 : }
391 :
392 8 : ret = EOK;
393 :
394 : done:
395 8 : if (ret != EOK) {
396 0 : tevent_req_error(req, ret);
397 0 : tevent_req_post(req, ev);
398 : }
399 8 : return req;
400 : }
401 :
402 1 : static void p11_child_write_done(struct tevent_req *subreq)
403 : {
404 1 : struct tevent_req *req = tevent_req_callback_data(subreq,
405 : struct tevent_req);
406 1 : struct pam_check_cert_state *state = tevent_req_data(req,
407 : struct pam_check_cert_state);
408 : int ret;
409 :
410 1 : ret = write_pipe_recv(subreq);
411 1 : talloc_zfree(subreq);
412 1 : if (ret != EOK) {
413 0 : tevent_req_error(req, ret);
414 0 : return;
415 : }
416 :
417 1 : close(state->write_to_child_fd);
418 1 : state->write_to_child_fd = -1;
419 :
420 1 : subreq = read_pipe_send(state, state->ev, state->read_from_child_fd);
421 1 : if (subreq == NULL) {
422 0 : tevent_req_error(req, ENOMEM);
423 0 : return;
424 : }
425 1 : tevent_req_set_callback(subreq, p11_child_done, req);
426 : }
427 :
428 8 : static void p11_child_done(struct tevent_req *subreq)
429 : {
430 : uint8_t *buf;
431 : ssize_t buf_len;
432 8 : struct tevent_req *req = tevent_req_callback_data(subreq,
433 : struct tevent_req);
434 8 : struct pam_check_cert_state *state = tevent_req_data(req,
435 : struct pam_check_cert_state);
436 : int ret;
437 :
438 8 : talloc_zfree(state->timeout_handler);
439 :
440 8 : ret = read_pipe_recv(subreq, state, &buf, &buf_len);
441 8 : talloc_zfree(subreq);
442 8 : if (ret != EOK) {
443 0 : tevent_req_error(req, ret);
444 0 : return;
445 : }
446 :
447 8 : close(state->read_from_child_fd);
448 8 : state->read_from_child_fd = -1;
449 :
450 8 : ret = parse_p11_child_response(state, buf, buf_len, &state->cert,
451 : &state->token_name);
452 8 : if (ret != EOK) {
453 0 : DEBUG(SSSDBG_OP_FAILURE, "parse_p11_child_respose failed.\n");
454 0 : tevent_req_error(req, ret);
455 0 : return;
456 : }
457 :
458 8 : tevent_req_done(req);
459 8 : return;
460 : }
461 :
462 0 : static void p11_child_timeout(struct tevent_context *ev,
463 : struct tevent_timer *te,
464 : struct timeval tv, void *pvt)
465 : {
466 0 : struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
467 0 : struct pam_check_cert_state *state =
468 0 : tevent_req_data(req, struct pam_check_cert_state);
469 :
470 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Timeout reached for p11_child.\n");
471 0 : child_handler_destroy(state->child_ctx);
472 0 : state->child_ctx = NULL;
473 0 : state->child_status = ETIMEDOUT;
474 0 : tevent_req_error(req, ERR_P11_CHILD);
475 0 : }
476 :
477 8 : errno_t pam_check_cert_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
478 : char **cert, char **token_name)
479 : {
480 8 : struct pam_check_cert_state *state =
481 8 : tevent_req_data(req, struct pam_check_cert_state);
482 :
483 8 : TEVENT_REQ_RETURN_ON_ERROR(req);
484 :
485 8 : if (cert != NULL) {
486 8 : *cert = talloc_steal(mem_ctx, state->cert);
487 : }
488 :
489 8 : if (token_name != NULL) {
490 8 : *token_name = talloc_steal(mem_ctx, state->token_name);
491 : }
492 :
493 8 : return EOK;
494 : }
495 :
496 2 : errno_t add_pam_cert_response(struct pam_data *pd, const char *user,
497 : const char *token_name)
498 : {
499 2 : uint8_t *msg = NULL;
500 : size_t user_len;
501 : size_t msg_len;
502 : size_t slot_len;
503 : int ret;
504 :
505 2 : if (user == NULL || token_name == NULL) {
506 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing mandatory user or slot name.\n");
507 0 : return EINVAL;
508 : }
509 :
510 2 : user_len = strlen(user) + 1;
511 2 : slot_len = strlen(token_name) + 1;
512 2 : msg_len = user_len + slot_len;
513 :
514 2 : msg = talloc_zero_size(pd, msg_len);
515 2 : if (msg == NULL) {
516 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_size failed.\n");
517 0 : return ENOMEM;
518 : }
519 :
520 2 : memcpy(msg, user, user_len);
521 2 : memcpy(msg + user_len, token_name, slot_len);
522 :
523 2 : ret = pam_add_response(pd, SSS_PAM_CERT_INFO, msg_len, msg);
524 2 : talloc_free(msg);
525 :
526 2 : return ret;
527 : }
|