Blame pam/pam_fprintd.c

Packit Service f1aff6
/*
Packit Service f1aff6
 * pam_fprint: PAM module for fingerprint authentication through fprintd
Packit Service f1aff6
 * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
Packit Service 8ebd8e
 * Copyright (C) 2008-2014, 2017-2020 Bastien Nocera <hadess@hadess.net>
Packit Service f1aff6
 *
Packit Service f1aff6
 * This program is free software; you can redistribute it and/or modify
Packit Service f1aff6
 * it under the terms of the GNU General Public License as published by
Packit Service f1aff6
 * the Free Software Foundation; either version 2 of the License, or
Packit Service f1aff6
 * (at your option) any later version.
Packit Service f1aff6
 *
Packit Service f1aff6
 * This program is distributed in the hope that it will be useful,
Packit Service f1aff6
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service f1aff6
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service f1aff6
 * GNU General Public License for more details.
Packit Service f1aff6
 *
Packit Service f1aff6
 * You should have received a copy of the GNU General Public License along
Packit Service f1aff6
 * with this program; if not, write to the Free Software Foundation, Inc.,
Packit Service f1aff6
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit Service f1aff6
 */
Packit Service f1aff6
Packit Service f1aff6
#include <config.h>
Packit Service f1aff6
Packit Service 8ebd8e
#define _GNU_SOURCE
Packit Service f1aff6
#include <stdio.h>
Packit Service f1aff6
#include <stdlib.h>
Packit Service 8ebd8e
#include <stdint.h>
Packit Service f1aff6
#include <unistd.h>
Packit Service f1aff6
#include <sys/types.h>
Packit Service f1aff6
#include <string.h>
Packit Service f1aff6
#include <syslog.h>
Packit Service 8ebd8e
#include <errno.h>
Packit Service f1aff6
Packit Service 8ebd8e
#include <libintl.h>
Packit Service 8ebd8e
#include <systemd/sd-bus.h>
Packit Service 8ebd8e
#include <systemd/sd-login.h>
Packit Service f1aff6
Packit Service f1aff6
#define PAM_SM_AUTH
Packit Service f1aff6
#include <security/pam_modules.h>
Packit Service 8ebd8e
#include <security/pam_ext.h>
Packit Service f1aff6
Packit Service 8ebd8e
#define _(s) ((char *) dgettext (GETTEXT_PACKAGE, s))
Packit Service 8ebd8e
#define TR(s) dgettext (GETTEXT_PACKAGE, s)
Packit Service 8ebd8e
#define N_(s) (s)
Packit Service f1aff6
Packit Service f1aff6
#include "fingerprint-strings.h"
Packit Service 8ebd8e
#include "pam_fprintd_autoptrs.h"
Packit Service f1aff6
Packit Service f1aff6
#define DEFAULT_MAX_TRIES 3
Packit Service f1aff6
#define DEFAULT_TIMEOUT 30
Packit Service 8ebd8e
#define MIN_TIMEOUT 10
Packit Service f1aff6
Packit Service 8ebd8e
#define DEBUG_MATCH "debug="
Packit Service f1aff6
#define MAX_TRIES_MATCH "max-tries="
Packit Service f1aff6
#define TIMEOUT_MATCH "timeout="
Packit Service f1aff6
Packit Service 8ebd8e
static bool debug = false;
Packit Service 8ebd8e
static unsigned max_tries = DEFAULT_MAX_TRIES;
Packit Service 8ebd8e
static unsigned timeout = DEFAULT_TIMEOUT;
Packit Service f1aff6
Packit Service 8ebd8e
#define USEC_PER_SEC ((uint64_t) 1000000ULL)
Packit Service 8ebd8e
#define NSEC_PER_USEC ((uint64_t) 1000ULL)
Packit Service f1aff6
Packit Service 8ebd8e
static uint64_t
Packit Service 8ebd8e
now (void)
Packit Service f1aff6
{
Packit Service 8ebd8e
  struct timespec ts;
Packit Service 8ebd8e
Packit Service 8ebd8e
  clock_gettime (CLOCK_MONOTONIC, &ts);
Packit Service 8ebd8e
  return (uint64_t) ts.tv_sec * USEC_PER_SEC + (uint64_t) ts.tv_nsec / NSEC_PER_USEC;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static bool
Packit Service 8ebd8e
str_has_prefix (const char *s, const char *prefix)
Packit Service f1aff6
{
Packit Service 8ebd8e
  if (s == NULL || prefix == NULL)
Packit Service 8ebd8e
    return false;
Packit Service 8ebd8e
  return strncmp (s, prefix, strlen (prefix)) == 0;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static bool
Packit Service 8ebd8e
send_msg (pam_handle_t *pamh, const char *msg, int style)
Packit Service f1aff6
{
Packit Service 8ebd8e
  const struct pam_message mymsg = {
Packit Service 8ebd8e
    .msg_style = style,
Packit Service 8ebd8e
    .msg = msg,
Packit Service 8ebd8e
  };
Packit Service 8ebd8e
  const struct pam_message *msgp = &mymsg;
Packit Service 8ebd8e
  const struct pam_conv *pc;
Packit Service 8ebd8e
  struct pam_response *resp;
Packit Service f1aff6
Packit Service 8ebd8e
  if (pam_get_item (pamh, PAM_CONV, (const void **) &pc) != PAM_SUCCESS)
Packit Service 8ebd8e
    return false;
Packit Service f1aff6
Packit Service 8ebd8e
  if (!pc || !pc->conv)
Packit Service 8ebd8e
    return false;
Packit Service f1aff6
Packit Service 8ebd8e
  return pc->conv (1, &msgp, &resp, pc->appdata_ptr) == PAM_SUCCESS;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static bool
Packit Service 8ebd8e
send_info_msg (pam_handle_t *pamh, const char *msg)
Packit Service f1aff6
{
Packit Service 8ebd8e
  return send_msg (pamh, msg, PAM_TEXT_INFO);
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static bool
Packit Service 8ebd8e
send_err_msg (pam_handle_t *pamh, const char *msg)
Packit Service f1aff6
{
Packit Service 8ebd8e
  return send_msg (pamh, msg, PAM_ERROR_MSG);
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static char *
Packit Service 8ebd8e
open_device (pam_handle_t *pamh,
Packit Service 8ebd8e
             sd_bus       *bus,
Packit Service 8ebd8e
             bool         *has_multiple_devices)
Packit Service f1aff6
{
Packit Service 8ebd8e
  pf_auto (sd_bus_error) error = SD_BUS_ERROR_NULL;
Packit Service 8ebd8e
  pf_autoptr (sd_bus_message) m = NULL;
Packit Service 8ebd8e
  size_t num_devices;
Packit Service 8ebd8e
  const char *path = NULL;
Packit Service 8ebd8e
  const char *s;
Packit Service 8ebd8e
  int r;
Packit Service 8ebd8e
Packit Service 8ebd8e
  *has_multiple_devices = false;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (sd_bus_call_method (bus,
Packit Service 8ebd8e
                          "net.reactivated.Fprint",
Packit Service 8ebd8e
                          "/net/reactivated/Fprint/Manager",
Packit Service 8ebd8e
                          "net.reactivated.Fprint.Manager",
Packit Service 8ebd8e
                          "GetDevices",
Packit Service 8ebd8e
                          &error,
Packit Service 8ebd8e
                          &m,
Packit Service 8ebd8e
                          NULL) < 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (pamh, LOG_ERR, "GetDevices failed: %s", error.message);
Packit Service 8ebd8e
      return NULL;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  r = sd_bus_message_enter_container (m, 'a', "o");
Packit Service 8ebd8e
  if (r < 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (pamh, LOG_ERR, "Failed to parse answer from GetDevices(): %d", r);
Packit Service 8ebd8e
      return NULL;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (sd_bus_message_read_basic (m, 'o', &path) < 0)
Packit Service 8ebd8e
    return NULL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  num_devices = 1;
Packit Service 8ebd8e
  while ((r = sd_bus_message_read_basic (m, 'o', &s)) > 0)
Packit Service 8ebd8e
    num_devices++;
Packit Service 8ebd8e
  *has_multiple_devices = (num_devices > 1);
Packit Service 8ebd8e
  if (debug)
Packit Service 8ebd8e
    pam_syslog (pamh, LOG_DEBUG, "Using device %s (out of %ld devices)", path, num_devices);
Packit Service 8ebd8e
Packit Service 8ebd8e
  sd_bus_message_exit_container (m);
Packit Service 8ebd8e
Packit Service 8ebd8e
  return path ? strdup (path) : NULL;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
typedef struct
Packit Service 8ebd8e
{
Packit Service 8ebd8e
  char         *dev;
Packit Service 8ebd8e
  bool          has_multiple_devices;
Packit Service 8ebd8e
Packit Service 8ebd8e
  unsigned      max_tries;
Packit Service 8ebd8e
  char         *result;
Packit Service 8ebd8e
  bool          timed_out;
Packit Service 8ebd8e
  bool          is_swipe;
Packit Service 8ebd8e
  bool          verify_started;
Packit Service 8ebd8e
  int           verify_ret;
Packit Service 8ebd8e
  pam_handle_t *pamh;
Packit Service 8ebd8e
Packit Service 8ebd8e
  char         *driver;
Packit Service 8ebd8e
} verify_data;
Packit Service f1aff6
Packit Service 8ebd8e
static void
Packit Service 8ebd8e
verify_data_free (verify_data *data)
Packit Service f1aff6
{
Packit Service 8ebd8e
  free (data->result);
Packit Service 8ebd8e
  free (data->driver);
Packit Service 8ebd8e
  free (data->dev);
Packit Service 8ebd8e
  free (data);
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
PF_DEFINE_AUTOPTR_CLEANUP_FUNC (verify_data, verify_data_free)
Packit Service f1aff6
Packit Service 8ebd8e
static int
Packit Service 8ebd8e
verify_result (sd_bus_message *m,
Packit Service 8ebd8e
               void           *userdata,
Packit Service 8ebd8e
               sd_bus_error   *ret_error)
Packit Service 8ebd8e
{
Packit Service 8ebd8e
  verify_data *data = userdata;
Packit Service 8ebd8e
  const char *msg;
Packit Service 8ebd8e
  const char *result = NULL;
Packit Service 8ebd8e
  /* see https://github.com/systemd/systemd/issues/14643 */
Packit Service 8ebd8e
  uint64_t done = false;
Packit Service 8ebd8e
  int r;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (!sd_bus_message_is_signal (m, "net.reactivated.Fprint.Device", "VerifyStatus"))
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (data->pamh, LOG_ERR, "Not the signal we expected (iface: %s, member: %s)",
Packit Service 8ebd8e
                  sd_bus_message_get_interface (m),
Packit Service 8ebd8e
                  sd_bus_message_get_member (m));
Packit Service 8ebd8e
      return 0;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  if ((r = sd_bus_message_read (m, "sb", &result, &done)) < 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (data->pamh, LOG_ERR, "Failed to parse VerifyResult signal: %d", r);
Packit Service 8ebd8e
      data->verify_ret = PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
      return 0;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (!data->verify_started)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (data->pamh, LOG_ERR, "Unexpected VerifyResult '%s', %" PRIu64 " signal", result, done);
Packit Service 8ebd8e
      return 0;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (debug)
Packit Service 8ebd8e
    pam_syslog (data->pamh, LOG_DEBUG, "Verify result: %s (done: %d)", result, done ? 1 : 0);
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (data->result)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      free (data->result);
Packit Service 8ebd8e
      data->result = NULL;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (done && result)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      data->result = strdup (result);
Packit Service 8ebd8e
      return 0;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  msg = verify_result_str_to_msg (result, data->is_swipe);
Packit Service 8ebd8e
  if (!msg)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      data->result = strdup ("Protocol error with fprintd!");
Packit Service 8ebd8e
      return 0;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
  send_err_msg (data->pamh, msg);
Packit Service 8ebd8e
Packit Service 8ebd8e
  return 0;
Packit Service 8ebd8e
}
Packit Service f1aff6
Packit Service 8ebd8e
static int
Packit Service 8ebd8e
verify_finger_selected (sd_bus_message *m,
Packit Service 8ebd8e
                        void           *userdata,
Packit Service 8ebd8e
                        sd_bus_error   *ret_error)
Packit Service f1aff6
{
Packit Service 8ebd8e
  verify_data *data = userdata;
Packit Service 8ebd8e
  const char *finger_name = NULL;
Packit Service 8ebd8e
  pf_autofree char *msg = NULL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (sd_bus_message_read_basic (m, 's', &finger_name) < 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (data->pamh, LOG_ERR, "Failed to parse VerifyFingerSelected signal: %d", errno);
Packit Service 8ebd8e
      data->verify_ret = PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
      return 0;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (!data->verify_started)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (data->pamh, LOG_ERR, "Unexpected VerifyFingerSelected %s signal", finger_name);
Packit Service 8ebd8e
      return 0;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  msg = finger_str_to_msg (finger_name, data->driver, data->is_swipe);
Packit Service 8ebd8e
  if (!msg)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      data->result = strdup ("Protocol error with fprintd!");
Packit Service 8ebd8e
      return 0;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
  if (debug)
Packit Service 8ebd8e
    pam_syslog (data->pamh, LOG_DEBUG, "verify_finger_selected %s", msg);
Packit Service 8ebd8e
  send_info_msg (data->pamh, msg);
Packit Service 8ebd8e
Packit Service 8ebd8e
  return 0;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
/* See https://github.com/systemd/systemd/issues/14636 */
Packit Service 8ebd8e
static int
Packit Service 8ebd8e
get_property_string (sd_bus       *bus,
Packit Service 8ebd8e
                     const char   *destination,
Packit Service 8ebd8e
                     const char   *path,
Packit Service 8ebd8e
                     const char   *interface,
Packit Service 8ebd8e
                     const char   *member,
Packit Service 8ebd8e
                     sd_bus_error *error,
Packit Service 8ebd8e
                     char        **ret)
Packit Service f1aff6
{
Packit Service f1aff6
Packit Service 8ebd8e
  pf_autoptr (sd_bus_message) reply = NULL;
Packit Service 8ebd8e
  const char *s;
Packit Service 8ebd8e
  char *n;
Packit Service 8ebd8e
  int r;
Packit Service 8ebd8e
Packit Service 8ebd8e
  r = sd_bus_call_method (bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", interface, member);
Packit Service 8ebd8e
  if (r < 0)
Packit Service 8ebd8e
    return r;
Packit Service 8ebd8e
Packit Service 8ebd8e
  r = sd_bus_message_enter_container (reply, 'v', "s");
Packit Service 8ebd8e
  if (r < 0)
Packit Service 8ebd8e
    return sd_bus_error_set_errno (error, r);
Packit Service 8ebd8e
Packit Service 8ebd8e
  r = sd_bus_message_read_basic (reply, 's', &s);
Packit Service 8ebd8e
  if (r < 0)
Packit Service 8ebd8e
    return sd_bus_error_set_errno (error, r);
Packit Service 8ebd8e
Packit Service 8ebd8e
  n = strdup (s);
Packit Service 8ebd8e
  if (!n)
Packit Service 8ebd8e
    return sd_bus_error_set_errno (error, -ENOMEM);
Packit Service f1aff6
Packit Service 8ebd8e
  *ret = n;
Packit Service 8ebd8e
  return 0;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
Packit Service 8ebd8e
static int
Packit Service 8ebd8e
verify_started_cb (sd_bus_message *m,
Packit Service 8ebd8e
                   void           *userdata,
Packit Service 8ebd8e
                   sd_bus_error   *ret_error)
Packit Service f1aff6
{
Packit Service 8ebd8e
  const sd_bus_error *error = sd_bus_message_get_error (m);
Packit Service 8ebd8e
  verify_data *data = userdata;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (error)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      if (sd_bus_error_has_name (error, "net.reactivated.Fprint.Error.NoEnrolledPrints"))
Packit Service 8ebd8e
        {
Packit Service 8ebd8e
          pam_syslog (data->pamh, LOG_DEBUG, "No prints enrolled");
Packit Service 8ebd8e
          data->verify_ret = PAM_USER_UNKNOWN;
Packit Service 8ebd8e
        }
Packit Service 8ebd8e
      else
Packit Service 8ebd8e
        {
Packit Service 8ebd8e
          data->verify_ret = PAM_AUTH_ERR;
Packit Service 8ebd8e
        }
Packit Service 8ebd8e
Packit Service 8ebd8e
      if (debug)
Packit Service 8ebd8e
        pam_syslog (data->pamh, LOG_DEBUG, "VerifyStart failed: %s", error->message);
Packit Service 8ebd8e
Packit Service 8ebd8e
      return 1;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (debug)
Packit Service 8ebd8e
    pam_syslog (data->pamh, LOG_DEBUG, "VerifyStart completed successfully");
Packit Service f1aff6
Packit Service 8ebd8e
  data->verify_started = true;
Packit Service f1aff6
Packit Service 8ebd8e
  return 1;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static int
Packit Service 8ebd8e
do_verify (sd_bus      *bus,
Packit Service 8ebd8e
           verify_data *data)
Packit Service f1aff6
{
Packit Service 8ebd8e
  pf_autoptr (sd_bus_slot) verify_status_slot = NULL;
Packit Service 8ebd8e
  pf_autoptr (sd_bus_slot) verify_finger_selected_slot = NULL;
Packit Service 8ebd8e
  pf_autofree char *scan_type = NULL;
Packit Service 8ebd8e
  int r;
Packit Service 8ebd8e
Packit Service 8ebd8e
  /* Get some properties for the device */
Packit Service 8ebd8e
  r = get_property_string (bus,
Packit Service 8ebd8e
                           "net.reactivated.Fprint",
Packit Service 8ebd8e
                           data->dev,
Packit Service 8ebd8e
                           "net.reactivated.Fprint.Device",
Packit Service 8ebd8e
                           "scan-type",
Packit Service 8ebd8e
                           NULL,
Packit Service 8ebd8e
                           &scan_type);
Packit Service 8ebd8e
  if (r < 0)
Packit Service 8ebd8e
    pam_syslog (data->pamh, LOG_ERR, "Failed to get scan-type for %s: %d", data->dev, r);
Packit Service 8ebd8e
  if (debug)
Packit Service 8ebd8e
    pam_syslog (data->pamh, LOG_DEBUG, "scan-type for %s: %s", data->dev, scan_type);
Packit Service 8ebd8e
  if (str_equal (scan_type, "swipe"))
Packit Service 8ebd8e
    data->is_swipe = true;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (data->has_multiple_devices)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      get_property_string (bus,
Packit Service 8ebd8e
                           "net.reactivated.Fprint",
Packit Service 8ebd8e
                           data->dev,
Packit Service 8ebd8e
                           "net.reactivated.Fprint.Device",
Packit Service 8ebd8e
                           "name",
Packit Service 8ebd8e
                           NULL,
Packit Service 8ebd8e
                           &data->driver);
Packit Service 8ebd8e
      if (r < 0)
Packit Service 8ebd8e
        pam_syslog (data->pamh, LOG_ERR, "Failed to get driver name for %s: %d", data->dev, r);
Packit Service 8ebd8e
      if (debug && r == 0)
Packit Service 8ebd8e
        pam_syslog (data->pamh, LOG_DEBUG, "driver name for %s: %s", data->dev, data->driver);
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  sd_bus_match_signal (bus,
Packit Service 8ebd8e
                       &verify_status_slot,
Packit Service 8ebd8e
                       "net.reactivated.Fprint",
Packit Service 8ebd8e
                       data->dev,
Packit Service 8ebd8e
                       "net.reactivated.Fprint.Device",
Packit Service 8ebd8e
                       "VerifyStatus",
Packit Service 8ebd8e
                       verify_result,
Packit Service 8ebd8e
                       data);
Packit Service 8ebd8e
Packit Service 8ebd8e
  sd_bus_match_signal (bus,
Packit Service 8ebd8e
                       &verify_finger_selected_slot,
Packit Service 8ebd8e
                       "net.reactivated.Fprint",
Packit Service 8ebd8e
                       data->dev,
Packit Service 8ebd8e
                       "net.reactivated.Fprint.Device",
Packit Service 8ebd8e
                       "VerifyFingerSelected",
Packit Service 8ebd8e
                       verify_finger_selected,
Packit Service 8ebd8e
                       data);
Packit Service 8ebd8e
Packit Service 8ebd8e
  while (data->max_tries > 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      uint64_t verification_end = now () + (timeout * USEC_PER_SEC);
Packit Service 8ebd8e
Packit Service 8ebd8e
      data->timed_out = false;
Packit Service 8ebd8e
      data->verify_started = false;
Packit Service 8ebd8e
      data->verify_ret = PAM_INCOMPLETE;
Packit Service 8ebd8e
Packit Service 8ebd8e
      free (data->result);
Packit Service 8ebd8e
      data->result = NULL;
Packit Service 8ebd8e
Packit Service 8ebd8e
      if (debug)
Packit Service 8ebd8e
        pam_syslog (data->pamh, LOG_DEBUG, "About to call VerifyStart");
Packit Service 8ebd8e
Packit Service 8ebd8e
      r = sd_bus_call_method_async (bus,
Packit Service 8ebd8e
                                    NULL,
Packit Service 8ebd8e
                                    "net.reactivated.Fprint",
Packit Service 8ebd8e
                                    data->dev,
Packit Service 8ebd8e
                                    "net.reactivated.Fprint.Device",
Packit Service 8ebd8e
                                    "VerifyStart",
Packit Service 8ebd8e
                                    verify_started_cb,
Packit Service 8ebd8e
                                    data,
Packit Service 8ebd8e
                                    "s",
Packit Service 8ebd8e
                                    "any");
Packit Service 8ebd8e
Packit Service 8ebd8e
      if (r < 0)
Packit Service 8ebd8e
        {
Packit Service 8ebd8e
          if (debug)
Packit Service 8ebd8e
            pam_syslog (data->pamh, LOG_DEBUG, "VerifyStart call failed: %d", r);
Packit Service 8ebd8e
          break;
Packit Service 8ebd8e
        }
Packit Service 8ebd8e
Packit Service 8ebd8e
      for (;;)
Packit Service 8ebd8e
        {
Packit Service 8ebd8e
          int64_t wait_time;
Packit Service 8ebd8e
Packit Service 8ebd8e
          wait_time = verification_end - now ();
Packit Service 8ebd8e
          if (wait_time <= 0)
Packit Service 8ebd8e
            break;
Packit Service 8ebd8e
Packit Service 8ebd8e
          r = sd_bus_process (bus, NULL);
Packit Service 8ebd8e
          if (r < 0)
Packit Service 8ebd8e
            break;
Packit Service 8ebd8e
          if (data->verify_ret != PAM_INCOMPLETE)
Packit Service 8ebd8e
            break;
Packit Service 8ebd8e
          if (!data->verify_started)
Packit Service 8ebd8e
            continue;
Packit Service 8ebd8e
          if (data->result != NULL)
Packit Service 8ebd8e
            break;
Packit Service 8ebd8e
          if (r == 0)
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              if (debug)
Packit Service 8ebd8e
                {
Packit Service 8ebd8e
                  pam_syslog (data->pamh, LOG_DEBUG,
Packit Service 8ebd8e
                              "Waiting for %" PRId64 " seconds (%" PRId64 " usecs)",
Packit Service 8ebd8e
                              wait_time / USEC_PER_SEC,
Packit Service 8ebd8e
                              wait_time);
Packit Service 8ebd8e
                }
Packit Service 8ebd8e
              if (sd_bus_wait (bus, wait_time) < 0)
Packit Service 8ebd8e
                break;
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
        }
Packit Service 8ebd8e
Packit Service 8ebd8e
      if (data->verify_ret != PAM_INCOMPLETE)
Packit Service 8ebd8e
        return data->verify_ret;
Packit Service 8ebd8e
Packit Service 8ebd8e
      if (now () >= verification_end)
Packit Service 8ebd8e
        {
Packit Service 8ebd8e
          data->timed_out = true;
Packit Service 8ebd8e
          send_info_msg (data->pamh, _("Verification timed out"));
Packit Service 8ebd8e
        }
Packit Service 8ebd8e
Packit Service 8ebd8e
      /* Ignore errors from VerifyStop */
Packit Service 8ebd8e
      data->verify_started = false;
Packit Service 8ebd8e
      sd_bus_call_method (bus,
Packit Service 8ebd8e
                          "net.reactivated.Fprint",
Packit Service 8ebd8e
                          data->dev,
Packit Service 8ebd8e
                          "net.reactivated.Fprint.Device",
Packit Service 8ebd8e
                          "VerifyStop",
Packit Service 8ebd8e
                          NULL,
Packit Service 8ebd8e
                          NULL,
Packit Service 8ebd8e
                          NULL,
Packit Service 8ebd8e
                          NULL);
Packit Service 8ebd8e
Packit Service 8ebd8e
      if (data->timed_out)
Packit Service 8ebd8e
        {
Packit Service 8ebd8e
          return PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
        }
Packit Service 8ebd8e
      else
Packit Service 8ebd8e
        {
Packit Service 8ebd8e
          if (str_equal (data->result, "verify-no-match"))
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              send_err_msg (data->pamh, "Failed to match fingerprint");
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
          else if (str_equal (data->result, "verify-match"))
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              return PAM_SUCCESS;
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
          else if (str_equal (data->result, "verify-unknown-error"))
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              return PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
          else if (str_equal (data->result, "verify-disconnected"))
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              return PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
          else
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              send_err_msg (data->pamh, _("An unknown error occurred"));
Packit Service 8ebd8e
              return PAM_AUTH_ERR;
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
        }
Packit Service 8ebd8e
      data->max_tries--;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (data->max_tries == 0)
Packit Service 8ebd8e
    return PAM_MAXTRIES;
Packit Service 8ebd8e
Packit Service 8ebd8e
  return PAM_AUTH_ERR;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static bool
Packit Service 8ebd8e
user_has_prints (pam_handle_t *pamh,
Packit Service 8ebd8e
                 sd_bus       *bus,
Packit Service 8ebd8e
                 const char   *dev,
Packit Service 8ebd8e
                 const char   *username)
Packit Service f1aff6
{
Packit Service 8ebd8e
  pf_auto (sd_bus_error) error = SD_BUS_ERROR_NULL;
Packit Service 8ebd8e
  pf_autoptr (sd_bus_message) m = NULL;
Packit Service 8ebd8e
  size_t num_fingers = 0;
Packit Service 8ebd8e
  const char *s;
Packit Service 8ebd8e
  int r;
Packit Service 8ebd8e
Packit Service 8ebd8e
  r = sd_bus_call_method (bus,
Packit Service 8ebd8e
                          "net.reactivated.Fprint",
Packit Service 8ebd8e
                          dev,
Packit Service 8ebd8e
                          "net.reactivated.Fprint.Device",
Packit Service 8ebd8e
                          "ListEnrolledFingers",
Packit Service 8ebd8e
                          &error,
Packit Service 8ebd8e
                          &m,
Packit Service 8ebd8e
                          "s",
Packit Service 8ebd8e
                          username);
Packit Service 8ebd8e
  if (r < 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      /* If ListEnrolledFingers fails then verification should
Packit Service 8ebd8e
       * also fail (both use the same underlying call), so we
Packit Service 8ebd8e
       * report false here and bail out early.  */
Packit Service 8ebd8e
      if (debug)
Packit Service 8ebd8e
        pam_syslog (pamh, LOG_DEBUG, "ListEnrolledFingers failed for %s: %s",
Packit Service 8ebd8e
                    username, error.message);
Packit Service 8ebd8e
      return false;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  r = sd_bus_message_enter_container (m, 'a', "s");
Packit Service 8ebd8e
  if (r < 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (pamh, LOG_ERR, "Failed to parse answer from ListEnrolledFingers(): %d", r);
Packit Service 8ebd8e
      return false;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  while ((r = sd_bus_message_read_basic (m, 's', &s)) > 0)
Packit Service 8ebd8e
    num_fingers++;
Packit Service 8ebd8e
  sd_bus_message_exit_container (m);
Packit Service 8ebd8e
Packit Service 8ebd8e
  return num_fingers > 0;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static void
Packit Service 8ebd8e
release_device (pam_handle_t *pamh,
Packit Service 8ebd8e
                sd_bus       *bus,
Packit Service 8ebd8e
                const char   *dev)
Packit Service f1aff6
{
Packit Service 8ebd8e
  pf_auto (sd_bus_error) error = SD_BUS_ERROR_NULL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (sd_bus_call_method (bus,
Packit Service 8ebd8e
                          "net.reactivated.Fprint",
Packit Service 8ebd8e
                          dev,
Packit Service 8ebd8e
                          "net.reactivated.Fprint.Device",
Packit Service 8ebd8e
                          "Release",
Packit Service 8ebd8e
                          &error,
Packit Service 8ebd8e
                          NULL,
Packit Service 8ebd8e
                          NULL,
Packit Service 8ebd8e
                          NULL) < 0)
Packit Service 8ebd8e
    pam_syslog (pamh, LOG_ERR, "ReleaseDevice failed: %s", error.message);
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static bool
Packit Service 8ebd8e
claim_device (pam_handle_t *pamh,
Packit Service 8ebd8e
              sd_bus       *bus,
Packit Service 8ebd8e
              const char   *dev,
Packit Service 8ebd8e
              const char   *username)
Packit Service f1aff6
{
Packit Service 8ebd8e
  pf_auto (sd_bus_error) error = SD_BUS_ERROR_NULL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (sd_bus_call_method (bus,
Packit Service 8ebd8e
                          "net.reactivated.Fprint",
Packit Service 8ebd8e
                          dev,
Packit Service 8ebd8e
                          "net.reactivated.Fprint.Device",
Packit Service 8ebd8e
                          "Claim",
Packit Service 8ebd8e
                          &error,
Packit Service 8ebd8e
                          NULL,
Packit Service 8ebd8e
                          "s",
Packit Service 8ebd8e
                          username) < 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      if (debug)
Packit Service 8ebd8e
        pam_syslog (pamh, LOG_DEBUG, "failed to claim device %s", error.message);
Packit Service 8ebd8e
      return false;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  return true;
Packit Service 8ebd8e
}
Packit Service 8ebd8e
Packit Service 8ebd8e
static int
Packit Service 8ebd8e
name_owner_changed (sd_bus_message *m,
Packit Service 8ebd8e
                    void           *userdata,
Packit Service 8ebd8e
                    sd_bus_error   *ret_error)
Packit Service 8ebd8e
{
Packit Service 8ebd8e
  verify_data *data = userdata;
Packit Service 8ebd8e
  const char *name = NULL;
Packit Service 8ebd8e
  const char *old_owner = NULL;
Packit Service 8ebd8e
  const char *new_owner = NULL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (sd_bus_message_read (m, "sss", &name, &old_owner, &new_owner) < 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (data->pamh, LOG_ERR, "Failed to parse NameOwnerChanged signal: %d", errno);
Packit Service 8ebd8e
      data->verify_ret = PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
      return 0;
Packit Service 8ebd8e
    }
Packit Service f1aff6
Packit Service 8ebd8e
  if (strcmp (name, "net.reactivated.Fprint") != 0)
Packit Service 8ebd8e
    return 0;
Packit Service f1aff6
Packit Service 8ebd8e
  /* Name owner for fprintd changed, give up as we might start listening
Packit Service 8ebd8e
   * to events from a new name owner otherwise. */
Packit Service 8ebd8e
  data->verify_ret = PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  pam_syslog (data->pamh, LOG_WARNING, "fprintd name owner changed during operation!");
Packit Service 8ebd8e
Packit Service 8ebd8e
  return 0;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static int
Packit Service 8ebd8e
do_auth (pam_handle_t *pamh, const char *username)
Packit Service f1aff6
{
Packit Service 8ebd8e
  bool have_prints;
Packit Service 8ebd8e
Packit Service 8ebd8e
  pf_autoptr (verify_data) data = NULL;
Packit Service 8ebd8e
  pf_autoptr (sd_bus) bus = NULL;
Packit Service 8ebd8e
  pf_autoptr (sd_bus_slot) name_owner_changed_slot = NULL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  data = calloc (1, sizeof (verify_data));
Packit Service 8ebd8e
  data->max_tries = max_tries;
Packit Service 8ebd8e
  data->pamh = pamh;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (sd_bus_open_system (&bus) < 0)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      pam_syslog (pamh, LOG_ERR, "Error with getting the bus: %d", errno);
Packit Service 8ebd8e
      return PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  data->dev = open_device (pamh, bus, &data->has_multiple_devices);
Packit Service 8ebd8e
  if (data->dev == NULL)
Packit Service 8ebd8e
    return PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  have_prints = user_has_prints (pamh, bus, data->dev, username);
Packit Service 8ebd8e
  if (debug)
Packit Service 8ebd8e
    pam_syslog (pamh, LOG_DEBUG, "prints registered: %s\n", have_prints ? "yes" : "no");
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (!have_prints)
Packit Service 8ebd8e
    return PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  /* Only connect to NameOwnerChanged when needed. In case of automatic startup
Packit Service 8ebd8e
   * we rely on the fact that we never see those signals.
Packit Service 8ebd8e
   */
Packit Service 8ebd8e
  name_owner_changed_slot = NULL;
Packit Service 8ebd8e
  sd_bus_match_signal (bus,
Packit Service 8ebd8e
                       &name_owner_changed_slot,
Packit Service 8ebd8e
                       "org.freedesktop.DBus",
Packit Service 8ebd8e
                       "/org/freedesktop/DBus",
Packit Service 8ebd8e
                       "org.freedesktop.DBus",
Packit Service 8ebd8e
                       "NameOwnerChanged",
Packit Service 8ebd8e
                       name_owner_changed,
Packit Service 8ebd8e
                       data);
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (claim_device (pamh, bus, data->dev, username))
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      int ret = do_verify (bus, data);
Packit Service 8ebd8e
      release_device (pamh, bus, data->dev);
Packit Service 8ebd8e
      return ret;
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  return PAM_AUTHINFO_UNAVAIL;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
static bool
Packit Service 8ebd8e
is_remote (pam_handle_t *pamh)
Packit Service f1aff6
{
Packit Service 8ebd8e
  const char *rhost = NULL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  pam_get_item (pamh, PAM_RHOST, (const void **) (const void *) &rhost);
Packit Service 8ebd8e
Packit Service 8ebd8e
  /* NULL or empty rhost if the host information is not available or set.
Packit Service 8ebd8e
   * "localhost" if the host is local.
Packit Service 8ebd8e
   * We want to not run for known remote hosts */
Packit Service 8ebd8e
  if (rhost != NULL &&
Packit Service 8ebd8e
      *rhost != '\0' &&
Packit Service 8ebd8e
      strcmp (rhost, "localhost") != 0)
Packit Service 8ebd8e
    return true;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (sd_session_is_remote (NULL) > 0)
Packit Service 8ebd8e
    return true;
Packit Service 8ebd8e
Packit Service 8ebd8e
  return false;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
PAM_EXTERN int
Packit Service 8ebd8e
pam_sm_authenticate (pam_handle_t *pamh, int flags, int argc,
Packit Service 8ebd8e
                     const char **argv)
Packit Service f1aff6
{
Packit Service 8ebd8e
  const char *username;
Packit Service 8ebd8e
  int i;
Packit Service 8ebd8e
Packit Service 8ebd8e
  bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
Packit Service 8ebd8e
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (is_remote (pamh))
Packit Service 8ebd8e
    return PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  if (pam_get_user (pamh, &username, NULL) != PAM_SUCCESS)
Packit Service 8ebd8e
    return PAM_AUTHINFO_UNAVAIL;
Packit Service 8ebd8e
Packit Service 8ebd8e
  for (i = 0; i < argc; i++)
Packit Service 8ebd8e
    {
Packit Service 8ebd8e
      if (argv[i] != NULL)
Packit Service 8ebd8e
        {
Packit Service 8ebd8e
          if (str_equal (argv[i], "debug"))
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              pam_syslog (pamh, LOG_DEBUG, "debug on");
Packit Service 8ebd8e
              debug = true;
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
          else if (str_has_prefix (argv[i], DEBUG_MATCH))
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              pam_syslog (pamh, LOG_DEBUG, "debug on");
Packit Service 8ebd8e
              const char *value;
Packit Service 8ebd8e
Packit Service 8ebd8e
              value = argv[i] + strlen (DEBUG_MATCH);
Packit Service 8ebd8e
              if (str_equal (value, "on") ||
Packit Service 8ebd8e
                  str_equal (value, "true") ||
Packit Service 8ebd8e
                  str_equal (value, "1"))
Packit Service 8ebd8e
                {
Packit Service 8ebd8e
                  pam_syslog (pamh, LOG_DEBUG, "debug on");
Packit Service 8ebd8e
                  debug = true;
Packit Service 8ebd8e
                }
Packit Service 8ebd8e
              else if (str_equal (value, "off") ||
Packit Service 8ebd8e
                       str_equal (value, "false") ||
Packit Service 8ebd8e
                       str_equal (value, "0"))
Packit Service 8ebd8e
                {
Packit Service 8ebd8e
                  debug = false;
Packit Service 8ebd8e
                }
Packit Service 8ebd8e
              else
Packit Service 8ebd8e
                {
Packit Service 8ebd8e
                  pam_syslog (pamh, LOG_DEBUG, "invalid debug value '%s', disabling", value);
Packit Service 8ebd8e
                }
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
          else if (str_has_prefix (argv[i], MAX_TRIES_MATCH) && strlen (argv[i]) == strlen (MAX_TRIES_MATCH) + 1)
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              max_tries = atoi (argv[i] + strlen (MAX_TRIES_MATCH));
Packit Service 8ebd8e
              if (max_tries < 1)
Packit Service 8ebd8e
                {
Packit Service 8ebd8e
                  if (debug)
Packit Service 8ebd8e
                    pam_syslog (pamh, LOG_DEBUG, "invalid max tries '%s', using %d",
Packit Service 8ebd8e
                                argv[i] + strlen (MAX_TRIES_MATCH), DEFAULT_MAX_TRIES);
Packit Service 8ebd8e
                  max_tries = DEFAULT_MAX_TRIES;
Packit Service 8ebd8e
                }
Packit Service 8ebd8e
              if (debug)
Packit Service 8ebd8e
                pam_syslog (pamh, LOG_DEBUG, "max_tries specified as: %d", max_tries);
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
          else if (str_has_prefix (argv[i], TIMEOUT_MATCH) && strlen (argv[i]) <= strlen (TIMEOUT_MATCH) + 2)
Packit Service 8ebd8e
            {
Packit Service 8ebd8e
              timeout = atoi (argv[i] + strlen (TIMEOUT_MATCH));
Packit Service 8ebd8e
              if (timeout < MIN_TIMEOUT)
Packit Service 8ebd8e
                {
Packit Service 8ebd8e
                  if (debug)
Packit Service 8ebd8e
                    pam_syslog (pamh, LOG_DEBUG, "timeout %d secs too low, using %d",
Packit Service 8ebd8e
                                timeout, MIN_TIMEOUT);
Packit Service 8ebd8e
                  timeout = MIN_TIMEOUT;
Packit Service 8ebd8e
                }
Packit Service 8ebd8e
              else if (debug)
Packit Service 8ebd8e
                {
Packit Service 8ebd8e
                  pam_syslog (pamh, LOG_DEBUG, "timeout specified as: %d secs", timeout);
Packit Service 8ebd8e
                }
Packit Service 8ebd8e
            }
Packit Service 8ebd8e
        }
Packit Service 8ebd8e
    }
Packit Service 8ebd8e
Packit Service 8ebd8e
  return do_auth (pamh, username);
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
PAM_EXTERN int
Packit Service 8ebd8e
pam_sm_setcred (pam_handle_t *pamh, int flags, int argc,
Packit Service 8ebd8e
                const char **argv)
Packit Service f1aff6
{
Packit Service 8ebd8e
  return PAM_SUCCESS;
Packit Service f1aff6
}
Packit Service f1aff6
Packit Service 8ebd8e
PAM_EXTERN int
Packit Service 8ebd8e
pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc,
Packit Service 8ebd8e
                  const char **argv)
Packit Service 8ebd8e
{
Packit Service 8ebd8e
  return PAM_SUCCESS;
Packit Service 8ebd8e
}