static int do_auth(struct login_ctx *ctx, const char *cmd) { struct duo_config cfg; struct passwd *pw; struct in_addr addr; duo_t *duo; duo_code_t code; const char *config, *p, *duouser; const char *ip, *host = NULL; char buf[64]; int i, flags, ret, prompts, matched; int headless = 0; if ((pw = getpwuid(ctx->uid)) == NULL) die("Who are you?"); duouser = ctx->duouser ? ctx->duouser : pw->pw_name; config = ctx->config ? ctx->config : DUO_CONF; flags = 0; duo_config_default(&cfg); /* Load our private config. */ if ((i = duo_parse_config(config, __ini_handler, &cfg)) != 0 || (!cfg.apihost || !cfg.apihost[0] || !cfg.skey || !cfg.skey[0] || !cfg.ikey || !cfg.ikey[0])) { switch (i) { case -2: fprintf(stderr, "%s must be readable only by " "user '%s'\n", config, pw->pw_name); break; case -1: fprintf(stderr, "Couldn't open %s: %s\n", config, strerror(errno)); break; case 0: fprintf(stderr, "Missing host, ikey, or skey in %s\n", config); break; default: fprintf(stderr, "Parse error in %s, line %d\n", config, i); break; } /* Implicit "safe" failmode for local configuration errors */ if (cfg.failmode == DUO_FAIL_SAFE) { return (EXIT_SUCCESS); } return (EXIT_FAILURE); } prompts = cfg.prompts; /* Check group membership. */ matched = duo_check_groups(pw, cfg.groups, cfg.groups_cnt); if (matched == -1) { return (EXIT_FAILURE); } else if (matched == 0) { return (EXIT_SUCCESS); } /* Check for remote login host */ if ((host = ip = getenv("SSH_CONNECTION")) != NULL || (host = ip = (char *)ctx->host) != NULL) { if (inet_aton(ip, &addr)) { strlcpy(buf, ip, sizeof(buf)); ip = strtok(buf, " "); host = ip; } else { ip = (cfg.local_ip_fallback ? duo_local_ip() : NULL); } } /* Honor configured http_proxy */ if (cfg.http_proxy != NULL) { setenv("http_proxy", cfg.http_proxy, 1); } /* Try Duo auth. */ if ((duo = duo_open(cfg.apihost, cfg.ikey, cfg.skey, "login_duo/" PACKAGE_VERSION, cfg.noverify ? "" : cfg.cafile, cfg.https_timeout)) == NULL) { duo_log(LOG_ERR, "Couldn't open Duo API handle", pw->pw_name, host, NULL); return (EXIT_FAILURE); } /* Special handling for non-interactive sessions */ if ((p = getenv("SSH_ORIGINAL_COMMAND")) != NULL || !isatty(STDIN_FILENO)) { /* Try to support automatic one-shot login */ duo_set_conv_funcs(duo, NULL, NULL, NULL); flags = (DUO_FLAG_SYNC|DUO_FLAG_AUTO); prompts = 1; headless = 1; } else if (cfg.autopush) { /* Special handling for autopush */ duo_set_conv_funcs(duo, NULL, __autopush_status_fn, NULL); flags = (DUO_FLAG_SYNC|DUO_FLAG_AUTO); } if (cfg.accept_env) { flags |= DUO_FLAG_ENV; } ret = EXIT_FAILURE; for (i = 0; i < prompts; i++) { code = duo_login(duo, duouser, host, flags, cfg.pushinfo ? cmd : NULL); if (code == DUO_FAIL) { duo_log(LOG_WARNING, "Failed Duo login", duouser, host, duo_geterr(duo)); if ((flags & DUO_FLAG_SYNC) == 0) { printf("\n"); } /* The autopush failed, fall back to regular process */ if (cfg.autopush && i == 0) { flags = 0; duo_reset_conv_funcs(duo); } /* Keep going */ continue; } /* Terminal conditions */ if (code == DUO_OK) { if ((p = duo_geterr(duo)) != NULL) { duo_log(LOG_WARNING, "Skipped Duo login", duouser, host, p); } else { duo_log(LOG_INFO, "Successful Duo login", duouser, host, NULL); } if (cfg.motd && !headless) { _print_motd(); } ret = EXIT_SUCCESS; } else if (code == DUO_ABORT) { duo_log(LOG_WARNING, "Aborted Duo login", duouser, host, duo_geterr(duo)); } else if (cfg.failmode == DUO_FAIL_SAFE && (code == DUO_CONN_ERROR || code == DUO_CLIENT_ERROR || code == DUO_SERVER_ERROR)) { duo_log(LOG_WARNING, "Failsafe Duo login", duouser, host, duo_geterr(duo)); ret = EXIT_SUCCESS; } else { duo_log(LOG_ERR, "Error in Duo login", duouser, host, duo_geterr(duo)); } break; } duo_close(duo); return (ret); }
static int do_auth(struct login_ctx *ctx, const char *cmd) { struct duo_config cfg; struct passwd *pw; struct in_addr addr; duo_t *duo; duo_code_t code; const char *config, *p, *duouser; const char *ip, *host = NULL; char buf[64]; int i, flags, ret, prompts, matched; int headless = 0; if ((pw = getpwuid(ctx->uid)) == NULL) { die("Who are you?"); } duouser = ctx->duouser ? ctx->duouser : pw->pw_name; config = ctx->config ? ctx->config : DUO_CONF; flags = 0; duo_config_default(&cfg); /* Load our private config. */ if ((i = duo_parse_config(config, __ini_handler, &cfg)) != 0 || (!cfg.apihost || !cfg.apihost[0] || !cfg.skey || !cfg.skey[0] || !cfg.ikey || !cfg.ikey[0])) { switch (i) { case -2: fprintf(stderr, "%s must be readable only by " "user '%s'\n", config, pw->pw_name); break; case -1: fprintf(stderr, "Couldn't open %s: %s\n", config, strerror(errno)); break; case 0: fprintf(stderr, "Missing host, ikey, or skey in %s\n", config); break; default: fprintf(stderr, "Parse error in %s, line %d\n", config, i); break; } /* Implicit "safe" failmode for local configuration errors */ if (cfg.failmode == DUO_FAIL_SAFE) { return (EXIT_SUCCESS); } return (EXIT_FAILURE); } #ifdef OPENSSL_FIPS /* * When fips_mode is configured, invoke OpenSSL's FIPS_mode_set() API. Note * that in some environments, FIPS may be enabled system-wide, causing FIPS * operation to be enabled automatically when OpenSSL is initialized. The * fips_mode option is an experimental feature allowing explicit entry to FIPS * operation in cases where it isn't enabled globally at the OS level (for * example, when integrating directly with the OpenSSL FIPS Object Module). */ if(!FIPS_mode_set(cfg.fips_mode)) { /* The smallest size buff can be according to the openssl docs */ char buff[256]; int error = ERR_get_error(); ERR_error_string_n(error, buff, sizeof(buff)); duo_syslog(LOG_ERR, "Unable to start fips_mode: %s", buff); return (EXIT_FAILURE); } #else if(cfg.fips_mode) { duo_syslog(LOG_ERR, "FIPS mode flag specified, but OpenSSL not built with FIPS support. Failing the auth."); return (EXIT_FAILURE); } #endif prompts = cfg.prompts; /* Check group membership. */ matched = duo_check_groups(pw, cfg.groups, cfg.groups_cnt); if (matched == -1) { close_config(&cfg); return (EXIT_FAILURE); } else if (matched == 0) { close_config(&cfg); return (EXIT_SUCCESS); } /* Use GECOS field if called for */ if ((cfg.send_gecos || cfg.gecos_username_pos >= 0) && !ctx->duouser) { if (strlen(pw->pw_gecos) > 0) { if (cfg.gecos_username_pos >= 0) { duouser = duo_split_at(pw->pw_gecos, cfg.gecos_delim, cfg.gecos_username_pos); if (duouser == NULL || (strcmp(duouser, "") == 0)) { duo_log(LOG_DEBUG, "Could not parse GECOS field", pw->pw_name, NULL, NULL); duouser = pw->pw_name; } } else { duouser = pw->pw_gecos; } } else { duo_log(LOG_WARNING, "Empty GECOS field", pw->pw_name, NULL, NULL); } } /* Check for remote login host */ if ((host = ip = getenv("SSH_CONNECTION")) != NULL || (host = ip = (char *)ctx->host) != NULL) { if (inet_aton(ip, &addr)) { strlcpy(buf, ip, sizeof(buf)); ip = strtok(buf, " "); host = ip; } else { if (cfg.local_ip_fallback) { host = duo_local_ip(); } } } /* Try Duo auth. */ if ((duo = duo_open(cfg.apihost, cfg.ikey, cfg.skey, "login_duo/" PACKAGE_VERSION, cfg.noverify ? "" : cfg.cafile, cfg.https_timeout, cfg.http_proxy)) == NULL) { duo_log(LOG_ERR, "Couldn't open Duo API handle", pw->pw_name, host, NULL); close_config(&cfg); return (EXIT_FAILURE); } /* Special handling for non-interactive sessions */ if ((p = getenv("SSH_ORIGINAL_COMMAND")) != NULL || !isatty(STDIN_FILENO)) { /* Try to support automatic one-shot login */ duo_set_conv_funcs(duo, NULL, NULL, NULL); flags = (DUO_FLAG_SYNC|DUO_FLAG_AUTO); prompts = 1; headless = 1; } else if (cfg.autopush) { /* Special handling for autopush */ duo_set_conv_funcs(duo, NULL, __autopush_status_fn, NULL); flags = (DUO_FLAG_SYNC|DUO_FLAG_AUTO); } if (cfg.accept_env) { flags |= DUO_FLAG_ENV; } ret = EXIT_FAILURE; for (i = 0; i < prompts; i++) { code = duo_login(duo, duouser, host, flags, cfg.pushinfo ? cmd : NULL, cfg.failmode); if (code == DUO_FAIL) { duo_log(LOG_WARNING, "Failed Duo login", duouser, host, duo_geterr(duo)); if ((flags & DUO_FLAG_SYNC) == 0) { printf("\n"); } /* The autopush failed, fall back to regular process */ if (cfg.autopush && i == 0) { flags = 0; duo_reset_conv_funcs(duo); } /* Keep going */ continue; } /* Terminal conditions */ if (code == DUO_OK) { if ((p = duo_geterr(duo)) != NULL) { duo_log(LOG_WARNING, "Skipped Duo login", duouser, host, p); } else { duo_log(LOG_INFO, "Successful Duo login", duouser, host, NULL); } if (cfg.motd && !headless) { _print_motd(); } ret = EXIT_SUCCESS; } else if (code == DUO_ABORT) { duo_log(LOG_WARNING, "Aborted Duo login", duouser, host, duo_geterr(duo)); } else if (code == DUO_FAIL_SAFE_ALLOW) { duo_log(LOG_WARNING, "Failsafe Duo login", duouser, host, duo_geterr(duo)); ret = EXIT_SUCCESS; } else if (code == DUO_FAIL_SECURE_DENY) { duo_log(LOG_WARNING, "Failsecure Duo login", duouser, host, duo_geterr(duo)); } else { duo_log(LOG_ERR, "Error in Duo login", duouser, host, duo_geterr(duo)); } break; } duo_close(duo); close_config(&cfg); return (ret); }