Skip to content
Snippets Groups Projects
pam_authclient.c 5.76 KiB
Newer Older
  • Learn to ignore specific revisions
  • ale's avatar
    ale committed
    
    #include "config.h"
    #include <stdio.h>
    #ifdef HAVE_STDLIB_H
    #include <stdlib.h>
    #endif
    #ifdef HAVE_MEMORY_H
    #include <memory.h>
    #endif
    
    #include "auth_client.h"
    
    /* These #defines must be present according to PAM documentation. */
    #define PAM_SM_AUTH
    
    #ifdef HAVE_SECURITY_PAM_APPL_H
    #include <security/pam_appl.h>
    #endif
    #ifdef HAVE_SECURITY_PAM_MODULES_H
    #include <security/pam_modules.h>
    #endif
    #ifdef HAVE_PAM_PAM_APPL_H
    #include <pam/pam_appl.h>
    #endif
    #ifdef HAVE_PAM_PAM_MODULES_H
    #include <pam/pam_modules.h>
    #endif
    
    
    ale's avatar
    ale committed
    #ifdef HAVE_SECURITY__PAM_MACROS_H
    #include <security/_pam_macros.h>
    #else
    #define D(x) do {							\
        printf ("debug: %s:%d (%s): ", __FILE__, __LINE__, __FUNCTION__);	\
        printf x;								\
        printf ("\n");							\
      } while (0)
    #endif
    
    
    ale's avatar
    ale committed
    #ifndef PAM_EXTERN
    #ifdef PAM_STATIC
    #define PAM_EXTERN static
    #else
    #define PAM_EXTERN extern
    #endif
    #endif
    
    static const char *kPasswordPrompt = "Password: ";
    static const char *kOtpPrompt = "OTP Token: ";
    
    struct cfg {
      int debug;
      int use_first_pass;
      int try_first_pass;
      char *auth_server;
      char *ssl_crt;
      char *ssl_key;
      char *ca_file;
    
      char *shard;
    
    ale's avatar
    ale committed
    };
    
    static void parse_cfg(int argc, const char **argv, struct cfg *cfg) {
      int i;
    
      memset(cfg, 0, sizeof(struct cfg));
    
    
      // Set some defaults.
      cfg->auth_server = "127.0.0.1:1616";
    
    
    ale's avatar
    ale committed
      for (i = 0; i < argc; i++) {
        if (!strcmp(argv[i], "debug")) {
          cfg->debug = 1;
        } else if (!strcmp(argv[i], "try_first_pass")) {
          cfg->try_first_pass = 1;
        } else if (!strcmp(argv[i], "use_first_pass")) {
          cfg->use_first_pass = 1;
        } else if (!strncmp(argv[i], "auth_server=", 12)) {
          cfg->auth_server = (char *)(argv[i] + 12);
        } else if (!strncmp(argv[i], "ssl_crt=", 8)) {
          cfg->ssl_crt = (char *)(argv[i] + 8);
        } else if (!strncmp(argv[i], "ssl_key=", 8)) {
          cfg->ssl_key = (char *)(argv[i] + 8);
        } else if (!strncmp(argv[i], "ca=", 3)) {
          cfg->ca_file = (char *)(argv[i] + 3);
    
        } else if (!strncmp(argv[i], "shard=", 6)) {
          cfg->shard = (char *)(argv[i] + 6);
    
    ale's avatar
    ale committed
        }
      }
    }
    
    PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,
                                       int flags, int argc, const char **argv) {
      int i, retval, err;
      const char *service = NULL;
      const char *username = NULL;
      const char *password = NULL;
      const char *otp_token = NULL;
      const char *source_ip = NULL;
      struct pam_conv *conv = NULL;
      struct cfg cfg;
      auth_client_t ac;
    
      parse_cfg(argc, argv, &cfg);
    
      err = pam_get_user(pamh, &username, NULL);
      if (err != PAM_SUCCESS) {
        D(("pam_get_user() error: %s", pam_strerror(pamh, err)));
        return PAM_AUTH_ERR;
      }
    
      err = pam_get_item(pamh, PAM_SERVICE, (PAM_CONST void **)&service);
      if (err != PAM_SUCCESS) {
        D(("pam_get_item(service) error: %s", pam_strerror(pamh, err)));
        return PAM_AUTH_ERR;
      }
    
      pam_get_item(pamh, PAM_RHOST, (PAM_CONST void **)&source_ip);
    
      if (cfg.try_first_pass || cfg.use_first_pass) {
        if (pam_get_item(pamh, PAM_AUTHTOK, (PAM_CONST void **)&password) != PAM_SUCCESS) {
          return PAM_AUTH_ERR;
        }
      }
    
      if (cfg.use_first_pass && password == NULL) {
        return PAM_AUTH_ERR;
      }
    
      if (password == NULL) {
        // Ask for the password interactively.
        struct pam_message *pmsg[1], msg[1];
        struct pam_response *resp;
    
        err = pam_get_item(pamh, PAM_CONV, (PAM_CONST void **)&conv);
        if (err != PAM_SUCCESS) {
          D(("pam_get_item(conv) error: %s", pam_strerror(pamh, err)));
          return PAM_AUTH_ERR;
        }
        pmsg[0] = &msg[0];
        msg[0].msg = (char *)kPasswordPrompt;
        msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
        err = conv->conv(1, (const struct pam_message **)pmsg,
                         &resp, conv->appdata_ptr);
        if (err != PAM_SUCCESS) {
          D(("conv() error: %s", pam_strerror(pamh, err)));
          return PAM_AUTH_ERR;
        }
        password = resp->resp;
      }
    
    
    ale's avatar
    ale committed
      // Create the auth client request.
      ac = auth_client_new(service, cfg.auth_server);
      if (cfg.ssl_crt && cfg.ssl_key && cfg.ca_file) {
    
        err = auth_client_set_certificate(ac, cfg.ca_file, cfg.ssl_crt, cfg.ssl_key);
        if (err != AC_OK) {
          D(("auth_client_set_certificate() error: %s", auth_client_strerror(err)));
          goto error;
        }
    
    ale's avatar
    ale committed
      }
    
      // Allow two authentication attempts in case we receive an
      // OTP_REQUIRED response from the server.
      for (i = 0; i < 2; i++) {
    
        int ac_err = auth_client_authenticate(ac, username, password, otp_token, source_ip, cfg.shard);
    
    ale's avatar
    ale committed
        if (ac_err == AC_OK) {
          retval = PAM_SUCCESS;
        } else if (ac_err == AC_ERR_OTP_REQUIRED) {
          struct pam_message *pmsg[1], msg[1];
          struct pam_response *resp;
    
          // Ask for the OTP token interactively.
          if (conv == NULL) {
            err = pam_get_item(pamh, PAM_CONV, (PAM_CONST void **)&conv);
            if (err != PAM_SUCCESS) {
              D(("pam_get_item(conv) error: %s", pam_strerror(pamh, err)));
              break;
            }
          }
          pmsg[0] = &msg[0];
          msg[0].msg = (char *)kOtpPrompt;
          msg[0].msg_style = PAM_PROMPT_ECHO_ON;
          err = conv->conv(1, (const struct pam_message **)pmsg,
                           &resp, conv->appdata_ptr);
          if (err != PAM_SUCCESS) {
            D(("conv() error: %s", pam_strerror(pamh, err)));
            break;
          }
          otp_token = resp->resp;
          continue;
        } else {
          D(("auth_client error: %s", auth_client_strerror(ac_err)));
        }
        break;
      }
    
    
    ale's avatar
    ale committed
      auth_client_free(ac);
      return retval;
    }
    
    PAM_EXTERN int pam_sm_setcred(pam_handle_t *pam, int flags, int argc, const char **argv) {
      return PAM_SUCCESS;
    }
    
    
    ale's avatar
    ale committed
    PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pam, int flags, int argc, const char **argv) {
      return PAM_SUCCESS;
    }
    
    
    ale's avatar
    ale committed
    #ifdef PAM_STATIC
    
    struct pam_module _pam_authclient_modstruct = {
      "pam_authclient",
      pam_sm_authenticate,
      pam_sm_setcred,
    
    ale's avatar
    ale committed
      pam_sm_acct_mgmt,
    
    ale's avatar
    ale committed
      NULL,
      NULL,
      NULL
    };
    
    #endif