LCOV - code coverage report
Current view: top level - responder/pam - pamsrv_p11.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 158 262 60.3 %
Date: 2015-10-19 Functions: 8 10 80.0 %

          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             : }

Generated by: LCOV version 1.10