LCOV - code coverage report
Current view: top level - providers/krb5 - krb5_wait_queue.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 121 153 79.1 %
Date: 2016-06-29 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     Kerberos 5 Backend Module - Serialize the request of a user
       5             : 
       6             :     Authors:
       7             :         Sumit Bose <sbose@redhat.com>
       8             : 
       9             :     Copyright (C) 2010 Red Hat
      10             : 
      11             :     This program is free software; you can redistribute it and/or modify
      12             :     it under the terms of the GNU General Public License as published by
      13             :     the Free Software Foundation; either version 3 of the License, or
      14             :     (at your option) any later version.
      15             : 
      16             :     This program is distributed in the hope that it will be useful,
      17             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19             :     GNU General Public License for more details.
      20             : 
      21             :     You should have received a copy of the GNU General Public License
      22             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      23             : */
      24             : 
      25             : #include <tevent.h>
      26             : #include <dhash.h>
      27             : 
      28             : #include <security/pam_modules.h>
      29             : 
      30             : #include "src/providers/krb5/krb5_auth.h"
      31             : 
      32             : #define INIT_HASH_SIZE 5
      33             : 
      34             : struct queue_entry {
      35             :     struct queue_entry *prev;
      36             :     struct queue_entry *next;
      37             : 
      38             :     struct be_ctx *be_ctx;
      39             :     struct be_req *be_req;
      40             :     struct tevent_req *parent_req;
      41             :     struct pam_data *pd;
      42             :     struct krb5_ctx *krb5_ctx;
      43             : };
      44             : 
      45             : static void wait_queue_auth_done(struct tevent_req *req);
      46             : 
      47             : static void krb5_auth_queue_finish(struct tevent_req *req, errno_t ret,
      48             :                                    int pam_status, int dp_err);
      49             : 
      50        1008 : static void wait_queue_auth(struct tevent_context *ev, struct tevent_timer *te,
      51             :                             struct timeval current_time, void *private_data)
      52             : {
      53        1008 :     struct queue_entry *qe = talloc_get_type(private_data, struct queue_entry);
      54             :     struct tevent_req *req;
      55             : 
      56        1008 :     req = krb5_auth_send(qe->parent_req, qe->be_ctx->ev,
      57             :                          qe->be_ctx, qe->pd, qe->krb5_ctx);
      58        1008 :     if (req == NULL) {
      59           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_auth_send failed.\n");
      60             :     } else {
      61        1008 :         tevent_req_set_callback(req, wait_queue_auth_done,
      62        1008 :                                 qe->parent_req);
      63             :     }
      64             : 
      65        1008 :     talloc_zfree(qe);
      66        1008 : }
      67             : 
      68        1008 : static void wait_queue_auth_done(struct tevent_req *req)
      69             : {
      70        1008 :     struct tevent_req *parent_req = \
      71        1008 :                 tevent_req_callback_data(req, struct tevent_req);
      72             :     int pam_status;
      73             :     int dp_err;
      74             :     errno_t ret;
      75             : 
      76        1008 :     ret = krb5_auth_recv(req, &pam_status, &dp_err);
      77        1008 :     talloc_zfree(req);
      78        1008 :     if (ret != EOK) {
      79           9 :         DEBUG(SSSDBG_OP_FAILURE, "krb5_auth_recv failed: %d\n", ret);
      80             :     }
      81             : 
      82        1008 :     krb5_auth_queue_finish(parent_req, ret, pam_status, dp_err);
      83        1008 : }
      84             : 
      85           3 : static void wait_queue_del_cb(hash_entry_t *entry, hash_destroy_enum type,
      86             :                               void *pvt)
      87             : {
      88             :     struct queue_entry *head;
      89             : 
      90           3 :     if (entry->value.type == HASH_VALUE_PTR) {
      91           3 :         head = talloc_get_type(entry->value.ptr, struct queue_entry);
      92           3 :         talloc_zfree(head);
      93           6 :         return;
      94             :     }
      95             : 
      96           0 :     DEBUG(SSSDBG_CRIT_FAILURE,
      97             :           "Unexpected value type [%d].\n", entry->value.type);
      98             : }
      99             : 
     100        1011 : static errno_t add_to_wait_queue(struct be_ctx *be_ctx,
     101             :                                  struct tevent_req *parent_req,
     102             :                                  struct pam_data *pd,
     103             :                                  struct krb5_ctx *krb5_ctx)
     104             : {
     105             :     int ret;
     106             :     hash_key_t key;
     107             :     hash_value_t value;
     108             :     struct queue_entry *head;
     109             :     struct queue_entry *queue_entry;
     110             : 
     111        1011 :     if (krb5_ctx->wait_queue_hash == NULL) {
     112           3 :         ret = sss_hash_create_ex(krb5_ctx, INIT_HASH_SIZE,
     113             :                                  &krb5_ctx->wait_queue_hash, 0, 0, 0, 0,
     114             :                                  wait_queue_del_cb, NULL);
     115           3 :         if (ret != EOK) {
     116           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "sss_hash_create failed\n");
     117           0 :             return ret;
     118             :         }
     119             :     }
     120             : 
     121        1011 :     key.type = HASH_KEY_STRING;
     122        1011 :     key.str = pd->user;
     123             : 
     124        1011 :     ret = hash_lookup(krb5_ctx->wait_queue_hash, &key, &value);
     125        1011 :     switch (ret) {
     126             :         case HASH_SUCCESS:
     127        1008 :             if (value.type != HASH_VALUE_PTR) {
     128           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected hash value type.\n");
     129           0 :                 return EINVAL;
     130             :             }
     131             : 
     132        1008 :             head = talloc_get_type(value.ptr, struct queue_entry);
     133             : 
     134        1008 :             queue_entry = talloc_zero(head, struct queue_entry);
     135        1008 :             if (queue_entry == NULL) {
     136           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
     137           0 :                 return ENOMEM;
     138             :             }
     139             : 
     140        1008 :             queue_entry->be_ctx = be_ctx;
     141        1008 :             queue_entry->parent_req = parent_req;
     142        1008 :             queue_entry->pd = pd;
     143        1008 :             queue_entry->krb5_ctx = krb5_ctx;
     144             : 
     145        1008 :             DLIST_ADD_END(head, queue_entry, struct queue_entry *);
     146             : 
     147        1008 :             break;
     148             :         case HASH_ERROR_KEY_NOT_FOUND:
     149           3 :             value.type = HASH_VALUE_PTR;
     150           3 :             head = talloc_zero(krb5_ctx->wait_queue_hash, struct queue_entry);
     151           3 :             if (head == NULL) {
     152           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
     153           0 :                 return ENOMEM;
     154             :             }
     155           3 :             value.ptr = head;
     156             : 
     157           3 :             ret = hash_enter(krb5_ctx->wait_queue_hash, &key, &value);
     158           3 :             if (ret != HASH_SUCCESS) {
     159           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "hash_enter failed.\n");
     160           0 :                 talloc_free(head);
     161           0 :                 return EIO;
     162             :             }
     163             : 
     164           3 :             break;
     165             :         default:
     166           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "hash_lookup failed.\n");
     167           0 :             return EIO;
     168             :     }
     169             : 
     170        1011 :     if (head->next == NULL) {
     171           3 :         return ENOENT;
     172             :     } else {
     173        1008 :         return EOK;
     174             :     }
     175             : }
     176             : 
     177        1011 : static void check_wait_queue(struct krb5_ctx *krb5_ctx, char *username)
     178             : {
     179             :     int ret;
     180             :     hash_key_t key;
     181             :     hash_value_t value;
     182             :     struct queue_entry *head;
     183             :     struct queue_entry *queue_entry;
     184             :     struct tevent_timer *te;
     185             : 
     186        1011 :     if (krb5_ctx->wait_queue_hash == NULL) {
     187           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "No wait queue available.\n");
     188           0 :         return;
     189             :     }
     190             : 
     191        1011 :     key.type = HASH_KEY_STRING;
     192        1011 :     key.str = username;
     193             : 
     194        1011 :     ret = hash_lookup(krb5_ctx->wait_queue_hash, &key, &value);
     195             : 
     196        1011 :     switch (ret) {
     197             :         case HASH_SUCCESS:
     198        1011 :             if (value.type != HASH_VALUE_PTR) {
     199           0 :                 DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected hash value type.\n");
     200           0 :                 return;
     201             :             }
     202             : 
     203        1011 :             head = talloc_get_type(value.ptr, struct queue_entry);
     204             : 
     205        1011 :             if (head->next == NULL) {
     206           3 :                 DEBUG(SSSDBG_TRACE_LIBS,
     207             :                       "Wait queue for user [%s] is empty.\n", username);
     208             :             } else {
     209        1008 :                 queue_entry = head->next;
     210             : 
     211        1008 :                 DLIST_REMOVE(head, queue_entry);
     212             : 
     213        1008 :                 te = tevent_add_timer(queue_entry->be_ctx->ev, krb5_ctx,
     214             :                                       tevent_timeval_current(), wait_queue_auth,
     215             :                                       queue_entry);
     216        1008 :                 if (te == NULL) {
     217           0 :                     DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer failed.\n");
     218             :                 } else {
     219        1008 :                     return;
     220             :                 }
     221             :             }
     222             : 
     223           3 :             ret = hash_delete(krb5_ctx->wait_queue_hash, &key);
     224           3 :             if (ret != HASH_SUCCESS) {
     225           0 :                 DEBUG(SSSDBG_CRIT_FAILURE,
     226             :                       "Failed to remove wait queue for user [%s].\n",
     227             :                           username);
     228             :             }
     229             : 
     230           3 :             break;
     231             :         case HASH_ERROR_KEY_NOT_FOUND:
     232           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     233             :                   "No wait queue for user [%s] found.\n", username);
     234           0 :             break;
     235             :         default:
     236           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "hash_lookup failed.\n");
     237             :     }
     238             : 
     239           3 :     return;
     240             : }
     241             : 
     242             : struct krb5_auth_queue_state {
     243             :     struct krb5_ctx *krb5_ctx;
     244             :     struct pam_data *pd;
     245             : 
     246             :     int pam_status;
     247             :     int dp_err;
     248             : };
     249             : 
     250             : static void krb5_auth_queue_done(struct tevent_req *subreq);
     251             : 
     252        1011 : struct tevent_req *krb5_auth_queue_send(TALLOC_CTX *mem_ctx,
     253             :                                         struct tevent_context *ev,
     254             :                                         struct be_ctx *be_ctx,
     255             :                                         struct pam_data *pd,
     256             :                                         struct krb5_ctx *krb5_ctx)
     257             : {
     258             :     errno_t ret;
     259             :     struct tevent_req *req;
     260             :     struct tevent_req *subreq;
     261             :     struct krb5_auth_queue_state *state;
     262             : 
     263        1011 :     req = tevent_req_create(mem_ctx, &state, struct krb5_auth_queue_state);
     264        1011 :     if (req == NULL) {
     265           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
     266           0 :         return NULL;
     267             :     }
     268        1011 :     state->krb5_ctx = krb5_ctx;
     269        1011 :     state->pd = pd;
     270             : 
     271        1011 :     ret = add_to_wait_queue(be_ctx, req, pd, krb5_ctx);
     272        1011 :     if (ret == EOK) {
     273        1008 :         DEBUG(SSSDBG_TRACE_LIBS,
     274             :               "Request [%p] successfully added to wait queue "
     275             :               "of user [%s].\n", req, pd->user);
     276        1008 :         ret = EOK;
     277        1008 :         goto immediate;
     278           3 :     } else if (ret == ENOENT) {
     279           3 :         DEBUG(SSSDBG_TRACE_LIBS, "Wait queue of user [%s] is empty, "
     280             :               "running request [%p] immediately.\n", pd->user, req);
     281             :     } else {
     282           0 :         DEBUG(SSSDBG_MINOR_FAILURE,
     283             :               "Failed to add request to wait queue of user [%s], "
     284             :               "running request [%p] immediately.\n", pd->user, req);
     285             :     }
     286             : 
     287           3 :     subreq = krb5_auth_send(req, ev, be_ctx, pd, krb5_ctx);
     288           3 :     if (req == NULL) {
     289           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "krb5_auth_send failed.\n");
     290           0 :         ret = ENOMEM;
     291           0 :         goto immediate;
     292             :     }
     293             : 
     294           3 :     tevent_req_set_callback(subreq, krb5_auth_queue_done, req);
     295             : 
     296           3 :     ret = EOK;
     297             : 
     298             : immediate:
     299        1011 :     if (ret != EOK) {
     300           0 :         tevent_req_error(req, ret);
     301           0 :         tevent_req_post(req, ev);
     302             :     }
     303        1011 :     return req;
     304             : }
     305             : 
     306           3 : static void krb5_auth_queue_done(struct tevent_req *subreq)
     307             : {
     308           3 :     struct tevent_req *req = \
     309           3 :                 tevent_req_callback_data(subreq, struct tevent_req);
     310           3 :     struct krb5_auth_queue_state *state = \
     311           3 :                 tevent_req_data(req, struct krb5_auth_queue_state);
     312             :     errno_t ret;
     313             : 
     314           3 :     ret = krb5_auth_recv(subreq, &state->pam_status, &state->dp_err);
     315           3 :     talloc_zfree(subreq);
     316             : 
     317           3 :     check_wait_queue(state->krb5_ctx, state->pd->user);
     318             : 
     319           3 :     if (ret != EOK) {
     320           1 :         DEBUG(SSSDBG_OP_FAILURE, "krb5_auth_recv failed with: %d\n", ret);
     321           1 :         tevent_req_error(req, ret);
     322           4 :         return;
     323             :     }
     324             : 
     325           2 :     DEBUG(SSSDBG_TRACE_LIBS, "krb5_auth_queue request [%p] done.\n", req);
     326           2 :     tevent_req_done(req);
     327             : }
     328             : 
     329             : /* This is a violation of the tevent_req style. Ideally, the wait queue would
     330             :  * be rewritten to the tevent_req style in the future, expose per-request recv
     331             :  * and not hide the request underneath. But this function allows us to expose
     332             :  * a tevent_req API for users of this module
     333             :  */
     334        1008 : static void krb5_auth_queue_finish(struct tevent_req *req,
     335             :                                    errno_t ret,
     336             :                                    int pam_status,
     337             :                                    int dp_err)
     338             : {
     339        1008 :     struct krb5_auth_queue_state *state = \
     340        1008 :                 tevent_req_data(req, struct krb5_auth_queue_state);
     341             : 
     342        1008 :     check_wait_queue(state->krb5_ctx, state->pd->user);
     343             : 
     344        1008 :     state->pam_status = pam_status;
     345        1008 :     state->dp_err = dp_err;
     346        1008 :     if (ret != EOK) {
     347           9 :         tevent_req_error(req, ret);
     348             :     } else {
     349         999 :         DEBUG(SSSDBG_TRACE_LIBS, "krb5_auth_queue request [%p] done.\n", req);
     350         999 :         tevent_req_done(req);
     351             :     }
     352        1008 : }
     353             : 
     354        1011 : int krb5_auth_queue_recv(struct tevent_req *req,
     355             :                          int *_pam_status,
     356             :                          int *_dp_err)
     357             : {
     358        1011 :     struct krb5_auth_queue_state *state = \
     359        1011 :                 tevent_req_data(req, struct krb5_auth_queue_state);
     360             : 
     361             :     /* Returning values even on failure is not typical, but IPA password migration
     362             :      * relies on receiving PAM_CRED_ERR even if the request fails..
     363             :      */
     364        1011 :     if (_pam_status) {
     365        1011 :         *_pam_status = state->pam_status;
     366             :     }
     367             : 
     368        1011 :     if (_dp_err) {
     369        1011 :         *_dp_err = state->dp_err;
     370             :     }
     371             : 
     372        1021 :     TEVENT_REQ_RETURN_ON_ERROR(req);
     373             : 
     374        1001 :     return EOK;
     375             : }

Generated by: LCOV version 1.10