<commit_before>// PAM module for two-factor authentication.
//
// Copyright 2010 Google Inc.
// Author: <NAME>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#define _GNU_SOURCE
#include <fcntl.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/fsuid.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>

#define PAM_SM_AUTH
#define PAM_SM_SESSION
#include <security/pam_modules.h>

#include "base32.h"
#include "hmac.h"
#include "sha1.h"

#define MODULE_NAME "pam_google_authenticator"
#define SECRET      "/.google_authenticator"

static void log_message(int priority, pam_handle_t *pamh,
                        const char *format, ...) {
  char *service = NULL;
  if (pamh)
    pam_get_item(pamh, PAM_SERVICE, (void *)&service);
  if (!service)
    service = "";

  char logname[80];
  snprintf(logname, sizeof(logname), "%s(" MODULE_NAME ")", service);

  va_list args;
  va_start(args, format);
  openlog(logname, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
  vsyslog(priority, format, args);
  va_end(args);

  closelog();
}

static int converse(pam_handle_t *pamh, int nargs,
                    const struct pam_message **message,
                    struct pam_response **response) {
  struct pam_conv *conv;
  int retval = pam_get_item(pamh, PAM_CONV, (void *)&conv);
  if (retval != PAM_SUCCESS) {
    return retval;
  }
  return conv->conv(nargs, message, response, conv->appdata_ptr);
}

static const char *get_user_name(pam_handle_t *pamh) {
  // Obtain the user's name
  const char *username;
  if (pam_get_item(pamh, PAM_USER, (void *)&username) != PAM_SUCCESS ||
      !username || !*username) {
    log_message(LOG_ERR, pamh,
                "No user name available when checking verification code");
    return NULL;
  }
  return username;
}

#ifdef TESTING
static char *secret_file_name;
void set_secret_filename(char *fn) {
  secret_file_name = fn;
}

static char *get_secret_filename(pam_handle_t *pamh, const char *username,
                                 int *uid) {
  *uid = getuid();
  return strdup(secret_file_name);
}
#else
static char *get_secret_filename(pam_handle_t *pamh, const char *username,
                                 int *uid) {
  // Obtain the user's home directory
  struct passwd pwbuf, *pw;
  #ifdef _SC_GETPW_R_SIZE_MAX
  int len = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (len <= 0) {
    len = 4096;
  }
  #else
  int len = 4096;
  #endif
  char *buf = malloc(len);
  char *secret_filename = NULL;
  *uid = -1;
  if (buf == NULL ||
      getpwnam_r(username, &pwbuf, buf, len, &pw) ||
      !pw ||
      !pw->pw_dir ||
      *pw->pw_dir != '/' ||
      !(secret_filename = malloc(strlen(pw->pw_dir) + strlen(SECRET) + 1))) {
    log_message(LOG_ERR, pamh, "Failed to find home directory for user \"%s\"",
                username);
    free(buf);
    free(secret_filename);
    return NULL;
  }
  free(buf);
  strcat(strcpy(secret_filename, pw->pw_dir), SECRET);
  *uid = pw->pw_uid;
  return secret_filename;
}
#endif

static int drop_privileges(pam_handle_t *pamh, const char *username, int uid) {
  // Try to become the new user. This might be necessary for NFS mounted home
  // directories.
  int old_uid = setfsuid(uid);
  if (uid != setfsuid(uid)) {
    log_message(LOG_ERR, pamh, "Failed to change user id to \"%s\"", username);
    setfsuid(old_uid);
    return -1;<commit_msg>Keep string pool until after strcpy<commit_after>// PAM module for two-factor authentication.
//
// Copyright 2010 Google Inc.
// Author: <NAME>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#define _GNU_SOURCE
#include <fcntl.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/fsuid.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>

#define PAM_SM_AUTH
#define PAM_SM_SESSION
#include <security/pam_modules.h>

#include "base32.h"
#include "hmac.h"
#include "sha1.h"

#define MODULE_NAME "pam_google_authenticator"
#define SECRET      "/.google_authenticator"

static void log_message(int priority, pam_handle_t *pamh,
                        const char *format, ...) {
  char *service = NULL;
  if (pamh)
    pam_get_item(pamh, PAM_SERVICE, (void *)&service);
  if (!service)
    service = "";

  char logname[80];
  snprintf(logname, sizeof(logname), "%s(" MODULE_NAME ")", service);

  va_list args;
  va_start(args, format);
  openlog(logname, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
  vsyslog(priority, format, args);
  va_end(args);

  closelog();
}

static int converse(pam_handle_t *pamh, int nargs,
                    const struct pam_message **message,
                    struct pam_response **response) {
  struct pam_conv *conv;
  int retval = pam_get_item(pamh, PAM_CONV, (void *)&conv);
  if (retval != PAM_SUCCESS) {
    return retval;
  }
  return conv->conv(nargs, message, response, conv->appdata_ptr);
}

static const char *get_user_name(pam_handle_t *pamh) {
  // Obtain the user's name
  const char *username;
  if (pam_get_item(pamh, PAM_USER, (void *)&username) != PAM_SUCCESS ||
      !username || !*username) {
    log_message(LOG_ERR, pamh,
                "No user name available when checking verification code");
    return NULL;
  }
  return username;
}

#ifdef TESTING
static char *secret_file_name;
void set_secret_filename(char *fn) {
  secret_file_name = fn;
}

static char *get_secret_filename(pam_handle_t *pamh, const char *username,
                                 int *uid) {
  *uid = getuid();
  return strdup(secret_file_name);
}
#else
static char *get_secret_filename(pam_handle_t *pamh, const char *username,
                                 int *uid) {
  // Obtain the user's home directory
  struct passwd pwbuf, *pw;
  #ifdef _SC_GETPW_R_SIZE_MAX
  int len = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (len <= 0) {
    len = 4096;
  }
  #else
  int len = 4096;
  #endif
  char *buf = malloc(len);
  char *secret_filename = NULL;
  *uid = -1;
  if (buf == NULL ||
      getpwnam_r(username, &pwbuf, buf, len, &pw) ||
      !pw ||
      !pw->pw_dir ||
      *pw->pw_dir != '/' ||
      !(secret_filename = malloc(strlen(pw->pw_dir) + strlen(SECRET) + 1))) {
    log_message(LOG_ERR, pamh, "Failed to find home directory for user \"%s\"",
                username);
    free(buf);
    free(secret_filename);
    return NULL;
  }
  strcat(strcpy(secret_filename, pw->pw_dir), SECRET);
  free(buf);
  *uid = pw->pw_uid;
  return secret_filename;
}
#endif

static int drop_privileges(pam_handle_t *pamh, const char *username, int uid) {
  // Try to become the new user. This might be necessary for NFS mounted home
  // directories.
  int old_uid = setfsuid(uid);
  if (uid != setfsuid(uid)) {
    log_message(LOG_ERR, pamh, "Failed to change user id to \"%s\"", username);
    setfsuid(old_uid);
    return -1;
{'fraction_non_alphanumeric': 0.1271122320302648, 'fraction_numerical': 0.006305170239596469, 'mean_word_length': 3.2823974082073435, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5504878', 'n_tokens_mistral': 2797, 'n_tokens_neox': 2753, 'n_words': 780}