root/daemons/based/based_remote.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. remote_connection_destroy
  2. init_remote_listener
  3. check_group_membership
  4. cib_remote_auth
  5. remote_auth_timeout_cb
  6. cib_remote_listen
  7. cib_remote_connection_destroy
  8. cib_handle_remote_msg
  9. cib_remote_msg
  10. construct_pam_passwd
  11. authenticate_user

   1 /*
   2  * Copyright 2004-2025 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <crm/crm.h>
  12 
  13 #include <sys/param.h>
  14 #include <stdio.h>
  15 #include <sys/types.h>
  16 #include <sys/stat.h>
  17 #include <unistd.h>
  18 #include <inttypes.h>           // PRIx64
  19 #include <sys/socket.h>
  20 #include <arpa/inet.h>
  21 
  22 #include <netinet/ip.h>
  23 
  24 #include <stdlib.h>
  25 #include <errno.h>
  26 
  27 #include <glib.h>
  28 #include <libxml/tree.h>
  29 
  30 #include <crm/common/ipc.h>
  31 #include <crm/common/ipc_internal.h>
  32 #include <crm/common/xml.h>
  33 #include <crm/common/remote_internal.h>
  34 #include <crm/common/tls_internal.h>
  35 #include <crm/cib/internal.h>
  36 
  37 #include "pacemaker-based.h"
  38 
  39 #include <gnutls/gnutls.h>
  40 
  41 #include <pwd.h>
  42 #include <grp.h>
  43 #if HAVE_SECURITY_PAM_APPL_H
  44 #  include <security/pam_appl.h>
  45 #  define HAVE_PAM 1
  46 #elif HAVE_PAM_PAM_APPL_H
  47 #  include <pam/pam_appl.h>
  48 #  define HAVE_PAM 1
  49 #endif
  50 
  51 static pcmk__tls_t *tls = NULL;
  52 
  53 extern int remote_tls_fd;
  54 extern gboolean cib_shutdown_flag;
  55 
  56 int init_remote_listener(int port, gboolean encrypted);
  57 void cib_remote_connection_destroy(gpointer user_data);
  58 
  59 // @TODO This is rather short for someone to type their password
  60 #define REMOTE_AUTH_TIMEOUT 10000
  61 
  62 int num_clients;
  63 static bool authenticate_user(const char *user, const char *passwd);
  64 static int cib_remote_listen(gpointer data);
  65 static int cib_remote_msg(gpointer data);
  66 
  67 static void
  68 remote_connection_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  69 {
  70     crm_info("No longer listening for remote connections");
  71     return;
  72 }
  73 
  74 int
  75 init_remote_listener(int port, gboolean encrypted)
     /* [previous][next][first][last][top][bottom][index][help] */
  76 {
  77     int rc;
  78     int *ssock = NULL;
  79     struct sockaddr_in saddr;
  80     int optval;
  81 
  82     static struct mainloop_fd_callbacks remote_listen_fd_callbacks = {
  83         .dispatch = cib_remote_listen,
  84         .destroy = remote_connection_destroy,
  85     };
  86 
  87     if (port <= 0) {
  88         /* don't start it */
  89         return 0;
  90     }
  91 
  92     if (encrypted) {
  93         bool use_cert = pcmk__x509_enabled();
  94 
  95         crm_notice("Starting TLS listener on port %d", port);
  96 
  97         rc = pcmk__init_tls(&tls, true, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_ANON);
  98         if (rc != pcmk_rc_ok) {
  99             return -1;
 100         }
 101     } else {
 102         crm_warn("Starting plain-text listener on port %d", port);
 103     }
 104 #ifndef HAVE_PAM
 105     crm_warn("This build does not support remote administrators "
 106              "because PAM support is not available");
 107 #endif
 108 
 109     /* create server socket */
 110     ssock = pcmk__assert_alloc(1, sizeof(int));
 111     *ssock = socket(AF_INET, SOCK_STREAM, 0);
 112     if (*ssock == -1) {
 113         crm_err("Listener socket creation failed: %s", pcmk_rc_str(errno));
 114         free(ssock);
 115         return -1;
 116     }
 117 
 118     /* reuse address */
 119     optval = 1;
 120     rc = setsockopt(*ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
 121     if (rc < 0) {
 122         crm_err("Local address reuse not allowed on listener socket: %s",
 123                 pcmk_rc_str(errno));
 124     }
 125 
 126     /* bind server socket */
 127     memset(&saddr, '\0', sizeof(saddr));
 128     saddr.sin_family = AF_INET;
 129     saddr.sin_addr.s_addr = INADDR_ANY;
 130     saddr.sin_port = htons(port);
 131     if (bind(*ssock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
 132         crm_err("Cannot bind to listener socket: %s", pcmk_rc_str(errno));
 133         close(*ssock);
 134         free(ssock);
 135         return -2;
 136     }
 137     if (listen(*ssock, 10) == -1) {
 138         crm_err("Cannot listen on socket: %s", pcmk_rc_str(errno));
 139         close(*ssock);
 140         free(ssock);
 141         return -3;
 142     }
 143 
 144     mainloop_add_fd("cib-remote", G_PRIORITY_DEFAULT, *ssock, ssock, &remote_listen_fd_callbacks);
 145     crm_debug("Started listener on port %d", port);
 146 
 147     return *ssock;
 148 }
 149 
 150 static int
 151 check_group_membership(const char *usr, const char *grp)
     /* [previous][next][first][last][top][bottom][index][help] */
 152 {
 153     int index = 0;
 154     struct passwd *pwd = NULL;
 155     struct group *group = NULL;
 156 
 157     pwd = getpwnam(usr);
 158     if (pwd == NULL) {
 159         crm_notice("Rejecting remote client: '%s' is not a valid user", usr);
 160         return FALSE;
 161     }
 162 
 163     group = getgrgid(pwd->pw_gid);
 164     if (group != NULL && pcmk__str_eq(grp, group->gr_name, pcmk__str_none)) {
 165         return TRUE;
 166     }
 167 
 168     group = getgrnam(grp);
 169     if (group == NULL) {
 170         crm_err("Rejecting remote client: '%s' is not a valid group", grp);
 171         return FALSE;
 172     }
 173 
 174     while (TRUE) {
 175         char *member = group->gr_mem[index++];
 176 
 177         if (member == NULL) {
 178             break;
 179 
 180         } else if (pcmk__str_eq(usr, member, pcmk__str_none)) {
 181             return TRUE;
 182         }
 183     }
 184 
 185     crm_notice("Rejecting remote client: User '%s' is not a member of "
 186                "group '%s'", usr, grp);
 187     return FALSE;
 188 }
 189 
 190 static gboolean
 191 cib_remote_auth(xmlNode * login)
     /* [previous][next][first][last][top][bottom][index][help] */
 192 {
 193     const char *user = NULL;
 194     const char *pass = NULL;
 195     const char *tmp = NULL;
 196 
 197     if (login == NULL) {
 198         return FALSE;
 199     }
 200 
 201     if (!pcmk__xe_is(login, PCMK__XE_CIB_COMMAND)) {
 202         crm_warn("Rejecting remote client: Unrecognizable message "
 203                  "(element '%s' not '" PCMK__XE_CIB_COMMAND "')", login->name);
 204         crm_log_xml_debug(login, "bad");
 205         return FALSE;
 206     }
 207 
 208     tmp = crm_element_value(login, PCMK_XA_OP);
 209     if (!pcmk__str_eq(tmp, "authenticate", pcmk__str_casei)) {
 210         crm_warn("Rejecting remote client: Unrecognizable message "
 211                  "(operation '%s' not 'authenticate')", tmp);
 212         crm_log_xml_debug(login, "bad");
 213         return FALSE;
 214     }
 215 
 216     user = crm_element_value(login, PCMK_XA_USER);
 217     pass = crm_element_value(login, PCMK__XA_PASSWORD);
 218     if (!user || !pass) {
 219         crm_warn("Rejecting remote client: No %s given",
 220                  ((user == NULL)? "username" : "password"));
 221         crm_log_xml_debug(login, "bad");
 222         return FALSE;
 223     }
 224 
 225     crm_log_xml_debug(login, "auth");
 226 
 227     return check_group_membership(user, CRM_DAEMON_GROUP)
 228            && authenticate_user(user, pass);
 229 }
 230 
 231 static gboolean
 232 remote_auth_timeout_cb(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 233 {
 234     pcmk__client_t *client = data;
 235 
 236     client->remote->auth_timeout = 0;
 237 
 238     if (pcmk_is_set(client->flags, pcmk__client_authenticated)) {
 239         return FALSE;
 240     }
 241 
 242     mainloop_del_fd(client->remote->source);
 243     crm_err("Remote client authentication timed out");
 244 
 245     return FALSE;
 246 }
 247 
 248 static int
 249 cib_remote_listen(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 250 {
 251     int csock = -1;
 252     unsigned laddr;
 253     struct sockaddr_storage addr;
 254     char ipstr[INET6_ADDRSTRLEN];
 255     int ssock = *(int *)data;
 256     int rc;
 257 
 258     pcmk__client_t *new_client = NULL;
 259 
 260     static struct mainloop_fd_callbacks remote_client_fd_callbacks = {
 261         .dispatch = cib_remote_msg,
 262         .destroy = cib_remote_connection_destroy,
 263     };
 264 
 265     /* accept the connection */
 266     laddr = sizeof(addr);
 267     memset(&addr, 0, sizeof(addr));
 268     csock = accept(ssock, (struct sockaddr *)&addr, &laddr);
 269     if (csock == -1) {
 270         crm_warn("Could not accept remote connection: %s", pcmk_rc_str(errno));
 271         return TRUE;
 272     }
 273 
 274     pcmk__sockaddr2str(&addr, ipstr);
 275 
 276     rc = pcmk__set_nonblocking(csock);
 277     if (rc != pcmk_rc_ok) {
 278         crm_warn("Dropping remote connection from %s because "
 279                  "it could not be set to non-blocking: %s",
 280                  ipstr, pcmk_rc_str(rc));
 281         close(csock);
 282         return TRUE;
 283     }
 284 
 285     num_clients++;
 286 
 287     new_client = pcmk__new_unauth_client(NULL);
 288     new_client->remote = pcmk__assert_alloc(1, sizeof(pcmk__remote_t));
 289 
 290     if (ssock == remote_tls_fd) {
 291         pcmk__set_client_flags(new_client, pcmk__client_tls);
 292 
 293         /* create gnutls session for the server socket */
 294         new_client->remote->tls_session = pcmk__new_tls_session(tls, csock);
 295         if (new_client->remote->tls_session == NULL) {
 296             close(csock);
 297             return TRUE;
 298         }
 299     } else {
 300         pcmk__set_client_flags(new_client, pcmk__client_tcp);
 301         new_client->remote->tcp_socket = csock;
 302     }
 303 
 304     // Require the client to authenticate within this time
 305     new_client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT,
 306                                                           remote_auth_timeout_cb,
 307                                                           new_client);
 308     crm_info("%s connection from %s pending authentication for client %s",
 309              ((ssock == remote_tls_fd)? "Encrypted" : "Clear-text"),
 310              ipstr, new_client->id);
 311 
 312     new_client->remote->source =
 313         mainloop_add_fd("cib-remote-client", G_PRIORITY_DEFAULT, csock, new_client,
 314                         &remote_client_fd_callbacks);
 315 
 316     return TRUE;
 317 }
 318 
 319 void
 320 cib_remote_connection_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 321 {
 322     pcmk__client_t *client = user_data;
 323     int csock = -1;
 324 
 325     if (client == NULL) {
 326         return;
 327     }
 328 
 329     crm_trace("Cleaning up after client %s disconnect",
 330               pcmk__client_name(client));
 331 
 332     num_clients--;
 333     crm_trace("Num unfree'd clients: %d", num_clients);
 334 
 335     switch (PCMK__CLIENT_TYPE(client)) {
 336         case pcmk__client_tcp:
 337             csock = client->remote->tcp_socket;
 338             break;
 339         case pcmk__client_tls:
 340             if (client->remote->tls_session) {
 341                 csock = pcmk__tls_get_client_sock(client->remote);
 342 
 343                 if (pcmk_is_set(client->flags,
 344                                 pcmk__client_tls_handshake_complete)) {
 345                     gnutls_bye(client->remote->tls_session, GNUTLS_SHUT_WR);
 346                 }
 347                 gnutls_deinit(client->remote->tls_session);
 348                 client->remote->tls_session = NULL;
 349             }
 350             break;
 351         default:
 352             crm_warn("Unknown transport for client %s "
 353                      QB_XS " flags=%#016" PRIx64,
 354                      pcmk__client_name(client), client->flags);
 355     }
 356 
 357     if (csock >= 0) {
 358         close(csock);
 359     }
 360 
 361     pcmk__free_client(client);
 362 
 363     crm_trace("Freed the cib client");
 364 
 365     if (cib_shutdown_flag) {
 366         cib_shutdown(0);
 367     }
 368     return;
 369 }
 370 
 371 static void
 372 cib_handle_remote_msg(pcmk__client_t *client, xmlNode *command)
     /* [previous][next][first][last][top][bottom][index][help] */
 373 {
 374     if (!pcmk__xe_is(command, PCMK__XE_CIB_COMMAND)) {
 375         crm_log_xml_trace(command, "bad");
 376         return;
 377     }
 378 
 379     if (client->name == NULL) {
 380         client->name = pcmk__str_copy(client->id);
 381     }
 382 
 383     /* unset dangerous options */
 384     pcmk__xe_remove_attr(command, PCMK__XA_SRC);
 385     pcmk__xe_remove_attr(command, PCMK__XA_CIB_HOST);
 386     pcmk__xe_remove_attr(command, PCMK__XA_CIB_UPDATE);
 387 
 388     crm_xml_add(command, PCMK__XA_T, PCMK__VALUE_CIB);
 389     crm_xml_add(command, PCMK__XA_CIB_CLIENTID, client->id);
 390     crm_xml_add(command, PCMK__XA_CIB_CLIENTNAME, client->name);
 391     crm_xml_add(command, PCMK__XA_CIB_USER, client->user);
 392 
 393     if (crm_element_value(command, PCMK__XA_CIB_CALLID) == NULL) {
 394         char *call_uuid = crm_generate_uuid();
 395 
 396         /* fix the command */
 397         crm_xml_add(command, PCMK__XA_CIB_CALLID, call_uuid);
 398         free(call_uuid);
 399     }
 400 
 401     if (crm_element_value(command, PCMK__XA_CIB_CALLOPT) == NULL) {
 402         crm_xml_add_int(command, PCMK__XA_CIB_CALLOPT, 0);
 403     }
 404 
 405     crm_log_xml_trace(command, "Remote command: ");
 406     cib_common_callback_worker(0, 0, command, client, TRUE);
 407 }
 408 
 409 static int
 410 cib_remote_msg(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 411 {
 412     xmlNode *command = NULL;
 413     pcmk__client_t *client = data;
 414     int rc;
 415     const char *client_name = pcmk__client_name(client);
 416 
 417     crm_trace("Remote %s message received for client %s",
 418               pcmk__client_type_str(PCMK__CLIENT_TYPE(client)), client_name);
 419 
 420     if ((PCMK__CLIENT_TYPE(client) == pcmk__client_tls)
 421         && !pcmk_is_set(client->flags, pcmk__client_tls_handshake_complete)) {
 422 
 423         int rc = pcmk__read_handshake_data(client);
 424 
 425         if (rc == EAGAIN) {
 426             /* No more data is available at the moment. Just return for now;
 427              * we'll get invoked again once the client sends more.
 428              */
 429             return 0;
 430         } else if (rc != pcmk_rc_ok) {
 431             return -1;
 432         }
 433 
 434         crm_debug("Completed TLS handshake with remote client %s", client_name);
 435         pcmk__set_client_flags(client, pcmk__client_tls_handshake_complete);
 436         if (client->remote->auth_timeout) {
 437             g_source_remove(client->remote->auth_timeout);
 438         }
 439 
 440         /* Now that the handshake is done, see if any client TLS certificate is
 441          * close to its expiration date and log if so.  If a TLS certificate is not
 442          * in use, this function will just return so we don't need to check for the
 443          * session type here.
 444          */
 445         pcmk__tls_check_cert_expiration(client->remote->tls_session);
 446 
 447         // Require the client to authenticate within this time
 448         client->remote->auth_timeout = pcmk__create_timer(REMOTE_AUTH_TIMEOUT,
 449                                                           remote_auth_timeout_cb,
 450                                                           client);
 451         return 0;
 452     }
 453 
 454     rc = pcmk__read_available_remote_data(client->remote);
 455     switch (rc) {
 456         case pcmk_rc_ok:
 457             break;
 458 
 459         case EAGAIN:
 460             /* We haven't read the whole message yet */
 461             return 0;
 462 
 463         default:
 464             /* Error */
 465             crm_trace("Error reading from remote client: %s", pcmk_rc_str(rc));
 466             return -1;
 467     }
 468 
 469     /* must pass auth before we will process anything else */
 470     if (!pcmk_is_set(client->flags, pcmk__client_authenticated)) {
 471         xmlNode *reg;
 472         const char *user = NULL;
 473 
 474         command = pcmk__remote_message_xml(client->remote);
 475         if (cib_remote_auth(command) == FALSE) {
 476             pcmk__xml_free(command);
 477             return -1;
 478         }
 479 
 480         pcmk__set_client_flags(client, pcmk__client_authenticated);
 481         g_source_remove(client->remote->auth_timeout);
 482         client->remote->auth_timeout = 0;
 483         client->name = crm_element_value_copy(command, PCMK_XA_NAME);
 484 
 485         user = crm_element_value(command, PCMK_XA_USER);
 486         if (user) {
 487             client->user = pcmk__str_copy(user);
 488         }
 489 
 490         crm_notice("Remote connection accepted for authenticated user %s "
 491                    QB_XS " client %s",
 492                    pcmk__s(user, ""), client_name);
 493 
 494         /* send ACK */
 495         reg = pcmk__xe_create(NULL, PCMK__XE_CIB_RESULT);
 496         crm_xml_add(reg, PCMK__XA_CIB_OP, CRM_OP_REGISTER);
 497         crm_xml_add(reg, PCMK__XA_CIB_CLIENTID, client->id);
 498         pcmk__remote_send_xml(client->remote, reg);
 499         pcmk__xml_free(reg);
 500         pcmk__xml_free(command);
 501     }
 502 
 503     command = pcmk__remote_message_xml(client->remote);
 504     if (command != NULL) {
 505         crm_trace("Remote message received from client %s", client_name);
 506         cib_handle_remote_msg(client, command);
 507         pcmk__xml_free(command);
 508     }
 509 
 510     return 0;
 511 }
 512 
 513 #ifdef HAVE_PAM
 514 /*!
 515  * \internal
 516  * \brief Pass remote user's password to PAM
 517  *
 518  * \param[in]  num_msg   Number of entries in \p msg
 519  * \param[in]  msg       Array of PAM messages
 520  * \param[out] response  Where to set response to PAM
 521  * \param[in]  data      User data (the password string)
 522  *
 523  * \return PAM return code (PAM_BUF_ERR for memory errors, PAM_CONV_ERR for all
 524  *         other errors, or PAM_SUCCESS on success)
 525  * \note See pam_conv(3) for more explanation
 526  */
 527 static int
 528 construct_pam_passwd(int num_msg, const struct pam_message **msg,
     /* [previous][next][first][last][top][bottom][index][help] */
 529                      struct pam_response **response, void *data)
 530 {
 531     /* In theory, multiple messages are allowed, but due to OS compatibility
 532      * issues, PAM implementations are recommended to only send one message at a
 533      * time. We can require that here for simplicity.
 534      */
 535     CRM_CHECK((num_msg == 1) && (msg != NULL) && (response != NULL)
 536               && (data != NULL), return PAM_CONV_ERR);
 537 
 538     switch (msg[0]->msg_style) {
 539         case PAM_PROMPT_ECHO_OFF:
 540         case PAM_PROMPT_ECHO_ON:
 541             // Password requested
 542             break;
 543         case PAM_TEXT_INFO:
 544             crm_info("PAM: %s", msg[0]->msg);
 545             data = NULL;
 546             break;
 547         case PAM_ERROR_MSG:
 548             /* In theory we should show msg[0]->msg, but that might
 549              * contain the password, which we don't want in the logs
 550              */
 551             crm_err("PAM reported an error");
 552             data = NULL;
 553             break;
 554         default:
 555             crm_warn("Ignoring PAM message of unrecognized type %d",
 556                      msg[0]->msg_style);
 557             return PAM_CONV_ERR;
 558     }
 559 
 560     *response = calloc(1, sizeof(struct pam_response));
 561     if (*response == NULL) {
 562         return PAM_BUF_ERR;
 563     }
 564     (*response)->resp_retcode = 0;
 565     (*response)->resp = pcmk__str_copy((const char *) data); // Caller will free
 566     return PAM_SUCCESS;
 567 }
 568 #endif
 569 
 570 /*!
 571  * \internal
 572  * \brief Verify the username and password passed for a remote CIB connection
 573  *
 574  * \param[in] user    Username passed for remote CIB connection
 575  * \param[in] passwd  Password passed for remote CIB connection
 576  *
 577  * \return \c true if the username and password are accepted, otherwise \c false
 578  * \note This function rejects all credentials when built without PAM support.
 579  */
 580 static bool
 581 authenticate_user(const char *user, const char *passwd)
     /* [previous][next][first][last][top][bottom][index][help] */
 582 {
 583 #ifdef HAVE_PAM
 584     int rc = 0;
 585     bool pass = false;
 586     const void *p_user = NULL;
 587     struct pam_conv p_conv;
 588     struct pam_handle *pam_h = NULL;
 589 
 590     static const char *pam_name = NULL;
 591 
 592     if (pam_name == NULL) {
 593         pam_name = getenv("CIB_pam_service");
 594         if (pam_name == NULL) {
 595             pam_name = "login";
 596         }
 597     }
 598 
 599     p_conv.conv = construct_pam_passwd;
 600     p_conv.appdata_ptr = (void *) passwd;
 601 
 602     rc = pam_start(pam_name, user, &p_conv, &pam_h);
 603     if (rc != PAM_SUCCESS) {
 604         crm_warn("Rejecting remote client for user %s "
 605                  "because PAM initialization failed: %s",
 606                  user, pam_strerror(pam_h, rc));
 607         goto bail;
 608     }
 609 
 610     // Check user credentials
 611     rc = pam_authenticate(pam_h, PAM_SILENT);
 612     if (rc != PAM_SUCCESS) {
 613         crm_notice("Access for remote user %s denied: %s",
 614                    user, pam_strerror(pam_h, rc));
 615         goto bail;
 616     }
 617 
 618     /* Get the authenticated user name (PAM modules can map the original name to
 619      * something else). Since the CIB manager runs as the daemon user (not
 620      * root), that is the only user that can be successfully authenticated.
 621      */
 622     rc = pam_get_item(pam_h, PAM_USER, &p_user);
 623     if (rc != PAM_SUCCESS) {
 624         crm_warn("Rejecting remote client for user %s "
 625                  "because PAM failed to return final user name: %s",
 626                  user, pam_strerror(pam_h, rc));
 627         goto bail;
 628     }
 629     if (p_user == NULL) {
 630         crm_warn("Rejecting remote client for user %s "
 631                  "because PAM returned no final user name", user);
 632         goto bail;
 633     }
 634 
 635     // @TODO Why do we require these to match?
 636     if (!pcmk__str_eq(p_user, user, pcmk__str_none)) {
 637         crm_warn("Rejecting remote client for user %s "
 638                  "because PAM returned different final user name %s",
 639                  user, p_user);
 640         goto bail;
 641     }
 642 
 643     // Check user account restrictions (expiration, etc.)
 644     rc = pam_acct_mgmt(pam_h, PAM_SILENT);
 645     if (rc != PAM_SUCCESS) {
 646         crm_notice("Access for remote user %s denied: %s",
 647                    user, pam_strerror(pam_h, rc));
 648         goto bail;
 649     }
 650     pass = true;
 651 
 652 bail:
 653     pam_end(pam_h, rc);
 654     return pass;
 655 #else
 656     // @TODO Implement for non-PAM environments
 657     crm_warn("Rejecting remote user %s because this build does not have "
 658              "PAM support", user);
 659     return false;
 660 #endif
 661 }

/* [previous][next][first][last][top][bottom][index][help] */