File google-authenticator-libpam-1.10.obscpio of Package google-authenticator-libpam

07070100000000000041ED0000000000000000000000026627DD8100000000000000000000000000000000000000000000002900000000google-authenticator-libpam-1.10/.github07070100000001000041ED0000000000000000000000026627DD8100000000000000000000000000000000000000000000003800000000google-authenticator-libpam-1.10/.github/ISSUE_TEMPLATE07070100000002000081A40000000000000000000000016627DD81000002DF000000000000000000000000000000000000004600000000google-authenticator-libpam-1.10/.github/ISSUE_TEMPLATE/bug_report.md---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

### Describe the bug
A clear and concise description of what the bug is.

### To Reproduce
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

### Expected behavior
A clear and concise description of what you expected to happen.

### Config. E.g. `/etc/ssh/sshd_config`

```
Paste the config here
```

### Logs: `/var/log/auth.log` or equivalent

```
Paste the relevant log lines here
```

### Environment
 - OS: [e.g. Ubuntu]
 - Version [e.g. 22.04]
- Do you use selinux? (check with e.g. `sestatus`): ___

### Additional context
Add any other context about the problem here.
07070100000003000081A40000000000000000000000016627DD81000002AE000000000000000000000000000000000000004E00000000google-authenticator-libpam-1.10/.github/ISSUE_TEMPLATE/configuration-help.md---
name: Configuration help
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''

---

### System information
Operating system (e.g. Ubuntu 22.04): ____
Do you use selinux? (check with e.g. `sestatus`): ___

### Steps to reproduce

1. …

### What I expected would happen

FILL IN

### What actually happened

FILL IN

### PAM config

Paste the relevant parts of your PAM config

```
paste here
```

### If SSH: SSH config

Paste the relevant parts of `/etc/ssh/sshd_config` or equivalent.

```
paste here
```

### If not SSH: That program's config, and logs

```
paste here
```

### Contents of `/var/log/auth.log` or equivalent

```
paste here
```
07070100000004000081A40000000000000000000000016627DD8100000253000000000000000000000000000000000000004B00000000google-authenticator-libpam-1.10/.github/ISSUE_TEMPLATE/feature_request.md---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.
07070100000005000081A40000000000000000000000016627DD8100000294000000000000000000000000000000000000005700000000google-authenticator-libpam-1.10/.github/ISSUE_TEMPLATE/totp-codes-are-not-accepted.md---
name: TOTP codes are not accepted
about: OTP codes are configured, but don't seem to be accepted, or only sometimes
  accepted
title: 'TOTP not accepted: '
labels: ''
assignees: ''

---

## Prerequisite

Read [this](https://twitter.com/thomashabets/status/1133780752582217728), have a little chuckle to yourself.

After that: no seriously really do confirm it. Most of the reported issues with TOTP follow that pattern.

## PAM config

E.g. `/etc/pam.d/ssh`, or `common-auth` if it uses that.

## SSH config (or equivalent if not using SSH)

`/etc/ssh/sshd_config`

## Enable `debug` on the module, and paste what's logged

Maybe from `/var/log/auth.log`.
07070100000006000081A40000000000000000000000016627DD8100001093000000000000000000000000000000000000002C00000000google-authenticator-libpam-1.10/.gitignore### Project specific ignores
.deps
.dirstamp
cmake-build-debug
libtool

contrib/rpm.spec

### Project binaries
google-authenticator
examples/demo
.libs

### Project tests
test-suite.log
tests/pam_google_authenticator_unittest
tests/*.log
tests/*.trs

### Autotools template
# http://www.gnu.org/software/automake

Makefile.in
/ar-lib
/mdate-sh
/py-compile
/test-driver
/ylwrap

# http://www.gnu.org/software/autoconf

/autom4te.cache
/autoscan.log
/autoscan-*.log
/aclocal.m4
/compile
/config.guess
/config.h.in
/config.h
/config.log
/config.sub
/config.status
/configure
/configure.scan
/depcomp
/install-sh
/missing
/stamp-h1

# https://www.gnu.org/software/libtool/

/ltmain.sh

# http://www.gnu.org/software/texinfo

/texinfo.tex
### C++ template
# Prerequisites
*.d

# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

.idea/

## File-based project format:
*.iws
*.iml

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Archives template
# It's better to unpack these files and commit the raw source because
# git has its own built in compression methods.
*.7z
*.jar
*.rar
*.zip
*.gz
*.bzip
*.bz2
*.xz
*.lzma
*.cab

#packing-only formats
*.iso
*.tar

#package management formats
*.dmg
*.xpi
*.gem
*.egg
*.deb
*.rpm
*.msi
*.msm
*.msp
### Xcode template
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## Build generated
build/
DerivedData/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/

## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
### macOS template
*.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Eclipse template

.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders

# Eclipse Core
.project

# External tool builders
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
*.launch

# PyDev specific (Python IDE for Eclipse)
*.pydevproject

# CDT-specific (C/C++ Development Tooling)
.cproject

# JDT-specific (Eclipse Java Development Tools)
.classpath

# Java annotation processor (APT)
.factorypath

# PDT-specific (PHP Development Tools)
.buildpath

# sbteclipse plugin
.target

# Tern plugin
.tern-project

# TeXlipse plugin
.texlipse

# STS (Spring Tool Suite)
.springBeans

# Code Recommenders
.recommenders/

# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
### C template
# Prerequisites

# Object files
*.ko
*.elf

# Linker output
*.ilk
*.map
*.exp

# Precompiled Headers

# Libraries

# Shared objects (inc. Windows DLLs)
*.so.*

# Executables
*.i*86
*.x86_64
*.hex

# Debug files
*.dSYM/
*.su
*.idb
*.pdb

# Kernel Module Compile Results
*.mod*
*.cmd
modules.order
Module.symvers
Mkfile.old
dkms.conf
### CMake template
CMakeCache.txt
CMakeFiles
CMakeLists.txt
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
### NetBeans template
nbproject/private/
nbbuild/
dist/
nbdist/
.nb-gradle/
### Vim template
# swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
base32
07070100000007000081A40000000000000000000000016627DD81000000B7000000000000000000000000000000000000002D00000000google-authenticator-libpam-1.10/.travis.ymllanguage: c

compiler:
  - clang
  - gcc

script: ./bootstrap.sh && ./configure && make && make check

sudo: false

addons:
  apt:
    packages:
    - libpam0g-dev
    - libqrencode3
07070100000008000081A40000000000000000000000016627DD81000005AA000000000000000000000000000000000000003100000000google-authenticator-libpam-1.10/CONTRIBUTING.mdWant to contribute? Great! First, read this page (including the small print at the end).

### Before you contribute
Before we can use your code, you must sign the
[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual)
(CLA), which you can do online. The CLA is necessary mainly because you own the
copyright to your changes, even after your contribution becomes part of our
codebase, so we need your permission to use and distribute your code. We also
need to be sure of various other things—for instance that you'll tell us if you
know that your code infringes on other people's patents. You don't have to sign
the CLA until after you've submitted your code for review and a member has
approved it, but you must do it before we can put your code into our codebase.
Before you start working on a larger contribution, you should get in touch with
us first through the issue tracker with your idea so that we can help out and
possibly guide you. Coordinating up front makes it much easier to avoid
frustration later on.

### Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose.

### The small print
Contributions made by corporations are covered by a different agreement than
the one above, the
[Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).
07070100000009000081A40000000000000000000000016627DD8100000BA3000000000000000000000000000000000000002C00000000google-authenticator-libpam-1.10/FILEFORMATAll configuration data and state is kept in ~/.google_authenticator
The file is all ASCII, but is kept in a very simple-to-parse and rigid
file format.

The file size is currently limited to 1kB. This should be generous even
when using a very large list of scratch codes.

The first line is the base32 encoded secret. It uses characters in the range
A..Z2..7. The secret is used after first trying any one-time scratch codes.
HOTP mode is used only if HOTP_COUNTER is present.
TOTP mode is used only if TOTP_AUTH is present *and* HOTP_COUNTER is
not present.

The following lines are optional. They all start with a double quote character,
followed by a space character. Followed by an option name. Option names are
all upper-case and must include an underscore. This ensures that they cannot
accidentally appear anywhere else in the file.

Options can be followed by option-specific parameters.

Currently, the following options are recognized:

  DISALLOW_REUSE
    if present, this signals that a time-based token can be used
    at most once. Any attempt to log in using the same token will be denied.
    This means that users can typically not log in faster than once every
    ~30 seconds.

    The option is followed by a space-separated list of time stamps that
    have previously been used for login attempts.

    This option has no effect when HOTP_COUNTER is present.

  RATE_LIMIT n m ...
    this optional parameter restricts the number of logins to at most "n"
    within each "m" second interval. Additional parameters in this line are
    undocumented; they are used internally to keep track of state.

  TOTP_AUTH
    the presence of this option indicates that the secret can be used to
    authenticate users with a time-based token.

  STEP_SIZE n
    the number of seconds in each time step during which a TOTP code is valid.
    The default is that a code is valid for 30 seconds.

  HOTP_COUNTER n
    the presence of this option indicates that the secret can be used to
    authenticate users with a counter-based token.  The argument "n"
    represents which counter value the token will accept next.  It should
    be initialized to 1.

  WINDOW_SIZE n
    the default window size is 3, allowing up to one extra valid token
    before and after the currently active one. This might be too restrictive
    if the client and the server experience significant time skew.
    You can provide a parameter to increase the login window size from 3 to "n"

    In counter-based mode, this option is the number of valid tokens after
    the currently active one.  The default is almost certainly too restrictive
    for most users as invalid login attempts and generated-but-not-used
    tokens both contribute to synchronization problems.


Any all-numeric sequence of eight-digit numbers are randomly generated
one-time scratch code tokens. The user can enter any arbitrary
one-time code to log into his account. The code will then be removed
from the file.
0707010000000A000081A40000000000000000000000016627DD8100002C5E000000000000000000000000000000000000002900000000google-authenticator-libpam-1.10/LICENSE
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   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.
0707010000000B000081A40000000000000000000000016627DD81000009B5000000000000000000000000000000000000002D00000000google-authenticator-libpam-1.10/Makefile.amAUTOMAKE_OPTIONS = foreign subdir-objects
ACLOCAL_AMFLAGS = -I build

pamdir = $(libdir)/security

bin_PROGRAMS      = google-authenticator
noinst_PROGRAMS   = base32
dist_man_MANS     = man/google-authenticator.1
dist_man_MANS     += man/pam_google_authenticator.8
pam_LTLIBRARIES   = pam_google_authenticator.la

dist_doc_DATA  = FILEFORMAT README.md
dist_html_DATA = totp.html

MODULES_LDFLAGS = -avoid-version -module -shared -export-dynamic

CORE_SRC  = src/util.h   src/util.c
CORE_SRC += src/base32.h src/base32.c
CORE_SRC += src/hmac.h   src/hmac.c
CORE_SRC += src/sha1.h   src/sha1.c

base32_SOURCES=\
src/base32.c \
src/base32_prog.c

google_authenticator_SOURCES = \
	src/google-authenticator.c \
	$(CORE_SRC)

pam_google_authenticator_la_SOURCES = \
	src/pam_google_authenticator.c \
	$(CORE_SRC)
pam_google_authenticator_la_LIBADD  = -lpam
pam_google_authenticator_la_CFLAGS  = $(AM_CFLAGS)
pam_google_authenticator_la_LDFLAGS = $(AM_LDFLAGS) $(MODULES_LDFLAGS) -export-symbols-regex "pam_sm_(setcred|open_session|authenticate)"


check_PROGRAMS    = examples/demo tests/pam_google_authenticator_unittest
check_LTLIBRARIES = libpam_google_authenticator_testing.la
TESTS             = tests/pam_google_authenticator_unittest tests/base32_test.sh
EXTRA_DIST        = tests/base32_test.sh

libpam_google_authenticator_testing_la_SOURCES = \
	src/pam_google_authenticator.c \
	$(CORE_SRC)
libpam_google_authenticator_testing_la_CFLAGS  = $(AM_CFLAGS) -DTESTING=1
libpam_google_authenticator_testing_la_LDFLAGS = $(AM_LDFLAGS) $(MODULES_LDFLAGS) -rpath $(abs_top_builddir) -lpam

tests_pam_google_authenticator_unittest_SOURCES = \
	tests/pam_google_authenticator_unittest.c \
	$(CORE_SRC)
tests_pam_google_authenticator_unittest_LDADD   = -lpam
tests_pam_google_authenticator_unittest_LDFLAGS = $(AM_LDFLAGS) -export-dynamic

test: check


examples_demo_SOURCES = \
	src/pam_google_authenticator.c \
	$(CORE_SRC) \
	examples/demo.c
examples_demo_LDADD  = -lpam
examples_demo_CFLAGS = $(AM_CFLAGS) -DDEMO=1


super-clean: maintainer-clean
	rm -fr aclocal autom4te.cache/ m4 missing libtool config.guess
	rm -fr config.lt config.status config.sub configure depcomp
	rm -fr libtool install-sh  *~ Makefile aclocal.m4 config.h.in ltmain.sh
	rm -fr Makefile.in test-driver compile

doc:
	(cd man && pandoc --standalone --to man google-authenticator.1.md > google-authenticator.1)
	(cd man && pandoc --standalone --to man pam_google_authenticator.8.md > pam_google_authenticator.8)
0707010000000C000081A40000000000000000000000016627DD81000022F7000000000000000000000000000000000000002B00000000google-authenticator-libpam-1.10/README.md# Google Authenticator PAM module

Example PAM module demonstrating two-factor authentication for logging
into servers via SSH, OpenVPN, etc…

This project is not about logging in to Google, Facebook, or other
TOTP/HOTP second factor systems, even if they recommend using the
Google Authenticator apps.

HMAC-Based One-time Password (HOTP) is specified in
[RFC 4226](https://tools.ietf.org/html/rfc4226) and 
Time-based One-time Password (TOTP) is specified in
[RFC 6238](https://tools.ietf.org/html/rfc6238).

[![Build Status](https://travis-ci.org/google/google-authenticator-libpam.svg?branch=master)](https://travis-ci.org/google/google-authenticator-libpam)

## Build & install
```shell
./bootstrap.sh
./configure
make
sudo make install
```

If you don't have access to "sudo", you have to manually become "root" prior
to calling "make install".

## Setting up the PAM module for your system

For highest security, make sure that both password and OTP are being requested
even if password and/or OTP are incorrect. This means that *at least* the first
of `pam_unix.so` (or whatever other module is used to verify passwords) and
`pam_google_authenticator.so` should be set as `required`, not `requisite`. It
probably can't hurt to have both be `required`, but it could depend on the rest
of your PAM config.

If you use HOTP (counter based as opposed to time based) then add the option
`no_increment_hotp` to make sure the counter isn't incremented for failed
attempts.

Add this line to your PAM configuration file:

`  auth required pam_google_authenticator.so no_increment_hotp`

## Setting up a user

Run the `google-authenticator` binary to create a new secret key in your home
directory. These settings will be stored in `~/.google_authenticator`.

If your system supports the "libqrencode" library, you will be shown a QRCode
that you can scan using the Android "Google Authenticator" application.

If your system does not have this library, you can either follow the URL that
`google-authenticator` outputs, or you have to manually enter the alphanumeric
secret key into the Android "Google Authenticator" application.

In either case, after you have added the key, click-and-hold until the context
menu shows. Then check that the key's verification value matches (this feature
might not be available in all builds of the Android application).

Each time you log into your system, you will now be prompted for your TOTP code
(time based one-time-password) or HOTP (counter-based), depending on options
given to `google-authenticator`, after having entered your normal user id and
your normal UNIX account password.

During the initial roll-out process, you might find that not all users have
created a secret key yet. If you would still like them to be able to log
in, you can pass the "nullok" option on the module's command line:

`  auth required pam_google_authenticator.so nullok`

## Encrypted home directories

If your system encrypts home directories until after your users entered their
password, you either have to re-arrange the entries in the PAM configuration
file to decrypt the home directory prior to asking for the OTP code, or
you have to store the secret file in a non-standard location:

`  auth required pam_google_authenticator.so secret=/var/unencrypted-home/${USER}/.google_authenticator`

would be a possible choice. Make sure to set appropriate permissions. You also
have to tell your users to manually move their .google_authenticator file to
this location.

In addition to "${USER}", the `secret=` option also recognizes both "~" and
`${HOME}` as short-hands for the user's home directory.

When using the `secret=` option, you might want to also set the `user=`
option. The latter forces the PAM module to switch to a dedicated hard-coded
user id prior to doing any file operations. When using the `user=` option, you
must not include "~" or "${HOME}" in the filename.

The `user=` option can also be useful if you want to authenticate users who do
not have traditional UNIX accounts on your system.

## Module options

### secret=/path/to/secret/file

See "encrypted home directories", above.

### authtok_prompt=prompt

Overrides default token prompt. If you want to include spaces in the prompt,
wrap the whole argument in square brackets:

`  auth required pam_google_authenticator.so [authtok_prompt=Your secret token: ]`

### user=some-user

Force the PAM module to switch to a hard-coded user id prior to doing any file
operations. Commonly used with `secret=`.

### no_strict_owner

DANGEROUS OPTION!

By default the PAM module requires that the secrets file must be owned the user
logging in (or if `user=` is specified, owned by that user). This option
disables that check.

This option can be used to allow daemons not running as root to still handle
configuration files not owned by that user, for example owned by the users
themselves.

### allowed_perm=0nnn

DANGEROUS OPTION!

By default, the PAM module requires the secrets file to be readable only by the
owner of the file (mode 0600 by default). In situations where the module is used
in a non-default configuration, an administrator may need more lenient file
permissions, or a specific setting for their use case.

### debug

Enable more verbose log messages in syslog.

### try_first_pass / use_first_pass / forward_pass

Some PAM clients cannot prompt the user for more than just the password. To
work around this problem, this PAM module supports stacking. If you pass the
`forward_pass` option, the `pam_google_authenticator` module queries the user
for both the system password and the verification code in a single prompt.
It then forwards the system password to the next PAM module, which will have
to be configured with the `use_first_pass` option.

In turn, `pam_google_authenticator` module also supports both the standard
`use_first_pass` and `try_first_pass` options. But most users would not need
to set those on the `pam_google_authenticator`.

### noskewadj

If you discover that your TOTP code never works, this is most commonly the
result of the clock on your server being different from the one on your Android
device. The PAM module makes an attempt to compensate for time skew. You can
teach it about the amount of skew that you are experiencing, by trying to log
it three times in a row. Make sure you always wait 30s (but not longer), so
that you get three distinct TOTP codes.

Some administrators prefer that time skew isn't adjusted automatically, as
doing so results in a slightly less secure system configuration. If you want
to disable it, you can do so on the module command line:

`  auth required pam_google_authenticator.so noskewadj`

### no_increment_hotp

Don't increment the counter for failed HOTP attempts.  Normally you should set
this so failed password attempts by an attacker without a token don't lock out
the authorized user.

### nullok

Allow users to log in without OTP, if they haven't set up OTP yet.

PAM requires at least one `SUCCESS` answer from a module, and `nullok`
causes this module to say `IGNORE`. This means that if this option is
used at least one other module must have said `SUCCESS`. One way to do
this is to add `auth required pam_permit.so` to the end of the PAM
config.

### echo_verification_code

By default, the PAM module does not echo the verification code when it is
entered by the user. In some situations, the administrator might prefer a
different behavior. Pass the `echo_verification_code` option to the module
in order to enable echoing.

If you would like verification codes that are counter based instead of
timebased, use the `google-authenticator` binary to generate a secret key in
your home directory with the proper option.  In this mode, clock skew is
irrelevant and the window size option now applies to how many codes beyond the
current one that would be accepted, to reduce synchronization problems.

### grace_period=seconds

If present and non-zero, provide a grace period during which a second
verification code will not be requested. Try setting seconds to 86400
to allow a full-day between requesting codes; or 3600 for an hour.

This works by adding an (IP address, timestamp) pair to the security
file after a successful one-time-password login;
only the last ten distinct IP addresses are tracked.

### allow_readonly

DANGEROUS OPTION!

With this option an attacker with ability to fill up the filesystem (flood
server with web requests, or if they have an account just fill the disk up) can
force a situation where one-time-passwords can be reused, defeating the purpose
of "one time".

By default, if the `grace_period` option is defined the PAM module requires
some free space to store the IP address and timestamp of the last login.
It could prevent access if a server has no free space or in case of an
update config file error. With the `allow_readonly` option you can ignore
any errors which could occur during config file update.
0707010000000D000081ED0000000000000000000000016627DD810000025D000000000000000000000000000000000000002E00000000google-authenticator-libpam-1.10/bootstrap.sh#!/bin/sh
# Copyright 2014 Google Inc.
#
# 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.
exec autoreconf -i
0707010000000E000041ED0000000000000000000000026627DD8100000000000000000000000000000000000000000000002700000000google-authenticator-libpam-1.10/build0707010000000F000081A40000000000000000000000016627DD810000000E000000000000000000000000000000000000003200000000google-authenticator-libpam-1.10/build/.gitignore*
!.gitignore
07070100000010000081A40000000000000000000000016627DD8100000907000000000000000000000000000000000000002E00000000google-authenticator-libpam-1.10/configure.acAC_PREREQ(2.61)
AC_INIT(google-authenticator, 1.10, habets@google.com)
AC_CONFIG_SRCDIR([src/google-authenticator.c])
AC_CONFIG_AUX_DIR([build])
AC_CONFIG_MACRO_DIR([build])

# --enable-silent-rules
m4_ifdef([AM_SILENT_RULES],
  [AM_SILENT_RULES([yes])],
  [AC_SUBST([AM_DEFAULT_VERBOSITY], [1])])

AC_USE_SYSTEM_EXTENSIONS
AM_INIT_AUTOMAKE([foreign subdir-objects])
AM_MAINTAINER_MODE([enable])

LT_INIT
AC_PROG_CC
AC_PROG_CC_STDC

AC_CHECK_HEADERS([sys/fsuid.h])
AC_CHECK_FUNCS([ \
	explicit_bzero \
	setfsuid \
	setfsgid \
])

AC_CHECK_HEADERS_ONCE([security/pam_appl.h])
# On Solaris at least, <security/pam_modules.h> requires <security/pam_appl.h>
# to be included first
AC_CHECK_HEADER([security/pam_modules.h], [], [], \
		[#ifdef HAVE_SECURITY_PAM_APPL_H
		# include <security/pam_appl.h>
		#endif
		])
AC_CHECK_LIB([pam], [pam_get_user], [:])
AS_IF([test "x$ac_cv_header_security_pam_modules_h" = "xno" \
       -o "x$ac_cv_lib_pam_pam_get_user" = "xno"], [
  AC_MSG_ERROR([Unable to find the PAM library or the PAM header files])
])

AC_MSG_CHECKING([whether certain PAM functions require const arguments])
AC_LANG_PUSH(C)
# Force test to bail if const isn't needed
AC_LANG_WERROR
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
	#include <security/pam_appl.h>
	#include <security/pam_modules.h>
]],[[
	const void **item = 0;
	int dummy = 0;
	/*
	 * since pam_handle_t is opaque on at least some platforms, give it
	 * a non-NULL dummy value
	 */
	 const pam_handle_t *ph = (const pam_handle_t *)&dummy;
	(void) pam_get_item(ph, 0, item);
]])],[AC_DEFINE([PAM_CONST], [const], \
	[Define if certain PAM functions require const arguments])
		AC_MSG_RESULT([yes])],
	[AC_DEFINE([PAM_CONST], [], \
	[Prevent certain PAM functions from using const arguments])
	AC_MSG_RESULT([no])])


	AC_MSG_CHECKING(whether compiler understands -Wall)
	old_CFLAGS="$CFLAGS"
	CFLAGS="$CFLAGS -Wall"
	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],
	  AC_MSG_RESULT(yes),
	  AC_MSG_RESULT(no)
	  CFLAGS="$old_CFLAGS")

AC_LANG_POP(C)



AC_SEARCH_LIBS([dlopen], [dl])

AC_CONFIG_HEADER(config.h)
AC_CONFIG_FILES([Makefile contrib/rpm.spec])
AC_OUTPUT

echo "
  $PACKAGE_NAME version $PACKAGE_VERSION
  Prefix.........: $prefix
  Debug Build....: $debug
  C Compiler.....: $CC $CFLAGS $CPPFLAGS
  Linker.........: $LD $LDFLAGS $LIBS
"
07070100000011000041ED0000000000000000000000026627DD8100000000000000000000000000000000000000000000002900000000google-authenticator-libpam-1.10/contrib07070100000012000081A40000000000000000000000016627DD8100000520000000000000000000000000000000000000003700000000google-authenticator-libpam-1.10/contrib/README.rpm.md## Building a Google-Authenticator RPM

Please note the RPM does not require QR-Encode as a dependency,
As technically the module builds fine without it. But in all likely-
hood you will need it in an actual deployment. Building a QR-Encode
RPM is outside the scope of this documentation, see the in-repo
documentation for instructions. https://github.com/fukuchi/libqrencode

If you are using RPMs in your testing a new build number option has
been added to the spec file IE: --define '_release #' to where # is
a build number. This will generate a RPM in the  namespace:
google-authenticator-1.01-#.el6.x86_64.rpm where # is your specified
build number. If no _release is set the build is defaulted to 1.
Example:

```
rpmbuild -ba contrib/rpm.spec --define '_release 8'
```

This will generate an rpm of:

```
google-authenticator-1.01-8.el6.x86_64.rpm
```

### Requirements

  * gcc
  * libtool
  * autoconf
  * automake
  * libpam-devel
  * rpm-builder
  * qr-encode (optional)


### Process

```shell
git clone https://github.com/google/google-authenticator.git
cd google-authenticator/libpam
./bootstrap.sh
./configure
make dist
cp google-autheticator-#.##.tar.gz ~/rpmbuild/SOURCES/
rpmbuild -ba contrib/rpm.spec
```

The script build-rpm.sh has been created to make these steps a bit easier to perform.
07070100000013000081ED0000000000000000000000016627DD8100000323000000000000000000000000000000000000003600000000google-authenticator-libpam-1.10/contrib/build-rpm.sh#!/bin/bash
#
# A simple script to make building an RPM of the software a lot easier
#

set -e

if [ "$(which rpmbuild)" == "" ]; then
  echo "To build an rpm the tool rpmbuild needs to be installed first"
  exit 1
fi

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

if [[ -z $1 ]]; then
  echo "Usage $0 \"release\""
  exit 1
fi

cd "${DIR}/.."

echo "Starting build"
./bootstrap.sh && \
./configure && \
make dist && \
(
   mkdir -p "${DIR}/_rpmbuild/SOURCES"
   cp -f google-authenticator-*.tar.gz "${DIR}/_rpmbuild/SOURCES/"
   rpmbuild -ba contrib/rpm.spec --define "_topdir ${DIR}/_rpmbuild" --define "_release $1"

   echo "=============="
   echo "Available RPMs"
   find "${DIR}/_rpmbuild/" -type f -name 'google-authenticator*.rpm'
) || (echo "Something went wrong"; exit 1)

exit 0

07070100000014000081A40000000000000000000000016627DD810000061B000000000000000000000000000000000000003500000000google-authenticator-libpam-1.10/contrib/rpm.spec.inName:           @PACKAGE_NAME@
Version:        @VERSION@
%if %{?_release:1}0
Release:        %{_release}%{?dist}
%else
Release:        1%{?dist}
%endif
Summary:        One-time passcode support using open standards

License:        ASL 2.0
URL:            https://github.com/google/google-authenticator
Source0:        %{name}-%{version}.tar.gz
BuildRequires:  gcc
BuildRequires:  pam-devel
BuildRequires:  libtool


%description
The Google Authenticator package contains a pluggable authentication
module (PAM) which allows login using one-time passcodes conforming to
the open standards developed by the Initiative for Open Authentication
(OATH) (which is unrelated to OAuth).

Passcode generators are available (separately) for several mobile
platforms.

These implementations support the HMAC-Based One-time Password (HOTP)
algorithm specified in RFC 4226 and the Time-based One-time Password
(TOTP) algorithm specified in RFC 6238.

%prep
%setup -q

%build
%configure --libdir=/%{_lib}
make %{?_smp_mflags}

%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
rm $RPM_BUILD_ROOT/%{_lib}/security/pam_google_authenticator.la

%files
/%{_lib}/security/pam_google_authenticator.so
%{_bindir}/%{name}
%{_defaultdocdir}/%{name}/README.md
%{_defaultdocdir}/%{name}/totp.html
%{_defaultdocdir}/%{name}/FILEFORMAT
%{_mandir}/man1/*
%{_mandir}/man8/*


%changelog
* Fri Aug 18 2017 Niels Basjes <niels@basjes.nl> - 1.04
- Added installing man pages
* Wed Jan 13 2016 Dan Molik <dan@d3fy.net> - 1.01
- A new and updated build for google-authenticator
07070100000015000041ED0000000000000000000000026627DD8100000000000000000000000000000000000000000000002A00000000google-authenticator-libpam-1.10/examples07070100000016000081A40000000000000000000000016627DD810000137F000000000000000000000000000000000000003100000000google-authenticator-libpam-1.10/examples/demo.c// Demo wrapper for the PAM module. This is part of the Google Authenticator
// project.
//
// Copyright 2011 Google Inc.
// Author: Markus Gutschke
//
// 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.
//
#include "config.h"

#include <assert.h>
#include <fcntl.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>

#if !defined(PAM_BAD_ITEM)
// FreeBSD does not know about PAM_BAD_ITEM. And PAM_SYMBOL_ERR is an "enum",
// we can't test for it at compile-time.
#define PAM_BAD_ITEM PAM_SYMBOL_ERR
#endif

static struct termios old_termios;
static int jmpbuf_valid;
static sigjmp_buf jmpbuf;

static int conversation(int num_msg, PAM_CONST struct pam_message **msg,
                        struct pam_response **resp, void *appdata_ptr) {
  if (num_msg == 1 &&
      (msg[0]->msg_style == PAM_PROMPT_ECHO_OFF ||
       msg[0]->msg_style == PAM_PROMPT_ECHO_ON)) {
    *resp = malloc(sizeof(struct pam_response));
    assert(*resp);
    (*resp)->resp = calloc(1024, 1);
    struct termios termios = old_termios;
    if (msg[0]->msg_style == PAM_PROMPT_ECHO_OFF) {
      termios.c_lflag &= ~(ECHO|ECHONL);
    }
    sigsetjmp(jmpbuf, 1);
    jmpbuf_valid = 1;
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGTSTP);
    assert(!sigprocmask(SIG_UNBLOCK, &mask, NULL));
    printf("%s ", msg[0]->msg);
    assert(!tcsetattr(0, TCSAFLUSH, &termios));
    assert(fgets((*resp)->resp, 1024, stdin));
    assert(!tcsetattr(0, TCSAFLUSH, &old_termios));
    puts("");
    assert(!sigprocmask(SIG_BLOCK, &mask, NULL));
    jmpbuf_valid = 0;
    char *ptr = strrchr((*resp)->resp, '\n');
    if (ptr) {
      *ptr = '\000';
    }
    (*resp)->resp_retcode = 0;
    return PAM_SUCCESS;
  }
  if (num_msg == 1 && msg[0]->msg_style == PAM_ERROR_MSG) {
    printf("Error message to user: %s\n", msg[0]->msg);
    return PAM_SUCCESS;
  }
  return PAM_CONV_ERR;
}

#ifdef sun
#define PAM_CONST
#else
#define PAM_CONST const
#endif

int pam_get_user(pam_handle_t *pamh, PAM_CONST char **user,
                 const char *prompt) {
  return pam_get_item(pamh, PAM_USER, (void *)user);
}

int pam_get_item(const pam_handle_t *pamh, int item_type,
                 PAM_CONST void **item) {
  switch (item_type) {
    case PAM_SERVICE: {
      static const char service[] = "google_authenticator_demo";
      *item = service;
      return PAM_SUCCESS;
    }
    case PAM_USER: {
      char *user = getenv("USER");
      *item = user;
      return PAM_SUCCESS;
    }
    case PAM_CONV: {
      static struct pam_conv conv = { .conv = conversation }, *p_conv = &conv;
      *item = p_conv;
      return PAM_SUCCESS;
    }
    default:
      return PAM_BAD_ITEM;
  }
}

int pam_set_item(pam_handle_t *pamh, int item_type,
                 const void *item) {
  switch (item_type) {
    case PAM_AUTHTOK:
      return PAM_SUCCESS;
    default:
      return PAM_BAD_ITEM;
  }
}

static void print_diagnostics(int signo) {
  extern const char *get_error_msg(void);
  assert(!tcsetattr(0, TCSAFLUSH, &old_termios));
  fprintf(stderr, "%s\n", get_error_msg());
  _exit(1);
}

static void reset_console(int signo) {
  assert(!tcsetattr(0, TCSAFLUSH, &old_termios));
  puts("");
  _exit(1);
}

static void stop(int signo) {
  assert(!tcsetattr(0, TCSAFLUSH, &old_termios));
  puts("");
  raise(SIGSTOP);
}

static void cont(int signo) {
  if (jmpbuf_valid) {
    siglongjmp(jmpbuf, 0);
  }
}

int main(int argc, char *argv[]) {
  extern int pam_sm_authenticate(pam_handle_t *, int, int, const char **);

  // Try to redirect stdio to /dev/tty
  int fd = open("/dev/tty", O_RDWR);
  if (fd >= 0) {
    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
    close(fd);
  }

  // Disable core files
  assert(!setrlimit(RLIMIT_CORE, (struct rlimit []){ { 0, 0 } }));

  // Set up error and job control handlers
  assert(!tcgetattr(0, &old_termios));
  sigset_t mask;
  sigemptyset(&mask);
  sigaddset(&mask, SIGTSTP);
  assert(!sigprocmask(SIG_BLOCK, &mask, NULL));
  assert(!signal(SIGABRT, print_diagnostics));
  assert(!signal(SIGINT, reset_console));
  assert(!signal(SIGTSTP, stop));
  assert(!signal(SIGCONT, cont));

  // Attempt login
  if (pam_sm_authenticate(NULL, 0, argc-1, (const char **)argv+1)
      != PAM_SUCCESS) {
    fprintf(stderr, "Login failed\n");
    abort();
  }

  return 0;
}
07070100000017000041ED0000000000000000000000026627DD8100000000000000000000000000000000000000000000002500000000google-authenticator-libpam-1.10/man07070100000018000081A40000000000000000000000016627DD8100001789000000000000000000000000000000000000003C00000000google-authenticator-libpam-1.10/man/google-authenticator.1.\" Automatically generated by Pandoc 1.16.0.2
.\"
.TH "GOOGLE\-AUTHENTICATOR" "1" "" "Google two\-factor authentication user manual" ""
.hy
.SH NAME
.PP
google\-authenticator \- initialize one\-time passcodes for the current
user
.SH SYNOPSIS
.PP
google\-authenticator [\f[I]options\f[]]
.PP
If no option is provided on the command line, google\-authenticator(1)
will ask interactively the user for the more important options.
.SH DESCRIPTION
.PP
The google\-authenticator(1) command creates a new secret key in the
current user\[aq]s home directory.
By default, this secret key and all settings will be stored in
\f[C]~/.google_authenticator\f[].
.PP
If the system supports the \f[C]libqrencode\f[] library, a QRCode will
be shown, that can be scanned using the Android Google Authenticator
application.
If the system does not have this library, google\-authenticator(1)
outputs an URL that can be followed using a web browser.
Alternatively, the alphanumeric secret key is also outputted and thus can
be manually entered into the Android Google Authenticator application.
.PP
In either case, after the key has been added, the verification value
should be checked.
To do that, the user must click\-and\-hold the added entry on its
Android system until the context menu shows.
Then, the user checks that the displayed key\[aq]s verification value
matches the one provided by google\-authenticator(1).
Please note that this feature might not be available in all builds of
the Android application.
.PP
Each time the user logs into the system, he will now be prompted for the
TOTP code (time based one\-time\-password) or HOTP (counter\-based
one\-time\-password), depending on options given to
google\-authenticator(1), after having entered its normal user id and
its normal UNIX account password.
.SH OPTIONS
.PP
The main option consists of choosing the authentication token type:
either time based or counter\-based.
.TP
.B \-c, \-\-counter\-based
Set up counter\-based verification.
.RS
.RE
.TP
.B \-t, \-\-time\-based
Set up time\-based verification.
.RS
.RE
.PP
From this choice depends the available options.
.SS Counter\-based specific options
.PP
Those settings are only relevant for counter\-based one\-time\-password
(HOTP):
.TP
.B \-w, \-\-window\-size=\f[I]W\f[]
Set window of concurrently valid codes.
.RS
.PP
By default, three tokens are valid at any one time.
This accounts for generated\-but\-not\-used tokens and failed login
attempts.
In order to decrease the likelihood of synchronization problems, this
window can be increased from its default size of 3.
.PP
The window size must be between 1 and 21.
.RE
.TP
.B \-W, \-\-minimal\-window
Disable window of concurrently valid codes.
.RS
.RE
.SS Time\-based specific options
.PP
Those settings are only relevant for time\-based one\-time\-password
(TOTP):
.TP
.B \-D, \-\-allow\-reuse, \-d, \-\-disallow\-reuse
(Dis)allow multiple uses of the same authentication token.
.RS
.PP
This restricts the user to one login about every 30 seconds, but it
increases the chances to notice or even prevent man\-in\-the\-middle
attacks.
.RE
.TP
.B \-w, \-\-window\-size=\f[I]W\f[]
Set window of concurrently valid codes.
.RS
.PP
By default, a new token is generated every 30 seconds by the mobile
application.
In order to compensate for possible time\-skew between the client and
the server, an extra token before and after the current time is allowed.
This allows for a time skew of up to 30 seconds between authentication
server and client.
.PP
For example, if problems with poor time synchronization are experienced,
the window can be increased from its default size of 3 permitted codes
(one previous code, the current code, the next code) to 17 permitted
codes (the 8 previous codes, the current code, and the 8 next codes).
This will permit for a time skew of up to 4 minutes between client and
server.
.PP
The window size must be between 1 and 21.
.RE
.TP
.B \-W, \-\-minimal\-window
Disable window of concurrently valid codes.
.RS
.RE
.TP
.B \-S, \-\-step\-size=\f[I]S\f[]
Set interval between token refreshes to \f[I]S\f[] seconds.
.RS
.PP
By default, time\-based tokens are generated every 30 seconds.
A non\-standard value can be configured in case a different time\-step
value must be used.
.PP
The time interval must be between 1 and 60 seconds.
.RE
.SS General options
.TP
.B \-s, \-\-secret=\f[I]FILE\f[]
Specify a non\-standard file location for the secret key and settings.
.RS
.RE
.TP
.B \-f, \-\-force
Write secret key and settings without first confirming with user.
.RS
.RE
.TP
.B \-l, \-\-label=\f[I]LABEL\f[]
Override the default label in \f[C]otpauth://\f[] URL.
.RS
.RE
.TP
.B \-i, \-\-issuer=\f[I]ISSUER\f[]
Override the default issuer in \f[C]otpauth://\f[] URL.
.RS
.RE
.TP
.B \-Q, \-\-qr\-mode=\f[I]none|ansi|utf8\f[]
QRCode output mode.
.RS
.PP
Suppress the QRCode output (\f[I]none\f[]), or output QRCode using
either ANSI colors (\f[I]ansi\f[]), or Unicode block elements
(\f[I]utf8\f[]).
.PP
Unicode block elements makes the QRCode much smaller, which is often
easier to scan.
Unfortunately, many terminal emulators do not display these Unicode
characters properly.
.RE
.TP
.B \-r, \-\-rate\-limit=\f[I]N\f[], \-R, \-\-rate\-time=\f[I]M\f[], \-u, \-\-no\-rate\-limit
Disable rate\-limiting, or limit logins to N per every M seconds.
.RS
.PP
If the system isn\[aq]t hardened against brute\-force login attempts,
rate\-limiting can be enabled for the authentication module: no more
than \f[I]N\f[] login attempts every \f[I]M\f[] seconds.
.PP
The rate limit must be between 1 and 10 attempts.
The rate time must be between 15 and 600 seconds.
.RE
.TP
.B \-e, \-\-emergency\-codes=\f[I]N\f[]
Generate \f[I]N\f[] emergency codes.
.RS
.PP
A maximum of 10 emergency codes can be generated.
.RE
.TP
.B \-q, \-\-quiet
Quiet mode.
.RS
.RE
.TP
.B \-h, \-\-help
Print the help message.
.RS
.RE
.SH SEE ALSO
.PP
The Google Authenticator source code and all documentation may be
downloaded from <https://github.com/google/google-authenticator-libpam>.
07070100000019000081A40000000000000000000000016627DD81000015B3000000000000000000000000000000000000003F00000000google-authenticator-libpam-1.10/man/google-authenticator.1.md% GOOGLE-AUTHENTICATOR(1) Google two-factor authentication user manual

# NAME

google-authenticator - initialize one-time passcodes for the current user

# SYNOPSIS

google-authenticator [*options*]

If no option is provided on the command line, google-authenticator(1) will ask
interactively the user for the more important options.

# DESCRIPTION

The google-authenticator(1) command creates a new secret key in the current
user's home directory. By default, this secret key and all settings will be
stored in `~/.google_authenticator`.

If the system supports the `libqrencode` library, a QRCode will be shown, that
can be scanned using the Android Google Authenticator application. If the
system does not have this library, google-authenticator(1) outputs an URL that
can be followed using a web browser. Alternatively, the alphanumeric secret key
is also outputted and thus can be manually entered into the Android Google
Authenticator application.

In either case, after the key has been added, the verification value should be
checked. To do that, the user must click-and-hold the added entry on its
Android system until the context menu shows. Then, the user checks that the
displayed key's verification value matches the one provided by
google-authenticator(1). Please note that this feature might not be available
in all builds of the Android application.

Each time the user logs into the system, he will now be prompted for the TOTP
code (time based one-time-password) or HOTP (counter-based one-time-password),
depending on options given to google-authenticator(1), after having entered its
normal user id and its normal UNIX account password.

# OPTIONS

The main option consists of choosing the authentication token type: either time
based or counter-based.

-c, --counter-based
:   Set up counter-based verification.

-t, --time-based
:   Set up time-based verification.

From this choice depends the available options.

## Counter-based specific options

Those settings are only relevant for counter-based one-time-password (HOTP):

-w, --window-size=*W*
:   Set window of concurrently valid codes.

    By default, three tokens are valid at any one time. This accounts for
    generated-but-not-used tokens and failed login attempts. In order to
    decrease the likelihood of synchronization problems, this window can be
    increased from its default size of 3.

    The window size must be between 1 and 21.

-W, --minimal-window
:   Disable window of concurrently valid codes.

## Time-based specific options

Those settings are only relevant for time-based one-time-password (TOTP):

-D, --allow-reuse, -d, --disallow-reuse
:   (Dis)allow multiple uses of the same authentication token.

    This restricts the user to one login about every 30 seconds, but it
    increases the chances to notice or even prevent man-in-the-middle attacks.

-w, --window-size=*W*
:   Set window of concurrently valid codes.

    By default, a new token is generated every 30 seconds by the mobile
    application. In order to compensate for possible time-skew between the
    client and the server, an extra token before and after the current time is
    allowed. This allows for a time skew of up to 30 seconds between
    authentication server and client.

    For example, if problems with poor time synchronization are experienced,
    the window can be increased from its default size of 3 permitted codes (one
    previous code, the current code, the next code) to 17 permitted codes (the
    8 previous codes, the current code, and the 8 next codes). This will permit
    for a time skew of up to 4 minutes between client and server.

    The window size must be between 1 and 21.

-W, --minimal-window
:   Disable window of concurrently valid codes.

-S, --step-size=*S*
:   Set interval between token refreshes to *S* seconds.

    By default, time-based tokens are generated every 30 seconds. A
    non-standard value can be configured in case a different time-step value
    must be used.

    The time interval must be between 1 and 60 seconds.

## General options

-s, --secret=*FILE*
:   Specify a non-standard file location for the secret key and settings.

-f, --force
:   Write secret key and settings without first confirming with user.

-l, --label=*LABEL*
:   Override the default label in `otpauth://` URL.

-i, --issuer=*ISSUER*
:   Override the default issuer in `otpauth://` URL.

-Q, --qr-mode=*none|ansi|utf8*
:   QRCode output mode.

    Suppress the QRCode output (*none*), or output QRCode using either ANSI
    colors (*ansi*), or Unicode block elements (*utf8*).

    Unicode block elements makes the QRCode much smaller, which is often easier
    to scan. Unfortunately, many terminal emulators do not display these
    Unicode characters properly.

-r, --rate-limit=*N*, -R, --rate-time=*M*, -u, --no-rate-limit
:   Disable rate-limiting, or limit logins to N per every M seconds.

    If the system isn't hardened against brute-force login attempts,
    rate-limiting can be enabled for the authentication module: no more than
    *N* login attempts every *M* seconds.

    The rate limit must be between 1 and 10 attempts.
    The rate time must be between 15 and 600 seconds.

-e, --emergency-codes=*N*
:   Generate *N* emergency codes.

    A maximum of 10 emergency codes can be generated.

-q, --quiet
:   Quiet mode.

-h, --help
:   Print the help message.

# SEE ALSO

The Google Authenticator source code and all documentation may be downloaded
from <https://github.com/google/google-authenticator-libpam>.
0707010000001A000081A40000000000000000000000016627DD8100001A4A000000000000000000000000000000000000004000000000google-authenticator-libpam-1.10/man/pam_google_authenticator.8.\" Automatically generated by Pandoc 1.16.0.2
.\"
.TH "PAM_GOOGLE_AUTHENTICATOR" "8" "" "Google Authenticator PAM module manual" ""
.hy
.SH NAME
.PP
pam_google_authenticator \- PAM module for Google two\-factor
authentication
.SH SYNOPSIS
.PP
\f[B]pam_google_authenticator.so\f[] [secret=\f[I]file\f[]]
[authtok_prompt=\f[I]prompt\f[]] [user=\f[I]username\f[]]
[no_strict_owner] [allowed_perm=\f[I]0nnn\f[]] [debug]
[try_first_pass|use_first_pass|forward_pass] [noskewadj]
[no_increment_hotp] [nullok] [echo_verification_code]
.SH DESCRIPTION
.PP
The \f[B]pam_google_authenticator\f[] module is designed to protect user
authentication with a second factor, either time\-based (TOTP) or
counter\-based (HOTP).
Prior logging in, the user will be asked for both its password and a
one\-time code.
Such one\-time codes can be generated with the Google Authenticator
application, installed on the user\[aq]s Android device.
To respectively generate and verify those one\-time codes, a secret key
(randomly generated) must be shared between the device on which
one\-time codes are generated and the system on which this PAM module is
enabled.
.PP
Depending on its configuration (see \f[I]options\f[] section), this
module requires that a secret file is manually set up for each account
on the system.
This secret file holds the secret key and user\-specific options (see
\f[B]google\-authenticator\f[](1)).
Unless the \f[B]nullok\f[] option is used, authentication tries will be
rejected if such secret file doesn\[aq]t exist.
Alternatively, a system administrator may create those secret files on
behalf of the users and then communicates to them the secret keys.
.SH OPTIONS
.TP
.B secret=\f[I]file\f[]
Specify a non\-standard file location for the secret file.
.RS
.PP
By default, the PAM module looks for the secret file in the
\f[C]\&.google_authenticator\f[] file within the home of the user
logging in.
This option overrides this location.
.PP
The provided location may include the following short\-hands:
.IP \[bu] 2
\f[B]${USER}\f[] that will be interpreted as the username.
.IP \[bu] 2
\f[B]${HOME}\f[] and \f[B]~\f[] that will be interpreted as the
user\[aq]s home directory.
.RE
.TP
.B authtok_prompt=\f[I]prompt\f[]
Override default token prompt.
.RS
.PP
Note that if spaces are present in the provided prompt, the whole
argument must be wrapped in square brackets.
.RE
.TP
.B user=\f[I]username\f[]
Switch to a hard\-coded user prior to doing any file operation.
.RS
.RE
.TP
.B no_strict_owner
Disable the check against the secret file\[aq]s owner.
.RS
.PP
By default, the secret file must be owned by the user logging in.
This option disables this check.
.RE
.TP
.B allowed_perm=\f[I]0nnn\f[]
Override checked permissions of the secret file.
.RS
.PP
By default, the secret file must be readable only by its owner (ie.
mode \f[I]0600\f[]).
This option allows a different mode to be specified for this file.
.RE
.TP
.B debug
Enable more verbose log messages in syslog.
.RS
.RE
.TP
.B try_first_pass|use_first_pass|forward_pass
Stacking options for this PAM module.
.RS
.PP
Because some PAM clients cannot prompt the user for more than just the
password, the following stacking options may be used:
.IP \[bu] 2
\f[B]try_first_pass\f[]: before prompting the user for the one\-time
code, this module first tries the previous stacked module\[aq]s password
in case that satisfies this module as well.
.IP \[bu] 2
\f[B]use_first_pass\f[]: force this module to use a previous stacked
modules password.
With this option, this module will never prompt the user for the
one\-time code.
Thus, if no valid one\-time code is available, the user will be denied
access.
.IP \[bu] 2
\f[B]forward_pass\f[]: query the user for both the system password and
the verification code in a single prompt.
The system password is then forwarded to the next PAM module, which will
have to be configured with either the \f[B]use_first_pass\f[] option, or
the \f[B]try_first_pass\f[] option.
.RE
.TP
.B noskewadj
Don\[aq]t adjust time skew automatically.
.RS
.PP
By default, the PAM module makes an attempt to compensate for time skew
between the server and the device on which one\-time passcodes are
generated.
This option disable this behavior.
.PP
Note that this option is only relevant for time\-based (TOTP) mode.
.RE
.TP
.B no_increment_hotp
Don\[aq]t increment the counter for failed attempts.
.RS
.PP
In some circonstance, failed passwords still get an OTP prompt.
This option disables counter incrementation is such situations.
.PP
Note that this option is only relevant for counter\-based (HOTP) mode.
.RE
.TP
.B nullok
Allow users to log in without OTP, if they haven\[aq]t set up OTP yet.
.RS
.PP
During the initial roll\-out process, all users may not have created a
secret key yet.
This option allows them to log in, even if the secret file doesn\[aq]t
exist.
.RE
.TP
.B echo_verification_code
Echo the verification code when it is entered by the user.
.RS
.RE
.SH MODULE TYPE PROVIDED
.PP
Only the \f[B]auth\f[] module type is provided.
.SH RETURN VALUES
.TP
.B PAM_SUCCESS
Either the provided one\-time code is correct or is a valid emergency
code.
.RS
.RE
.TP
.B PAM_IGNORE
This module is ignored.
.RS
.RE
.TP
.B PAM_AUTH_ERR
The provided one\-time code isn\[aq]t correct and isn\[aq]t a valid
emergency code, or an error was encountered.
.RS
.RE
.SH EXAMPLES
.PP
The following lines may be used to enable this PAM module:
.IP \[bu] 2
\f[C]auth\ required\ pam_google_authenticator.so\ no_increment_hotp\f[]
# Make sure the counter (for HOTP mode) isn\[aq]t incremented for failed
attempts.
.IP \[bu] 2
\f[C]auth\ required\ pam_google_authenticator.so\ nullok\f[] # Allow
users to log in if their secret files don\[aq]t exist
.IP \[bu] 2
\f[C]auth\ required\ pam_google_authenticator.so\ secret=/var/unencrypted\-home/${USER}/.google_authenticator\f[]
# Store secret files in a specific location
.IP \[bu] 2
\f[C]auth\ required\ pam_google_authenticator.so\ [authtok_prompt=Your\ secret\ token:\ ]\f[]
# Use a specific prompt
.IP \[bu] 2
\f[C]auth\ required\ pam_google_authenticator.so\ noskewadj\f[] #
Don\[aq]t compensate time skew automatically
.SH SECURITY NOTES
.PP
For highest security, make sure that both password and one\-time code
are being requested even if password and/or one\-time code are
incorrect.
This means that \f[I]at least\f[] the first of \f[C]pam_unix.so\f[] (or
whatever other module is used to verify passwords) and
\f[C]pam_google_authenticator.so\f[] should be set as \f[B]required\f[],
not \f[B]requisite\f[].
.SH SEE ALSO
.PP
\f[B]google\-authenticator\f[](1).
.PP
The Google Authenticator source code and all documentation may be
downloaded from <https://github.com/google/google-authenticator-libpam>.
0707010000001B000081A40000000000000000000000016627DD810000182B000000000000000000000000000000000000004300000000google-authenticator-libpam-1.10/man/pam_google_authenticator.8.md% PAM_GOOGLE_AUTHENTICATOR(8) Google Authenticator PAM module manual

# NAME

pam_google_authenticator - PAM module for Google two-factor authentication

# SYNOPSIS

**pam_google_authenticator.so** [secret=*file*] [authtok_prompt=*prompt*]
[user=*username*] [no_strict_owner] [allowed_perm=*0nnn*] [debug]
[try_first_pass|use_first_pass|forward_pass] [noskewadj] [no_increment_hotp]
[nullok] [echo_verification_code]

# DESCRIPTION

The **pam_google_authenticator** module is designed to protect user
authentication with a second factor, either time-based (TOTP) or counter-based
(HOTP). Prior logging in, the user will be asked for both its password and a
one-time code. Such one-time codes can be generated with the Google
Authenticator application, installed on the user's Android device. To
respectively generate and verify those one-time codes, a secret key (randomly
generated) must be shared between the device on which one-time codes are
generated and the system on which this PAM module is enabled.

Depending on its configuration (see *options* section), this module requires
that a secret file is manually set up for each account on the system. This
secret file holds the secret key and user-specific options (see
**google-authenticator**(1)). Unless the **nullok** option is used,
authentication tries will be rejected if such secret file doesn't exist.
Alternatively, a system administrator may create those secret files on behalf
of the users and then communicates to them the secret keys.

# OPTIONS

secret=*file*
:   Specify a non-standard file location for the secret file.

    By default, the PAM module looks for the secret file in the
    `.google_authenticator` file within the home of the user logging in. This
    option overrides this location.

    The provided location may include the following short-hands:

    - **\${USER}** that will be interpreted as the username.
    - **\${HOME}** and **~** that will be interpreted as the user's home
    directory.

authtok_prompt=*prompt*
:   Override default token prompt.

    Note that if spaces are present in the provided prompt, the whole argument
    must be wrapped in square brackets.

user=*username*
:   Switch to a hard-coded user prior to doing any file operation.

no_strict_owner
:   Disable the check against the secret file's owner.

    By default, the secret file must be owned by the user logging in. This
    option disables this check.

allowed_perm=*0nnn*
:   Override checked permissions of the secret file.

    By default, the secret file must be readable only by its owner (ie. mode
    *0600*). This option allows a different mode to be specified for this file.

debug
:   Enable more verbose log messages in syslog.

try_first_pass|use_first_pass|forward_pass
:   Stacking options for this PAM module.

    Because some PAM clients cannot prompt the user for more than just the
    password, the following stacking options may be used:

    - **try_first_pass**: before prompting the user for the one-time code, this
    module first tries the previous stacked module's password in case that
    satisfies this module as well.
    - **use_first_pass**: force this module to use a previous stacked modules
    password. With this option, this module will never prompt the user for the
    one-time code. Thus, if no valid one-time code is available, the user will
    be denied access.
    - **forward_pass**: query the user for both the system password and the
    verification code in a single prompt. The system password is then forwarded
    to the next PAM module, which will have to be configured with either the
    **use_first_pass** option, or the **try_first_pass** option.

noskewadj
:   Don't adjust time skew automatically.

    By default, the PAM module makes an attempt to compensate for time skew
    between the server and the device on which one-time passcodes are
    generated. This option disable this behavior.

    Note that this option is only relevant for time-based (TOTP) mode.

no_increment_hotp
:   Don't increment the counter for failed attempts.

    In some circonstance, failed passwords still get an OTP prompt. This option
    disables counter incrementation is such situations.

    Note that this option is only relevant for counter-based (HOTP) mode.

nullok
:   Allow users to log in without OTP, if they haven't set up OTP yet.

    During the initial roll-out process, all users may not have created a
    secret key yet. This option allows them to log in, even if the secret file
    doesn't exist.

echo_verification_code
:   Echo the verification code when it is entered by the user.

# MODULE TYPE PROVIDED

Only the **auth** module type is provided.

# RETURN VALUES

PAM_SUCCESS
:   Either the provided one-time code is correct or is a valid emergency code.

PAM_IGNORE
:   This module is ignored.

PAM_AUTH_ERR
:   The provided one-time code isn't correct and isn't a valid emergency code,
or an error was encountered.

# EXAMPLES

The following lines may be used to enable this PAM module:

- `auth required pam_google_authenticator.so no_increment_hotp` # Make sure the
counter (for HOTP mode) isn't incremented for failed attempts.
- `auth required pam_google_authenticator.so nullok` # Allow users to log in if
their secret files don't exist
- `auth required pam_google_authenticator.so
secret=/var/unencrypted-home/${USER}/.google_authenticator` # Store secret
files in a specific location
- `auth required pam_google_authenticator.so [authtok_prompt=Your secret token:
]` # Use a specific prompt
- `auth required pam_google_authenticator.so noskewadj` # Don't compensate time
skew automatically

# SECURITY NOTES

For highest security, make sure that both password and one-time code are being
requested even if password and/or one-time code are incorrect. This means that
*at least* the first of `pam_unix.so` (or whatever other module is used to
verify passwords) and `pam_google_authenticator.so` should be set as
**required**, not **requisite**.

# SEE ALSO

**google-authenticator**(1).

The Google Authenticator source code and all documentation may be downloaded
from <https://github.com/google/google-authenticator-libpam>.
0707010000001C000041ED0000000000000000000000026627DD8100000000000000000000000000000000000000000000002500000000google-authenticator-libpam-1.10/src0707010000001D000081A40000000000000000000000016627DD81000009B9000000000000000000000000000000000000002E00000000google-authenticator-libpam-1.10/src/base32.c// Base32 implementation
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
//
// 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.

#include <string.h>

#include "base32.h"

int base32_decode(const uint8_t *encoded, uint8_t *result, int bufSize) {
  unsigned int buffer = 0;
  int bitsLeft = 0;
  int count = 0;
  for (const uint8_t *ptr = encoded; count < bufSize && *ptr; ++ptr) {
    uint8_t ch = *ptr;
    if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '-') {
      continue;
    }
    buffer <<= 5;

    // Deal with commonly mistyped characters
    if (ch == '0') {
      ch = 'O';
    } else if (ch == '1') {
      ch = 'L';
    } else if (ch == '8') {
      ch = 'B';
    }

    // Look up one base32 digit
    if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) {
      ch = (ch & 0x1F) - 1;
    } else if (ch >= '2' && ch <= '7') {
      ch -= '2' - 26;
    } else {
      return -1;
    }

    buffer |= ch;
    bitsLeft += 5;
    if (bitsLeft >= 8) {
      result[count++] = buffer >> (bitsLeft - 8);
      bitsLeft -= 8;
    }
  }
  if (count < bufSize) {
    result[count] = '\000';
  }
  return count;
}

int base32_encode(const uint8_t *data, int length, uint8_t *result,
                  int bufSize) {
  if (length < 0 || length > (1 << 28)) {
    return -1;
  }
  int count = 0;
  if (length > 0) {
    unsigned int buffer = data[0];
    int next = 1;
    int bitsLeft = 8;
    while (count < bufSize && (bitsLeft > 0 || next < length)) {
      if (bitsLeft < 5) {
        if (next < length) {
          buffer <<= 8;
          buffer |= data[next++] & 0xFF;
          bitsLeft += 8;
        } else {
          int pad = 5 - bitsLeft;
          buffer <<= pad;
          bitsLeft += pad;
        }
      }
      int index = 0x1F & (buffer >> (bitsLeft - 5));
      bitsLeft -= 5;
      result[count++] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"[index];
    }
  }
  if (count < bufSize) {
    result[count] = '\000';
  }
  return count;
}
0707010000001E000081A40000000000000000000000016627DD810000056B000000000000000000000000000000000000002E00000000google-authenticator-libpam-1.10/src/base32.h// Base32 implementation
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
//
// 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.
//
// Encode and decode from base32 encoding using the following alphabet:
//   ABCDEFGHIJKLMNOPQRSTUVWXYZ234567
// This alphabet is documented in RFC 4648/3548
//
// We allow white-space and hyphens, but all other characters are considered
// invalid.
//
// All functions return the number of output bytes or -1 on error. If the
// output buffer is too small, the result will silently be truncated.

#ifndef _BASE32_H_
#define _BASE32_H_

#include <stdint.h>

int base32_decode(const uint8_t *encoded, uint8_t *result, int bufSize)
    __attribute__((visibility("hidden")));
int base32_encode(const uint8_t *data, int length, uint8_t *result,
                  int bufSize)
    __attribute__((visibility("hidden")));

#endif /* _BASE32_H_ */
0707010000001F000081A40000000000000000000000016627DD81000016CC000000000000000000000000000000000000003300000000google-authenticator-libpam-1.10/src/base32_prog.c// Base32 utility program
//
// Copyright 2015 TWO SIGMA OPEN SOURCE, LLC
// Author: Eric Haszlakiewicz
//
// 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.

#include "config.h"

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>

#include "base32.h"

/*
 * Convert arbitrary input from a file to base-32 encoded output on stdout.
 * Decode base-32 input from a file or from the command line to arbitrary
 * output on stdout.
 *
 * ~/google-authenticator/libpam/base32 e $(awk '{ print $2 }' < gauth.seed | xxd -r -p)
 */

enum modes {
  ENCODE_NONE = 0,
  ENCODE_FILE = 1,
  DECODE_FILE = 2,
  DECODE_STRING = 3,
};

static void usage(const char *argv0, int exitval, char *errmsg) __attribute__((noreturn));
static void usage(const char *argv0, int exitval, char *errmsg)
{
  FILE *f = stdout;
  if (errmsg) {
    fprintf(stderr, "ERROR: %s\n", errmsg);
    f = stderr;
  }

  fprintf(f, "Usage: %s -e [<file>]\n", argv0);
  fprintf(f, "Usage: %s -d [<file>]\n", argv0);
  fprintf(f, "Usage: %s -D <value>\n", argv0);
  fprintf(f, "  Emits <value> encoded in/decoded from base-32 on stdout.\n");
  fprintf(f, "  When encoding, the file should contain raw binary data.\n");
  fprintf(f, "   If no filename is specified, it reads from stdin.\n");
  fprintf(f, "  All output is written to stdout.\n");

  exit(exitval);
}

/* Write the full contents of buf to the given file descriptor,
 * even across multiple short writes.
 * If write() fails, exit with an error.
 */
static void full_write(int fd, uint8_t *buf, size_t remaining)
{
  ssize_t offset, written;
  for (offset = 0; remaining != 0; remaining -= written, offset += written) {
    if ((written = write(fd, buf + offset, remaining)) < 0) {
      err(1, "Failed to write to stdout");
    }
  }
}

int main(int argc, char *argv[]) {
  int c;
  int mode = ENCODE_NONE;
  while ((c = getopt(argc, argv, "edDh")) != -1) {
    switch (c) {
      case 'e':
        mode = ENCODE_FILE;
        break;
      case 'd':
        mode = DECODE_FILE;
        break;
      case 'D':
        mode = DECODE_STRING;
        break;
      case 'h':
        usage(argv[0], 0, NULL);
        break;
      default:
        usage(argv[0], 1, "Unknown command line argument");
        break;
    }
  }

  if (mode == ENCODE_NONE) {
    usage(argv[0], 1, "A mode of operation must be chosen.");
  }

  int retval;
  if (mode == ENCODE_FILE || mode == DECODE_FILE) {
    if (argc - optind > 1) {
      usage(argv[0], 1, "Too many args");
    }

    const char *binfile = (optind < argc) ? argv[optind] : "-";
    const int d = strcmp(binfile, "-") == 0 ? STDIN_FILENO : open(binfile, O_RDONLY);
    if (d < 0) {
      err(1, "Failed to open %s: %s\n", binfile, strerror(errno));
    }
    struct stat st;
    memset(&st, 0, sizeof(st));
    if (fstat(d, &st) < 0 || st.st_size == 0) {
      st.st_size = 5 * 1024;  // multiple of 5 to avoid internal padding
      // AND multiple of 8 to ensure we feed
      //  valid data to base32_decode().
    }
    const size_t bufsize = st.st_size + 1;
    if (bufsize < st.st_size) {
      err(1, "Integer overflow in input size");
    }
    uint8_t *input = malloc(bufsize);
    if (!input) {
      err(1, "Failed to allocate memory");
    }
    int amt_read;
    const int amt_to_read = st.st_size;
    errno = 0;
    while ((amt_read = read(d, input, amt_to_read)) > 0 || errno == EINTR) {
      if (errno == EINTR) {
        continue;
      }

      // Encoding: 8 bytes out for every 5 input, plus up to 6 padding, and nul
      // Decoding: up to 5 bytes out for every 8 input.
      const int result_avail = (mode == ENCODE_FILE) ?
                               ((amt_read + 4) / 5 * 8 + 6 + 1) :
                               ((amt_read + 7) / 8 * 5) ;
      uint8_t *result = malloc(result_avail);

      input[amt_read] = '\0';

      if (mode == ENCODE_FILE) {
        retval = base32_encode(input, amt_read, result, result_avail);
      } else {
        retval = base32_decode(input, result, result_avail);
      }
      if (retval < 0) {
        fprintf(stderr, "%s failed.  Input too long?\n", (mode == ENCODE_FILE) ? "base32_encode" : "base32_decode");
        exit(1);
      }
      //printf("%s", result);
      full_write(STDOUT_FILENO, result, retval);
      fflush(stdout);
      free(result);
    }
    free(input);
    if (amt_read < 0) {
      err(1, "Failed to read from %s: %s\n", binfile, strerror(errno));
    }
    if (mode == ENCODE_FILE) {
      printf("\n");
    }
  } else { // mode == DECODE_STRING
    if (argc - optind < 1) {
      usage(argv[0], 1, "Not enough args");
    }

    const char *base32_value = argv[2];
    const int result_avail = strlen(base32_value) + 1;
    if (result_avail < strlen(base32_value)) {
      err(1, "Integer overflow in input size");
    }

    uint8_t *result = malloc(result_avail);

    retval = base32_decode((uint8_t *)base32_value, result, result_avail);
    if (retval < 0) {
      fprintf(stderr, "base32_decode failed.  Input too long?\n");
      exit(1);
    }
    full_write(STDOUT_FILENO, result, retval);
    free(result);
  }
  return 0;
}
/* ---- Emacs Variables ----
 * Local Variables:
 * c-basic-offset: 2
 * indent-tabs-mode: nil
 * End:
 */
07070100000020000081A40000000000000000000000016627DD810000755D000000000000000000000000000000000000003C00000000google-authenticator-libpam-1.10/src/google-authenticator.c// Helper program to generate a new secret for use in two-factor
// authentication.
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
//
// 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.

#include "config.h"

#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

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

#define SECRET                    "/.google_authenticator"
#define SECRET_BITS               128         // Must be divisible by eight
#define VERIFICATION_CODE_MODULUS (1000*1000) // Six digits
#define SCRATCHCODES              5           // Default number of initial scratchcodes
#define MAX_SCRATCHCODES          10          // Max number of initial scratchcodes
#define SCRATCHCODE_LENGTH        8           // Eight digits per scratchcode
#define BYTES_PER_SCRATCHCODE     4           // 32bit of randomness is enough
#define BITS_PER_BASE32_CHAR      5           // Base32 expands space by 8/5

static enum { QR_UNSET=0, QR_NONE, QR_ANSI, QR_UTF8 } qr_mode = QR_UNSET;

static int generateCode(const char *key, unsigned long tm) {
  uint8_t challenge[8];
  for (int i = 8; i--; tm >>= 8) {
    challenge[i] = tm;
  }

  // Estimated number of bytes needed to represent the decoded secret. Because
  // of white-space and separators, this is an upper bound of the real number,
  // which we later get as a return-value from base32_decode()
  int secretLen = (strlen(key) + 7)/8*BITS_PER_BASE32_CHAR;

  // Sanity check, that our secret will fixed into a reasonably-sized static
  // array.
  if (secretLen <= 0 || secretLen > 100) {
    return -1;
  }

  // Decode secret from Base32 to a binary representation, and check that we
  // have at least one byte's worth of secret data.
  uint8_t secret[100];
  if ((secretLen = base32_decode((const uint8_t *)key, secret, secretLen))<1) {
    return -1;
  }

  // Compute the HMAC_SHA1 of the secret and the challenge.
  uint8_t hash[SHA1_DIGEST_LENGTH];
  hmac_sha1(secret, secretLen, challenge, 8, hash, SHA1_DIGEST_LENGTH);

  // Pick the offset where to sample our hash value for the actual verification
  // code.
  const int offset = hash[SHA1_DIGEST_LENGTH - 1] & 0xF;

  // Compute the truncated hash in a byte-order independent loop.
  unsigned int truncatedHash = 0;
  for (int i = 0; i < 4; ++i) {
    truncatedHash <<= 8;
    truncatedHash  |= hash[offset + i];
  }

  // Truncate to a smaller number of digits.
  truncatedHash &= 0x7FFFFFFF;
  truncatedHash %= VERIFICATION_CODE_MODULUS;

  return truncatedHash;
}

// return the user name in heap-allocated buffer.
// Caller frees.
static const char *getUserName(uid_t uid) {
  struct passwd pwbuf, *pw;
  char *buf;
  #ifdef _SC_GETPW_R_SIZE_MAX
  int len = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (len <= 0) {
    len = 4096;
  }
  #else
  int len = 4096;
  #endif
  buf = malloc(len);
  char *user;
  if (getpwuid_r(uid, &pwbuf, buf, len, &pw) || !pw) {
    user = malloc(32);
    snprintf(user, 32, "%d", uid);
  } else {
    user = strdup(pw->pw_name);
    if (!user) {
      perror("malloc()");
      _exit(1);
    }
  }
  free(buf);
  return user;
}

static const char *urlEncode(const char *s) {
  const size_t size = 3 * strlen(s) + 1;
  if (size > 10000) {
    // Anything "too big" is too suspect to let through.
    fprintf(stderr, "Error: Generated URL would be unreasonably large.\n");
    exit(1);
  }
  char *ret = malloc(size);
  char *d = ret;
  do {
    switch (*s) {
    case '%':
    case '&':
    case '?':
    case '=':
    encode:
      snprintf(d, size-(d-ret), "%%%02X", (unsigned char)*s);
      d += 3;
      break;
    default:
      if ((*s && *s <= ' ') || *s >= '\x7F') {
        goto encode;
      }
      *d++ = *s;
      break;
    }
  } while (*s++);
  char* newret = realloc(ret, strlen(ret) + 1);
  if (newret) {
    ret = newret;
  }
  return ret;
}

static const char *getURL(const char *secret, const char *label,
                          const int use_totp, const char *issuer) {
  const char *encodedLabel = urlEncode(label);
  char *url;
  const char totp = use_totp ? 't' : 'h';
  if (asprintf(&url, "otpauth://%cotp/%s?secret=%s", totp, encodedLabel, secret) < 0) {
    fprintf(stderr, "String allocation failed, probably running out of memory.\n");
    _exit(1);
  }

  if (issuer != NULL && strlen(issuer) > 0) {
    // Append to URL &issuer=<issuer>
    const char *encodedIssuer = urlEncode(issuer);
    char *newUrl;
    if (asprintf(&newUrl, "%s&issuer=%s", url, encodedIssuer) < 0) {
      fprintf(stderr, "String allocation failed, probably running out of memory.\n");
      _exit(1);
    }
    free((void *)encodedIssuer);
    free(url);
    url = newUrl;
  }

  free((void *)encodedLabel);
  return url;
}

#define ANSI_RESET        "\x1B[0m"
#define ANSI_BLACKONGREY  "\x1B[30;47;27m"
#define ANSI_WHITE        "\x1B[27m"
#define ANSI_BLACK        "\x1B[7m"
#define UTF8_BOTH         "\xE2\x96\x88"
#define UTF8_TOPHALF      "\xE2\x96\x80"
#define UTF8_BOTTOMHALF   "\xE2\x96\x84"

// Display QR code visually. If not possible, return 0.
static int displayQRCode(const char* url) {
  void *qrencode = dlopen("libqrencode.so.2", RTLD_NOW | RTLD_LOCAL);
  if (!qrencode) {
    qrencode = dlopen("libqrencode.so.3", RTLD_NOW | RTLD_LOCAL);
  }
  if (!qrencode) {
    qrencode = dlopen("libqrencode.so.4", RTLD_NOW | RTLD_LOCAL);
  }
  if (!qrencode) {
    qrencode = dlopen("libqrencode.3.dylib", RTLD_NOW | RTLD_LOCAL);
  }
  if (!qrencode) {
    qrencode = dlopen("libqrencode.4.dylib", RTLD_NOW | RTLD_LOCAL);
  }
  if (!qrencode) {
    return 0;
  }
  typedef struct {
    int version;
    int width;
    unsigned char *data;
  } QRcode;
  QRcode *(*QRcode_encodeString8bit)(const char *, int, int) =
      (QRcode *(*)(const char *, int, int))
      dlsym(qrencode, "QRcode_encodeString8bit");
  void (*QRcode_free)(QRcode *qrcode) =
      (void (*)(QRcode *))dlsym(qrencode, "QRcode_free");
  if (!QRcode_encodeString8bit || !QRcode_free) {
    dlclose(qrencode);
    return 0;
  }
  QRcode *qrcode = QRcode_encodeString8bit(url, 0, 1);
  const char *ptr = (char *)qrcode->data;
  // Output QRCode using ANSI colors. Instead of black on white, we
  // output black on grey, as that works independently of whether the
  // user runs their terminal in a black on white or white on black color
  // scheme.
  // But this requires that we print a border around the entire QR Code.
  // Otherwise readers won't be able to recognize it.
  if (qr_mode != QR_UTF8) {
    for (int i = 0; i < 2; ++i) {
      printf(ANSI_BLACKONGREY);
      for (int x = 0; x < qrcode->width + 4; ++x) printf("  ");
      puts(ANSI_RESET);
    }
    for (int y = 0; y < qrcode->width; ++y) {
      printf(ANSI_BLACKONGREY"    ");
      int isBlack = 0;
      for (int x = 0; x < qrcode->width; ++x) {
        if (*ptr++ & 1) {
          if (!isBlack) {
            printf(ANSI_BLACK);
          }
          isBlack = 1;
        } else {
          if (isBlack) {
            printf(ANSI_WHITE);
          }
          isBlack = 0;
        }
        printf("  ");
      }
      if (isBlack) {
        printf(ANSI_WHITE);
      }
      puts("    "ANSI_RESET);
    }
    for (int i = 0; i < 2; ++i) {
      printf(ANSI_BLACKONGREY);
      for (int x = 0; x < qrcode->width + 4; ++x) printf("  ");
      puts(ANSI_RESET);
    }
  } else {
    // Drawing the QRCode with Unicode block elements is desirable as
    // it makes the code much smaller, which is often easier to scan.
    // Unfortunately, many terminal emulators do not display these
    // Unicode characters properly.
    printf(ANSI_BLACKONGREY);
    for (int i = 0; i < qrcode->width + 4; ++i) {
      printf(" ");
    }
    puts(ANSI_RESET);
    for (int y = 0; y < qrcode->width; y += 2) {
      printf(ANSI_BLACKONGREY"  ");
      for (int x = 0; x < qrcode->width; ++x) {
        const int top = qrcode->data[y*qrcode->width + x] & 1;
        int bottom = 0;
        if (y+1 < qrcode->width) {
          bottom = qrcode->data[(y+1)*qrcode->width + x] & 1;
        }
        if (top) {
          if (bottom) {
            printf(UTF8_BOTH);
          } else {
            printf(UTF8_TOPHALF);
          }
        } else {
          if (bottom) {
            printf(UTF8_BOTTOMHALF);
          } else {
            printf(" ");
          }
        }
      }
      puts("  "ANSI_RESET);
    }
    printf(ANSI_BLACKONGREY);
    for (int i = 0; i < qrcode->width + 4; ++i) {
      printf(" ");
    }
    puts(ANSI_RESET);
  }
  QRcode_free(qrcode);
  dlclose(qrencode);
  return 1;
}

// Display to the user what they need to provision their app.
static void displayEnrollInfo(const char *secret, const char *label,
                              const int use_totp, const char *issuer) {
  if (qr_mode == QR_NONE) {
    return;
  }
  const char *url = getURL(secret, label, use_totp, issuer);

  // Only newer systems have support for libqrencode. So instead of requiring
  // it at build-time we look for it at run-time. If it cannot be found, the
  // user can still type the code in manually or copy the URL into
  // their browser.
  if (isatty(STDOUT_FILENO)) {
    if (!displayQRCode(url)) {
      printf(
          "Failed to use libqrencode to show QR code visually for scanning.\n"
          "Consider typing the OTP secret into your app manually.\n");
    }
  }

  free((char *)url);
}

// ask for a code. Return code, or some garbage if no number given. That's fine
// because bad data also won't match code.
static int ask_code(const char* msg) {
  char line[128];
  printf("%s ", msg);
  fflush(stdout);

  line[sizeof(line)-1] = 0;
  if (NULL == fgets(line, sizeof line, stdin)) {
    if (errno == 0) {
      printf("\n");
    } else {
      perror("getline()");
    }
    exit(1);
  }

  return strtol(line, NULL, 10);
}

// ask y/n, and return 0 for no, 1 for yes.
static int maybe(const char *msg) {
  printf("\n");
  for (;;) {
    char line[128];
    printf("%s (y/n) ", msg);
    fflush(stdout);

    line[sizeof(line)-1] = 0;
    if (NULL == fgets(line, sizeof(line), stdin)) {
      if (errno == 0) {
        printf("\n");
      } else {
        perror("getline()");
      }
      exit(1);
    }
    switch (line[0]) {
    case 'Y':
    case 'y':
      return 1;
    case 'N':
    case 'n':
      return 0;
    }
  }
}

static char *addOption(char *buf, size_t nbuf, const char *option) {
  assert(strlen(buf) + strlen(option) < nbuf);
  char *scratchCodes = strchr(buf, '\n');
  assert(scratchCodes);
  scratchCodes++;
  memmove(scratchCodes + strlen(option), scratchCodes,
          strlen(scratchCodes) + 1);
  memcpy(scratchCodes, option, strlen(option));
  return buf;
}

static char *maybeAddOption(const char *msg, char *buf, size_t nbuf,
                            const char *option) {
  if (maybe(msg)) {
    buf = addOption(buf, nbuf, option);
  }
  return buf;
}

static void
print_version() {
  puts("google-authenticator "VERSION);
}

static void usage(void) {
  print_version();
  puts(
 "google-authenticator [<options>]\n"
 " -h, --help                     Print this message\n"
 "     --version                  Print version\n"
 " -c, --counter-based            Set up counter-based (HOTP) verification\n"
 " -C, --no-confirm               Don't confirm code. For non-interactive setups\n"
 " -t, --time-based               Set up time-based (TOTP) verification\n"
 " -d, --disallow-reuse           Disallow reuse of previously used TOTP tokens\n"
 " -D, --allow-reuse              Allow reuse of previously used TOTP tokens\n"
 " -f, --force                    Write file without first confirming with user\n"
 " -l, --label=<label>            Override the default label in \"otpauth://\" URL\n"
 " -i, --issuer=<issuer>          Override the default issuer in \"otpauth://\" URL\n"
 " -q, --quiet                    Quiet mode\n"
 " -Q, --qr-mode={NONE,ANSI,UTF8} QRCode output mode\n"
 " -r, --rate-limit=N             Limit logins to N per every M seconds\n"
 " -R, --rate-time=M              Limit logins to N per every M seconds\n"
 " -u, --no-rate-limit            Disable rate-limiting\n"
 " -s, --secret=<file>            Specify a non-standard file location\n"
 " -S, --step-size=S              Set interval between token refreshes\n"
 " -w, --window-size=W            Set window of concurrently valid codes\n"
 " -W, --minimal-window           Disable window of concurrently valid codes\n"
 " -e, --emergency-codes=N        Number of emergency codes to generate");
}

int main(int argc, char *argv[]) {
  uint8_t buf[SECRET_BITS/8 + MAX_SCRATCHCODES*BYTES_PER_SCRATCHCODE];
  static const char hotp[]      = "\" HOTP_COUNTER 1\n";
  static const char totp[]      = "\" TOTP_AUTH\n";
  static const char disallow[]  = "\" DISALLOW_REUSE\n";
  static const char step[]      = "\" STEP_SIZE 30\n";
  static const char window[]    = "\" WINDOW_SIZE 17\n";
  static const char ratelimit[] = "\" RATE_LIMIT 3 30\n";
  char secret[(SECRET_BITS + BITS_PER_BASE32_CHAR-1)/BITS_PER_BASE32_CHAR +
              1 /* newline */ +
              sizeof(hotp) +  // hotp and totp are mutually exclusive.
              sizeof(disallow) +
              sizeof(step) +
              sizeof(window) +
              sizeof(ratelimit) + 5 + // NN MMM (total of five digits)
              SCRATCHCODE_LENGTH*(MAX_SCRATCHCODES + 1 /* newline */) +
              1 /* NUL termination character */];

  enum { ASK_MODE, HOTP_MODE, TOTP_MODE } mode = ASK_MODE;
  enum { ASK_REUSE, DISALLOW_REUSE, ALLOW_REUSE } reuse = ASK_REUSE;
  int force = 0, quiet = 0;
  int r_limit = 0, r_time = 0;
  char *secret_fn = NULL;
  char *label = NULL;
  char *issuer = NULL;
  int step_size = 0;
  int confirm = 1;
  int window_size = 0;
  int emergency_codes = -1;
  int idx;
  for (;;) {
    static const char optstring[] = "+hcCtdDfl:i:qQ:r:R:us:S:w:We:";
    static struct option options[] = {
      { "help",             0, 0, 'h' },
      { "version",          0, 0, 0},
      { "counter-based",    0, 0, 'c' },
      { "no-confirm",       0, 0, 'C' },
      { "time-based",       0, 0, 't' },
      { "disallow-reuse",   0, 0, 'd' },
      { "allow-reuse",      0, 0, 'D' },
      { "force",            0, 0, 'f' },
      { "label",            1, 0, 'l' },
      { "issuer",           1, 0, 'i' },
      { "quiet",            0, 0, 'q' },
      { "qr-mode",          1, 0, 'Q' },
      { "rate-limit",       1, 0, 'r' },
      { "rate-time",        1, 0, 'R' },
      { "no-rate-limit",    0, 0, 'u' },
      { "secret",           1, 0, 's' },
      { "step-size",        1, 0, 'S' },
      { "window-size",      1, 0, 'w' },
      { "minimal-window",   0, 0, 'W' },
      { "emergency-codes",  1, 0, 'e' },
      { 0,                  0, 0,  0  }
    };
    idx = -1;
    const int c = getopt_long(argc, argv, optstring, options, &idx);
    if (c > 0) {
      for (int i = 0; options[i].name; i++) {
        if (options[i].val == c) {
          idx = i;
          break;
        }
      }
    } else if (c < 0) {
      break;
    }
    if (idx-- <= 0) {
      // Help (or invalid argument)
    err:
      usage();
      if (idx < -1) {
        fprintf(stderr, "Failed to parse command line\n");
        _exit(1);
      }
      exit(0);
    } else if (!idx--) {
      // --version
      print_version();
      exit(0);
    } else if (!idx--) {
      // counter-based, -c
      if (mode != ASK_MODE) {
        fprintf(stderr, "Duplicate -c and/or -t option detected\n");
        _exit(1);
      }
      if (reuse != ASK_REUSE) {
      reuse_err:
        fprintf(stderr, "Reuse of tokens is not a meaningful parameter "
                "in counter-based mode\n");
        _exit(1);
      }
      mode = HOTP_MODE;
    } else if (!idx--) {
      // don't confirm code provisioned, -C
      confirm = 0;
    } else if (!idx--) {
      // time-based
      if (mode != ASK_MODE) {
        fprintf(stderr, "Duplicate -c and/or -t option detected\n");
        _exit(1);
      }
      mode = TOTP_MODE;
    } else if (!idx--) {
      // disallow-reuse
      if (reuse != ASK_REUSE) {
        fprintf(stderr, "Duplicate -d and/or -D option detected\n");
        _exit(1);
      }
      if (mode == HOTP_MODE) {
        goto reuse_err;
      }
      reuse = DISALLOW_REUSE;
    } else if (!idx--) {
      // allow-reuse
      if (reuse != ASK_REUSE) {
        fprintf(stderr, "Duplicate -d and/or -D option detected\n");
        _exit(1);
      }
      if (mode == HOTP_MODE) {
        goto reuse_err;
      }
      reuse = ALLOW_REUSE;
    } else if (!idx--) {
      // force
      if (force) {
        fprintf(stderr, "Duplicate -f option detected\n");
        _exit(1);
      }
      force = 1;
    } else if (!idx--) {
      // label
      if (label) {
        fprintf(stderr, "Duplicate -l option detected\n");
        _exit(1);
      }
      label = strdup(optarg);
    } else if (!idx--) {
      // issuer
      if (issuer) {
        fprintf(stderr, "Duplicate -i option detected\n");
        _exit(1);
      }
      issuer = strdup(optarg);
    } else if (!idx--) {
      // quiet
      if (quiet) {
        fprintf(stderr, "Duplicate -q option detected\n");
        _exit(1);
      }
      quiet = 1;
    } else if (!idx--) {
      // qr-mode
      if (qr_mode != QR_UNSET) {
        fprintf(stderr, "Duplicate -Q option detected\n");
        _exit(1);
      }
      if (!strcasecmp(optarg, "none")) {
        qr_mode = QR_NONE;
      } else if (!strcasecmp(optarg, "ansi")) {
        qr_mode = QR_ANSI;
      } else if (!strcasecmp(optarg, "utf8")) {
        qr_mode = QR_UTF8;
      } else {
        fprintf(stderr, "Invalid qr-mode \"%s\"\n", optarg);
        _exit(1);
      }
    } else if (!idx--) {
      // rate-limit
      if (r_limit > 0) {
        fprintf(stderr, "Duplicate -r option detected\n");
        _exit(1);
      } else if (r_limit < 0) {
        fprintf(stderr, "-u is mutually exclusive with -r\n");
        _exit(1);
      }
      char *endptr;
      errno = 0;
      const long l = strtol(optarg, &endptr, 10);
      if (errno || endptr == optarg || *endptr || l < 1 || l > 10) {
        fprintf(stderr, "-r requires an argument in the range 1..10\n");
        _exit(1);
      }
      r_limit = (int)l;
    } else if (!idx--) {
      // rate-time
      if (r_time > 0) {
        fprintf(stderr, "Duplicate -R option detected\n");
        _exit(1);
      } else if (r_time < 0) {
        fprintf(stderr, "-u is mutually exclusive with -R\n");
        _exit(1);
      }
      char *endptr;
      errno = 0;
      const long l = strtol(optarg, &endptr, 10);
      if (errno || endptr == optarg || *endptr || l < 15 || l > 600) {
        fprintf(stderr, "-R requires an argument in the range 15..600\n");
        _exit(1);
      }
      r_time = (int)l;
    } else if (!idx--) {
      // no-rate-limit
      if (r_limit > 0 || r_time > 0) {
        fprintf(stderr, "-u is mutually exclusive with -r/-R\n");
        _exit(1);
      }
      if (r_limit < 0) {
        fprintf(stderr, "Duplicate -u option detected\n");
        _exit(1);
      }
      r_limit = r_time = -1;
    } else if (!idx--) {
      // secret
      if (secret_fn) {
        fprintf(stderr, "Duplicate -s option detected\n");
        _exit(1);
      }
      if (!*optarg) {
        fprintf(stderr, "-s must be followed by a filename\n");
        _exit(1);
      }
      secret_fn = strdup(optarg);
      if (!secret_fn) {
        perror("malloc()");
        _exit(1);
      }
    } else if (!idx--) {
      // step-size
      if (step_size) {
        fprintf(stderr, "Duplicate -S option detected\n");
        _exit(1);
      }
      char *endptr;
      errno = 0;
      const long l = strtol(optarg, &endptr, 10);
      if (errno || endptr == optarg || *endptr || l < 1 || l > 60) {
        fprintf(stderr, "-S requires an argument in the range 1..60\n");
        _exit(1);
      }
      step_size = (int)l;
    } else if (!idx--) {
      // window-size
      if (window_size) {
        fprintf(stderr, "Duplicate -w/-W option detected\n");
        _exit(1);
      }
      char *endptr;
      errno = 0;
      const long l = strtol(optarg, &endptr, 10);
      if (errno || endptr == optarg || *endptr || l < 1 || l > 21) {
        fprintf(stderr, "-w requires an argument in the range 1..21\n");
        _exit(1);
      }
      window_size = (int)l;
    } else if (!idx--) {
      // minimal-window
      if (window_size) {
        fprintf(stderr, "Duplicate -w/-W option detected\n");
        _exit(1);
      }
      window_size = -1;
    } else if (!idx--) {
      // emergency-codes
      if (emergency_codes >= 0) {
        fprintf(stderr, "Duplicate -e option detected\n");
        _exit(1);
      }
      char *endptr;
      errno = 0;
      long l = strtol(optarg, &endptr, 10);
      if (errno || endptr == optarg || *endptr || l < 0 || l > MAX_SCRATCHCODES) {
        fprintf(stderr, "-e requires an argument in the range 0..%d\n", MAX_SCRATCHCODES);
        _exit(1);
      }
      emergency_codes = (int)l;
    } else {
      fprintf(stderr, "Error\n");
      _exit(1);
    }
  }
  idx = -1;
  if (optind != argc) {
    goto err;
  }
  if (reuse != ASK_REUSE && mode != TOTP_MODE) {
    fprintf(stderr, "Must select time-based mode, when using -d or -D\n");
    _exit(1);
  }
  if ((r_time && !r_limit) || (!r_time && r_limit)) {
    fprintf(stderr, "Must set -r when setting -R, and vice versa\n");
    _exit(1);
  }
  if (emergency_codes < 0) {
    emergency_codes = SCRATCHCODES;
  }
  if (!label) {
    const uid_t uid = getuid();
    const char *user = getUserName(uid);
    char hostname[128] = { 0 };
    if (gethostname(hostname, sizeof(hostname)-1)) {
      strcpy(hostname, "unix");
    }
    label = strcat(strcat(strcpy(malloc(strlen(user) + strlen(hostname) + 2),
                                 user), "@"), hostname);
    free((char *)user);
  }
  if (!issuer) {
    char hostname[128] = { 0 };
    if (gethostname(hostname, sizeof(hostname)-1)) {
      strcpy(hostname, "unix");
    }

    issuer = strdup(hostname);
  }
  // Not const because 'fd' is reused. TODO.
  int fd = open("/dev/urandom", O_RDONLY);
  if (fd < 0) {
    perror("Failed to open \"/dev/urandom\"");
    return 1;
  }
  if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
  urandom_failure:
    perror("Failed to read from \"/dev/urandom\"");
    return 1;
  }

  base32_encode(buf, SECRET_BITS/8, (uint8_t *)secret, sizeof(secret));
  int use_totp;
  if (mode == ASK_MODE) {
    use_totp = maybe("Do you want authentication tokens to be time-based");
  } else {
    use_totp = mode == TOTP_MODE;
  }
  if (!quiet) {
    displayEnrollInfo(secret, label, use_totp, issuer);
    printf("Your new secret key is: %s\n", secret);

    // Confirm code.
    if (confirm && use_totp) {
      for (;;) {
        const int test_code = ask_code("Enter code from app (-1 to skip):");
        if (test_code < 0) {
          printf("Code confirmation skipped\n");
          break;
        }
        const unsigned long tm = time(NULL)/(step_size ? step_size : 30);
        const int correct_code = generateCode(secret, tm);
        if (test_code == correct_code) {
          printf("Code confirmed\n");
          break;
        }
        printf("Code incorrect (correct code %06d). Try again.\n",
               correct_code);
      }
    } else {
      const unsigned long tm = 1;
      printf("Your verification code for code %lu is %06d\n",
             tm, generateCode(secret, tm));
    }
    printf("Your emergency scratch codes are:\n");
  }
  free(label);
  free(issuer);
  strcat(secret, "\n");
  if (use_totp) {
    strcat(secret, totp);
  } else {
    strcat(secret, hotp);
  }
  for (int i = 0; i < emergency_codes; ++i) {
  new_scratch_code:;
    int scratch = 0;
    for (int j = 0; j < BYTES_PER_SCRATCHCODE; ++j) {
      scratch = 256*scratch + buf[SECRET_BITS/8 + BYTES_PER_SCRATCHCODE*i + j];
    }
    int modulus = 1;
    for (int j = 0; j < SCRATCHCODE_LENGTH; j++) {
      modulus *= 10;
    }
    scratch = (scratch & 0x7FFFFFFF) % modulus;
    if (scratch < modulus/10) {
      // Make sure that scratch codes are always exactly eight digits. If they
      // start with a sequence of zeros, just generate a new scratch code.
      if (read(fd, buf + (SECRET_BITS/8 + BYTES_PER_SCRATCHCODE*i),
               BYTES_PER_SCRATCHCODE) != BYTES_PER_SCRATCHCODE) {
        goto urandom_failure;
      }
      goto new_scratch_code;
    }
    if (!quiet) {
      printf("  %08d\n", scratch);
    }
    snprintf(strrchr(secret, '\000'), sizeof(secret) - strlen(secret),
             "%08d\n", scratch);
  }
  close(fd);
  if (!secret_fn) {
    const char *home = getenv("HOME");
    if (!home || *home != '/') {
      fprintf(stderr, "Cannot determine home directory\n");
      return 1;
    }
    secret_fn = malloc(strlen(home) + strlen(SECRET) + 1);
    if (!secret_fn) {
      perror("malloc()");
      _exit(1);
    }
    strcat(strcpy(secret_fn, home), SECRET);
  }
  if (!force) {
    char s[1024];
    snprintf(s, sizeof s, "Do you want me to update your \"%s\" file?",
             secret_fn);
    if (!maybe(s)) {
      exit(0);
    }
  }

  const int size = strlen(secret_fn) + 3;
  char* tmp_fn = malloc(size);
  if (!tmp_fn) {
    perror("malloc()");
    _exit(1);
  }
  snprintf(tmp_fn, size, "%s~", secret_fn);

  // Add optional flags.
  if (use_totp) {
    if (reuse == ASK_REUSE) {
      maybeAddOption("Do you want to disallow multiple uses of the same "
                     "authentication\ntoken? This restricts you to one login "
                     "about every 30s, but it increases\nyour chances to "
                     "notice or even prevent man-in-the-middle attacks",
                     secret, sizeof(secret), disallow);
    } else if (reuse == DISALLOW_REUSE) {
      addOption(secret, sizeof(secret), disallow);
    }
    if (step_size) {
      char s[80];
      snprintf(s, sizeof s, "\" STEP_SIZE %d\n", step_size);
      addOption(secret, sizeof(secret), s);
    }
    if (!window_size) {
      maybeAddOption("By default, a new token is generated every 30 seconds by"
                     " the mobile app.\nIn order to compensate for possible"
                     " time-skew between the client and the server,\nwe allow"
                     " an extra token before and after the current time. This"
                     " allows for a\ntime skew of up to 30 seconds between"
                     " authentication server and client. If you\nexperience"
                     " problems with poor time synchronization, you can"
                     " increase the window\nfrom its default size of 3"
                     " permitted codes (one previous code, the current\ncode,"
                     " the next code) to 17 permitted codes (the 8 previous"
                     " codes, the current\ncode, and the 8 next codes)."
                     " This will permit for a time skew of up to 4 minutes"
                     "\nbetween client and server."
                     "\nDo you want to do so?",
                     secret, sizeof(secret), window);
    } else {
      char s[80];
      // TODO: Should 3 really be the minimal window size for TOTP?
      // If so, the code should not allow -w=1 here.
      snprintf(s, sizeof s, "\" WINDOW_SIZE %d\n", window_size > 0 ? window_size : 3);
      addOption(secret, sizeof(secret), s);
    }
  } else {
    // Counter based.
    if (!window_size) {
      maybeAddOption("By default, three tokens are valid at any one time.  "
                     "This accounts for\ngenerated-but-not-used tokens and "
                     "failed login attempts. In order to\ndecrease the "
                     "likelihood of synchronization problems, this window "
                     "can be\nincreased from its default size of 3 to 17. Do "
                     "you want to do so?",
                     secret, sizeof(secret), window);
    } else {
      char s[80];
      snprintf(s, sizeof s, "\" WINDOW_SIZE %d\n", window_size > 0 ? window_size : 1);
      addOption(secret, sizeof(secret), s);
    }
  }
  if (!r_limit && !r_time) {
    maybeAddOption("If the computer that you are logging into isn't hardened "
                   "against brute-force\nlogin attempts, you can enable "
                   "rate-limiting for the authentication module.\nBy default, "
                   "this limits attackers to no more than 3 login attempts "
                   "every 30s.\nDo you want to enable rate-limiting?",
                   secret, sizeof(secret), ratelimit);
  } else if (r_limit > 0 && r_time > 0) {
    char s[80];
    snprintf(s, sizeof s, "\" RATE_LIMIT %d %d\n", r_limit, r_time);
    addOption(secret, sizeof(secret), s);
  }

  fd = open(tmp_fn, O_WRONLY|O_EXCL|O_CREAT|O_NOFOLLOW|O_TRUNC, 0400);
  if (fd < 0) {
    fprintf(stderr, "Failed to create \"%s\" (%s)",
            secret_fn, strerror(errno));
    goto errout;
  }
  if (write(fd, secret, strlen(secret)) != (ssize_t)strlen(secret) ||
      rename(tmp_fn, secret_fn)) {
    perror("Failed to write new secret");
    unlink(secret_fn);
    goto errout;
  }

  free(tmp_fn);
  free(secret_fn);
  close(fd);

  return 0;

errout:
  if (fd > 0) {
    close(fd);
  }
  free(secret_fn);
  free(tmp_fn);
  return 1;
}
/* ---- Emacs Variables ----
 * Local Variables:
 * c-basic-offset: 2
 * indent-tabs-mode: nil
 * End:
 */
07070100000021000081A40000000000000000000000016627DD81000009FE000000000000000000000000000000000000002C00000000google-authenticator-libpam-1.10/src/hmac.c// HMAC_SHA1 implementation
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
//
// 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.

#include <string.h>

#include "hmac.h"
#include "sha1.h"
#include "util.h"

void hmac_sha1(const uint8_t *key, int keyLength,
               const uint8_t *data, int dataLength,
               uint8_t *result, int resultLength) {
  SHA1_INFO ctx;
  uint8_t hashed_key[SHA1_DIGEST_LENGTH];
  if (keyLength > 64) {
    // The key can be no bigger than 64 bytes. If it is, we'll hash it down to
    // 20 bytes.
    sha1_init(&ctx);
    sha1_update(&ctx, key, keyLength);
    sha1_final(&ctx, hashed_key);
    key = hashed_key;
    keyLength = SHA1_DIGEST_LENGTH;
  }

  // The key for the inner digest is derived from our key, by padding the key
  // the full length of 64 bytes, and then XOR'ing each byte with 0x36.
  uint8_t tmp_key[64];
  for (int i = 0; i < keyLength; ++i) {
    tmp_key[i] = key[i] ^ 0x36;
  }
  if (keyLength < 64) {
    memset(tmp_key + keyLength, 0x36, 64 - keyLength);
  }

  // Compute inner digest
  sha1_init(&ctx);
  sha1_update(&ctx, tmp_key, 64);
  sha1_update(&ctx, data, dataLength);
  uint8_t sha[SHA1_DIGEST_LENGTH];
  sha1_final(&ctx, sha);

  // The key for the outer digest is derived from our key, by padding the key
  // the full length of 64 bytes, and then XOR'ing each byte with 0x5C.
  for (int i = 0; i < keyLength; ++i) {
    tmp_key[i] = key[i] ^ 0x5C;
  }
  memset(tmp_key + keyLength, 0x5C, 64 - keyLength);

  // Compute outer digest
  sha1_init(&ctx);
  sha1_update(&ctx, tmp_key, 64);
  sha1_update(&ctx, sha, SHA1_DIGEST_LENGTH);
  sha1_final(&ctx, sha);

  // Copy result to output buffer and truncate or pad as necessary
  memset(result, 0, resultLength);
  if (resultLength > SHA1_DIGEST_LENGTH) {
    resultLength = SHA1_DIGEST_LENGTH;
  }
  memcpy(result, sha, resultLength);

  // Zero out all internal data structures
  explicit_bzero(hashed_key, sizeof(hashed_key));
  explicit_bzero(sha, sizeof(sha));
  explicit_bzero(tmp_key, sizeof(tmp_key));
}
07070100000022000081A40000000000000000000000016627DD8100000397000000000000000000000000000000000000002C00000000google-authenticator-libpam-1.10/src/hmac.h// HMAC_SHA1 implementation
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
//
// 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.

#ifndef _HMAC_H_
#define _HMAC_H_

#include <stdint.h>

void hmac_sha1(const uint8_t *key, int keyLength,
               const uint8_t *data, int dataLength,
               uint8_t *result, int resultLength)
 __attribute__((visibility("hidden")));

#endif /* _HMAC_H_ */
07070100000023000081A40000000000000000000000016627DD810001014C000000000000000000000000000000000000004000000000google-authenticator-libpam-1.10/src/pam_google_authenticator.c// PAM module for two-factor authentication.
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
//
// 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.
#include "config.h"

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>

#ifdef HAVE_SYS_FSUID_H
// We much rather prefer to use setfsuid(), but this function is unfortunately
// not available on all systems.
#include <sys/fsuid.h>
#endif

#ifndef PAM_EXTERN
#define PAM_EXTERN
#endif

#if !defined(LOG_AUTHPRIV) && defined(LOG_AUTH)
#define LOG_AUTHPRIV LOG_AUTH
#endif

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

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

// Module name shortened to work with rsyslog.
// See https://github.com/google/google-authenticator-libpam/issues/172
#define MODULE_NAME   "pam_google_auth"

#define SECRET        "~/.google_authenticator"
#define CODE_PROMPT   "Verification code: "
#define PWCODE_PROMPT "Password & verification code: "

typedef struct Params {
  const char *secret_filename_spec;
  const char *authtok_prompt;
  enum { NULLERR=0, NULLOK, SECRETNOTFOUND } nullok;
  int        noskewadj;
  int        echocode;
  int        fixed_uid;
  int        no_increment_hotp;
  uid_t      uid;
  enum { PROMPT = 0, TRY_FIRST_PASS, USE_FIRST_PASS } pass_mode;
  int        forward_pass;
  int        debug;
  int        no_strict_owner;
  int        allowed_perm;
  time_t     grace_period;
  int        allow_readonly;
} Params;

static char oom;

static const char* nobody = "nobody";

#if defined(DEMO) || defined(TESTING)
static char* error_msg = NULL;

const char *get_error_msg(void) __attribute__((visibility("default")));
const char *get_error_msg(void) {
  if (!error_msg) {
    return "";
  }
  return error_msg;
}
#endif

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);
#if !defined(DEMO) && !defined(TESTING)
  openlog(logname, LOG_CONS | LOG_PID, LOG_AUTHPRIV);
  vsyslog(priority, format, args);
  closelog();
#else
  if (!error_msg) {
    error_msg = strdup("");
  }
  {
    char buf[1000];
    vsnprintf(buf, sizeof buf, format, args);
    const int newlen = strlen(error_msg) + 1 + strlen(buf) + 1;
    char* n = malloc(newlen);
    if (n) {
      snprintf(n, newlen, "%s%s%s", error_msg, strlen(error_msg)?"\n":"",buf);
      free(error_msg);
      error_msg = n;
    } else {
      fprintf(stderr, "Failed to malloc %d bytes for log data.\n", newlen);
    }
  }
#endif

  va_end(args);

  if (priority == LOG_EMERG) {
    // Something really bad happened. There is no way we can proceed safely.
    _exit(1);
  }
}

static int converse(pam_handle_t *pamh, int nargs,
                    PAM_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, const Params *params) {
  // Obtain the user's name
  const char *username;
  if (pam_get_user(pamh, &username, NULL) != PAM_SUCCESS ||
      !username || !*username) {
    log_message(LOG_ERR, pamh,
                "pam_get_user() failed to get a user name"
                " when checking verification code");
    return NULL;
  }
  if (params->debug) {
    log_message(LOG_INFO, pamh, "debug: start of google_authenticator for \"%s\"", username);
  }
  return username;
}

/*
 * Return rhost as a string. Return value must not be free()ed.
 * Returns NULL if PAM_RHOST is not known.
 */
static const char *
get_rhost(pam_handle_t *pamh, const Params *params) {
  // Get the remote host
  PAM_CONST void *rhost;
  if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS) {
    log_message(LOG_ERR, pamh, "pam_get_rhost() failed to get the remote host");
    return NULL;
  }
  if (params->debug) {
    log_message(LOG_INFO, pamh, "debug: google_authenticator for host \"%s\"",
                rhost);
  }
  return (const char *)rhost;
}

static size_t
getpwnam_buf_max_size()
{
#ifdef _SC_GETPW_R_SIZE_MAX
  const ssize_t len = sysconf(_SC_GETPW_R_SIZE_MAX);
  if (len <= 0) {
    return 4096;
  }
  return len;
#else
  return 4096;
#endif
}

static char *get_secret_filename(pam_handle_t *pamh, const Params *params,
                                 const char *username, uid_t *uid) {
  if (!username) {
    return NULL;
  }

  // Check whether the administrator decided to override the default location
  // for the secret file.
  const char *spec = params->secret_filename_spec
    ? params->secret_filename_spec : SECRET;

  // Obtain the user's id and home directory
  // NOTE: These variables need to be here because of their lifetimes.
  struct passwd *pw = NULL;     // Used
  struct passwd pwbuf;          // Here because `pw` points into it.
  char *buf = NULL;             // Here because `pw` members use it.
  char *secret_filename = NULL; // Here because goto jumps.

  if (!params->fixed_uid) {
    const int len = getpwnam_buf_max_size();
    buf = malloc(len);
    *uid = -1;
    if (buf == NULL) {
      log_message(LOG_ERR, pamh, "Short (%d) mem allocation failed", len);
      goto errout;
    }

    const int rc = getpwnam_r(username, &pwbuf, buf, len, &pw);
    if (rc) {
      log_message(LOG_ERR, pamh, "getpwnam_r(\"%s\")!=0: %d", username, rc);
      goto errout;
    }

    if (!pw) {
      log_message(LOG_ERR, pamh, "user(\"%s\") not found", username);
      goto errout;
    }

    if (!pw->pw_dir) {
      log_message(LOG_ERR, pamh, "user(\"%s\") has no home dir", username);
      goto errout;
    }

    if (*pw->pw_dir != '/') {
      log_message(LOG_ERR, pamh, "User \"%s\" home dir not absolute", username);
      goto errout;
    }
  }

  // Expand filename specification to an actual filename.
  if ((secret_filename = strdup(spec)) == NULL) {
    log_message(LOG_ERR, pamh, "Short (%d) mem allocation failed", strlen(spec));
    goto errout;
  }
  int allow_tilde = 1;
  for (int offset = 0; secret_filename[offset];) {
    char *cur = secret_filename + offset;
    char *var = NULL;
    size_t var_len = 0;
    const char *subst = NULL;
    if (allow_tilde && *cur == '~') {
      var_len = 1;
      if (!pw) {
        log_message(LOG_ERR, pamh,
                    "Home dir in 'secret' not implemented when 'user' set");
        goto errout;
      }
      subst = pw->pw_dir;
      var = cur;
    } else if (secret_filename[offset] == '$') {
      if (!memcmp(cur, "${HOME}", 7)) {
        var_len = 7;
        if (!pw) {
          log_message(LOG_ERR, pamh,
                      "Home dir in 'secret' not implemented when 'user' set");
          goto errout;
        }
        subst = pw->pw_dir;
        var = cur;
      } else if (!memcmp(cur, "${USER}", 7)) {
        var_len = 7;
        subst = username;
        var = cur;
      }
    }
    if (var) {
      const size_t subst_len = strlen(subst);
      if (subst_len > 1000000) {
        log_message(LOG_ERR, pamh, "Unexpectedly large path name: %d", subst_len);
        goto errout;
      }
      const int varidx = var - secret_filename;
      char *resized = realloc(secret_filename,
                              strlen(secret_filename) + subst_len + 1);
      if (!resized) {
        log_message(LOG_ERR, pamh, "Short mem allocation failed");
        goto errout;
      }
      var = resized + varidx;
      secret_filename = resized;
      memmove(var + subst_len, var + var_len, strlen(var + var_len) + 1);
      memmove(var, subst, subst_len);
      offset = var + subst_len - resized;
      allow_tilde = 0;
    } else {
      allow_tilde = *cur == '/';
      ++offset;
    }
  }

  *uid = params->fixed_uid ? params->uid : pw->pw_uid;
  free(buf);
  return secret_filename;

errout:
  free(secret_filename);
  free(buf);
  return NULL;
}

static int setuser(int uid) {
#ifdef HAVE_SETFSUID
  // The semantics for setfsuid() are a little unusual. On success, the
  // previous user id is returned. On failure, the current user id is returned.
  int old_uid = setfsuid(uid);
  if (uid != setfsuid(uid)) {
    setfsuid(old_uid);
    return -1;
  }
#else
#ifdef linux
#error "Linux should have setfsuid(). Refusing to build."
#endif
  int old_uid = geteuid();
  if (old_uid != uid && seteuid(uid)) {
    return -1;
  }
#endif
  return old_uid;
}

static int setgroup(int gid) {
#ifdef HAVE_SETFSGID
  // The semantics of setfsgid() are a little unusual. On success, the
  // previous group id is returned. On failure, the current groupd id is
  // returned.
  int old_gid = setfsgid(gid);
  if (gid != setfsgid(gid)) {
    setfsgid(old_gid);
    return -1;
  }
#else
  int old_gid = getegid();
  if (old_gid != gid && setegid(gid)) {
    return -1;
  }
#endif
  return old_gid;
}

// Drop privileges and return 0 on success.
static int drop_privileges(pam_handle_t *pamh, const char *username, int uid,
                           int *old_uid, int *old_gid) {
  // Try to become the new user. This might be necessary for NFS mounted home
  // directories.

  // First, look up the user's default group
  #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);
  if (!buf) {
    log_message(LOG_ERR, pamh, "Out of memory");
    return -1;
  }
  struct passwd pwbuf, *pw;
  if (getpwuid_r(uid, &pwbuf, buf, len, &pw) || !pw) {
    log_message(LOG_ERR, pamh, "Cannot look up user id %d", uid);
    free(buf);
    return -1;
  }
  gid_t gid = pw->pw_gid;
  free(buf);

  int gid_o = setgroup(gid);
  int uid_o = setuser(uid);
  if (uid_o < 0) {
    if (gid_o >= 0) {
      if (setgroup(gid_o) < 0 || setgroup(gid_o) != gid_o) {
        // Inform the caller that we were unsuccessful in resetting the group.
        *old_gid = gid_o;
      }
    }
    log_message(LOG_ERR, pamh, "Failed to change user id to \"%s\"",
                username);
    return -1;
  }
  if (gid_o < 0 && (gid_o = setgroup(gid)) < 0) {
    // In most typical use cases, the PAM module will end up being called
    // while uid=0. This allows the module to change to an arbitrary group
    // prior to changing the uid. But there are many ways that PAM modules
    // can be invoked and in some scenarios this might not work. So, we also
    // try changing the group _after_ changing the uid. It might just work.
    if (setuser(uid_o) < 0 || setuser(uid_o) != uid_o) {
      // Inform the caller that we were unsuccessful in resetting the uid.
      *old_uid = uid_o;
    }
    log_message(LOG_ERR, pamh,
                "Failed to change group id for user \"%s\" to %d", username,
                (int)gid);
    return -1;
  }

  *old_uid = uid_o;
  *old_gid = gid_o;
  return 0;
}

// open secret file, return fd on success, or <0 on error.
static int open_secret_file(pam_handle_t *pamh, const char *secret_filename,
                            struct Params *params, const char *username,
                            int uid, struct stat *orig_stat) {
  // Try to open "~/.google_authenticator"
  const int fd = open(secret_filename, O_RDONLY);
  if (fd < 0 ||
      fstat(fd, orig_stat) < 0) {
    if (params->nullok != NULLERR && errno == ENOENT) {
      // The user doesn't have a state file, but the administrator said
      // that this is OK. We still return an error from open_secret_file(),
      // but we remember that this was the result of a missing state file.
      params->nullok = SECRETNOTFOUND;
    } else {
      log_message(LOG_ERR, pamh, "Failed to read \"%s\" for \"%s\": %s",
                  secret_filename, username, strerror(errno));
    }
 error:
    if (fd >= 0) {
      close(fd);
    }
    return -1;
  }

  if (params->debug) {
    log_message(LOG_INFO, pamh,
                "debug: Secret file permissions are %04o."
                " Allowed permissions are %04o",
                orig_stat->st_mode & 03777, params->allowed_perm);
  }

  // Check permissions on "~/.google_authenticator".
  if (!S_ISREG(orig_stat->st_mode)) {
    log_message(LOG_ERR, pamh, "Secret file \"%s\" is not a regular file",
                secret_filename);
    goto error;
  }
  if (orig_stat->st_mode & 03777 & ~params->allowed_perm) {
    log_message(LOG_ERR, pamh,
                "Secret file \"%s\" permissions (%04o)"
                " are more permissive than %04o", secret_filename,
                orig_stat->st_mode & 03777, params->allowed_perm);
    goto error;
  }

  if (!params->no_strict_owner && (orig_stat->st_uid != (uid_t)uid)) {
    char buf[80];
    if (params->fixed_uid) {
      snprintf(buf, sizeof buf, "user id %d", params->uid);
      username = buf;
    }
    log_message(LOG_ERR, pamh,
                "Secret file \"%s\" must be owned by \"%s\"",
                secret_filename, username);
    goto error;
  }

  // Sanity check for file length
  if (orig_stat->st_size < 1 || orig_stat->st_size > 64*1024) {
    log_message(LOG_ERR, pamh,
                "Invalid file size for \"%s\"", secret_filename);
    goto error;
  }

  return fd;
}

// Read secret file contents.
// If there's an error the file is closed, NULL is returned, and errno set.
static char *read_file_contents(pam_handle_t *pamh,
                                const Params *params,
                                const char *secret_filename, int *fd,
                                off_t filesize) {
  // Arbitrary limit to prevent integer overflow.
  if (filesize > 1000000) {
    close(*fd);
    errno = E2BIG;
    return NULL;
  }

  // Read file contents
  char *buf = malloc(filesize + 1);
  if (!buf) {
    log_message(LOG_ERR, pamh, "Failed to malloc %d+1", filesize);
    goto out;
  }

  if (filesize != read(*fd, buf, filesize)) {
    log_message(LOG_ERR, pamh, "Could not read \"%s\"", secret_filename);
    goto out;
  }
  close(*fd);
  *fd = -1;

  // The rest of the code assumes that there are no NUL bytes in the file.
  if (memchr(buf, 0, filesize)) {
    log_message(LOG_ERR, pamh, "Invalid file contents in \"%s\"",
                secret_filename);
    goto out;
  }

  // Terminate the buffer with a NUL byte.
  buf[filesize] = '\000';

  if(params->debug) {
    log_message(LOG_INFO, pamh, "debug: \"%s\" read", secret_filename);
  }
  return buf;

out:
  // If we have any data, erase it.
  if (buf) {
    explicit_bzero(buf, filesize);
  }
  free(buf);
  if (*fd >= 0) {
    close(*fd);
    *fd = -1;
  }
  return NULL;
}

static int is_totp(const char *buf) {
  return !!strstr(buf, "\" TOTP_AUTH");
}

// Wrap write() making sure that partial writes don't break everything.
// Return 0 on success, errno otherwise.
static int
full_write(int fd, const char* buf, size_t len) {
  const char* p = buf;
  int errors = 0;
  for (;;) {
    const ssize_t left = len - (p - buf);
    const ssize_t rc = write(fd, p, left);
    if (rc == left) {
      return 0;
    }
    if (rc < 0) {
      switch (errno) {
      case EAGAIN:
      case EINTR:
        if (errors++ < 3) {
          continue;
        }
      }
      return errno;
    }
    p += rc;
  }
}

// Safely overwrite the old secret file.
// Return 0 on success, errno otherwise.
static int write_file_contents(pam_handle_t *pamh,
                               const Params *params,
                               const char *secret_filename,
                               struct stat *orig_stat,
                               const char *buf) {
  int err = 0;
  int fd = -1;
  const size_t fnlength = strlen(secret_filename) + 1 + 6 + 1;

  char *tmp_filename = malloc(fnlength);
  if (tmp_filename == NULL) {
    err = errno;
    goto cleanup;
  }

  if (fnlength - 1 != snprintf(tmp_filename, fnlength,
                               "%s~XXXXXX", secret_filename)) {
    err = ERANGE;
    goto cleanup;
  }
  const mode_t old_mask = umask(077);
  fd = mkstemp(tmp_filename);
  umask(old_mask);
  if (fd < 0) {
    err = errno;
    log_message(LOG_ERR, pamh, "Failed to create tempfile \"%s\": %s",
                tmp_filename, strerror(err));

    // Couldn't open file; don't try to delete it later.
    free(tmp_filename);
    tmp_filename = NULL;
    goto cleanup;
  }
  if (fchmod(fd, 0400)) {
    err = errno;
    goto cleanup;
  }

  // Make sure the secret file is still the same. This prevents attackers
  // from opening a lot of pending sessions and then reusing the same
  // scratch code multiple times.
  //
  // (except for the brief race condition between this stat and the
  // `rename` below)
  {
    struct stat sb;
    if (stat(secret_filename, &sb) != 0) {
      err = errno;
      log_message(LOG_ERR, pamh, "stat(): %s", strerror(err));
      goto cleanup;
    }

    if (sb.st_ino != orig_stat->st_ino ||
        sb.st_size != orig_stat->st_size ||
        sb.st_mtime != orig_stat->st_mtime) {
      err = EAGAIN;
      log_message(LOG_ERR, pamh,
                  "Secret file \"%s\" changed while trying to use "
                  "scratch code\n", secret_filename);
      goto cleanup;
    }
  }

  // Write the new file contents.
  if ((err = full_write(fd, buf, strlen(buf)))) {
    log_message(LOG_ERR, pamh, "write(): %s", strerror(err));
    goto cleanup;
  }
  if (fsync(fd)) {
    err = errno;
    log_message(LOG_ERR, pamh, "fsync(): %s", strerror(err));
    goto cleanup;
  }
  if (close(fd)) {
    err = errno;
    log_message(LOG_ERR, pamh, "close(): %s", strerror(err));
    goto cleanup;
  }
  fd = -1; // Prevent double-close.

  // Double-check that the file size is correct.
  {
    struct stat st;
    if (stat(tmp_filename, &st)) {
      err = errno;
      log_message(LOG_ERR, pamh, "stat(%s): %s", tmp_filename, strerror(err));
      goto cleanup;
    }
    const off_t want = strlen(buf);
    if (st.st_size == 0 || (want != st.st_size)) {
      err = EAGAIN;
      log_message(LOG_ERR, pamh, "temp file size %d. Should be non-zero and %d", st.st_size, want);
      goto cleanup;
    }
  }
  
  if (rename(tmp_filename, secret_filename) != 0) {
    err = errno;
    log_message(LOG_ERR, pamh, "rename(): %s", strerror(err));
    goto cleanup;
  }
  free(tmp_filename);
  tmp_filename = NULL; // Prevent unlink & double-free.

  if (params->debug) {
    log_message(LOG_INFO, pamh, "debug: \"%s\" written", secret_filename);
  }

cleanup:
  if (fd >= 0) {
    close(fd);
  }
  if (tmp_filename) {
    if (unlink(tmp_filename)) {
      log_message(LOG_ERR, pamh, "Failed to delete tempfile \"%s\": %s",
                  tmp_filename, strerror(errno));
    }
  }
  free(tmp_filename);

  if (err) {
    log_message(LOG_ERR, pamh, "Failed to update secret file \"%s\": %s",
                secret_filename, strerror(err));
    return err;
  }
  return 0;
}

// given secret file content (buf), extract the secret and base32 decode it.
//
// Return pointer to `malloc()`'d secret on success (caller frees),
// NULL on error. Length of secret stored in *secretLen.
static uint8_t *get_shared_secret(pam_handle_t *pamh,
                                  const Params *params,
                                  const char *secret_filename,
                                  const char *buf, int *secretLen) {
  if (!buf) {
    return NULL;
  }
  // Decode secret key
  const int base32Len = strcspn(buf, "\n");

  // Arbitrary limit to prevent integer overflow.
  if (base32Len > 100000) {
    return NULL;
  }

  *secretLen = (base32Len*5 + 7)/8;
  uint8_t *secret = malloc(base32Len + 1);
  if (secret == NULL) {
    *secretLen = 0;
    return NULL;
  }
  memcpy(secret, buf, base32Len);
  secret[base32Len] = '\000';
  if ((*secretLen = base32_decode(secret, secret, base32Len)) < 1) {
    log_message(LOG_ERR, pamh,
                "Could not find a valid BASE32 encoded secret in \"%s\"",
                secret_filename);
    explicit_bzero(secret, base32Len);
    free(secret);
    return NULL;
  }
  memset(secret + *secretLen, 0, base32Len + 1 - *secretLen);

  if(params->debug) {
    log_message(LOG_INFO, pamh, "debug: shared secret in \"%s\" processed", secret_filename);
  }
  return secret;
}

#ifdef TESTING
static time_t current_time;
void set_time(time_t t) __attribute__((visibility("default")));
void set_time(time_t t) {
  current_time = t;
}

static time_t get_time(void) {
  return current_time;
}
#else
static time_t get_time(void) {
  return time(NULL);
}
#endif

static int comparator(const void *a, const void *b) {
  return *(unsigned int *)a - *(unsigned int *)b;
}

static char *get_cfg_value(pam_handle_t *pamh, const char *key,
                           const char *buf) {
  const size_t key_len = strlen(key);
  for (const char *line = buf; *line; ) {
    const char *ptr;
    if (line[0] == '"' && line[1] == ' ' && !strncmp(line+2, key, key_len) &&
        (!*(ptr = line+2+key_len) || *ptr == ' ' || *ptr == '\t' ||
         *ptr == '\r' || *ptr == '\n')) {
      ptr += strspn(ptr, " \t");
      size_t val_len = strcspn(ptr, "\r\n");
      char *val = malloc(val_len + 1);
      if (!val) {
        log_message(LOG_ERR, pamh, "Out of memory");
        return &oom;
      } else {
        memcpy(val, ptr, val_len);
        val[val_len] = '\000';
        return val;
      }
    } else {
      line += strcspn(line, "\r\n");
      line += strspn(line, "\r\n");
    }
  }
  return NULL;
}

static int set_cfg_value(pam_handle_t *pamh, const char *key, const char *val,
                         char **buf) {
  const size_t key_len = strlen(key);
  char *start = NULL;
  char *stop = NULL;

  // Find an existing line, if any.
  for (char *line = *buf; *line; ) {
    char *ptr;
    if (line[0] == '"' && line[1] == ' ' && !strncmp(line+2, key, key_len) &&
        (!*(ptr = line+2+key_len) || *ptr == ' ' || *ptr == '\t' ||
         *ptr == '\r' || *ptr == '\n')) {
      start = line;
      stop  = start + strcspn(start, "\r\n");
      stop += strspn(stop, "\r\n");
      break;
    } else {
      line += strcspn(line, "\r\n");
      line += strspn(line, "\r\n");
    }
  }

  // If no existing line, insert immediately after the first line.
  if (!start) {
    start  = *buf + strcspn(*buf, "\r\n");
    start += strspn(start, "\r\n");
    stop   = start;
  }

  // Replace [start..stop] with the new contents.
  const size_t val_len = strlen(val);
  const size_t total_len = key_len + val_len + 4;
  if (total_len <= stop - start) {
    // We are decreasing out space requirements. Shrink the buffer and pad with
    // NUL characters.
    const size_t tail_len = strlen(stop);
    memmove(start + total_len, stop, tail_len + 1);
    memset(start + total_len + tail_len, 0, stop - start - total_len + 1);
  } else {
    // Must resize existing buffer. We cannot call realloc(), as it could
    // leave parts of the buffer content in unused parts of the heap.
    const size_t buf_len = strlen(*buf);
    const size_t tail_len = buf_len - (stop - *buf);
    char *resized = malloc(buf_len - (stop - start) + total_len + 1);
    if (!resized) {
      log_message(LOG_ERR, pamh, "Out of memory");
      return -1;
    }
    memcpy(resized, *buf, start - *buf);
    memcpy(resized + (start - *buf) + total_len, stop, tail_len + 1);
    memset(*buf, 0, buf_len);
    free(*buf);
    start = start - *buf + resized;
    *buf = resized;
  }

  // Fill in new contents.
  start[0] = '"';
  start[1] = ' ';
  memcpy(start + 2, key, key_len);
  start[2+key_len] = ' ';
  memcpy(start+3+key_len, val, val_len);
  start[3+key_len+val_len] = '\n';

  // Check if there are any other occurrences of "value". If so, delete them.
  for (char *line = start + 4 + key_len + val_len; *line; ) {
    char *ptr;
    if (line[0] == '"' && line[1] == ' ' && !strncmp(line+2, key, key_len) &&
        (!*(ptr = line+2+key_len) || *ptr == ' ' || *ptr == '\t' ||
         *ptr == '\r' || *ptr == '\n')) {
      start = line;
      stop = start + strcspn(start, "\r\n");
      stop += strspn(stop, "\r\n");
      size_t tail_len = strlen(stop);
      memmove(start, stop, tail_len + 1);
      memset(start + tail_len, 0, stop - start);
      line = start;
    } else {
      line += strcspn(line, "\r\n");
      line += strspn(line, "\r\n");
    }
  }

  return 0;
}

static int step_size(pam_handle_t *pamh, const char *secret_filename,
                     const char *buf) {
  const char *value = get_cfg_value(pamh, "STEP_SIZE", buf);
  if (!value) {
    // Default step size is 30.
    return 30;
  } else if (value == &oom) {
    // Out of memory. This is a fatal error.
    return 0;
  }

  char *endptr;
  errno = 0;
  const int step = (int)strtoul(value, &endptr, 10);
  if (errno || !*value || value == endptr ||
      (*endptr && *endptr != ' ' && *endptr != '\t' &&
       *endptr != '\n' && *endptr != '\r') ||
      step < 1 || step > 60) {
    free((void *)value);
    log_message(LOG_ERR, pamh, "Invalid STEP_SIZE option in \"%s\"",
                secret_filename);
    return 0;
  }
  free((void *)value);
  return step;
}

static int get_timestamp(pam_handle_t *pamh, const char *secret_filename,
                         const char **buf) {
  const int step = step_size(pamh, secret_filename, *buf);
  if (!step) {
    return 0;
  }
  return get_time()/step;
}

static long get_hotp_counter(pam_handle_t *pamh, const char *buf) {
  if (!buf) {
    return -1;
  }
  const char *counter_str = get_cfg_value(pamh, "HOTP_COUNTER", buf);
  if (counter_str == &oom) {
    // Out of memory. This is a fatal error
    return -1;
  }

  long counter = 0;
  if (counter_str) {
    counter = strtol(counter_str, NULL, 10);
  }
  free((void *)counter_str);

  return counter;
}

static int rate_limit(pam_handle_t *pamh, const char *secret_filename,
                      int *updated, char **buf) {
  const char *value = get_cfg_value(pamh, "RATE_LIMIT", *buf);
  if (!value) {
    // Rate limiting is not enabled for this account
    return 0;
  } else if (value == &oom) {
    // Out of memory. This is a fatal error.
    return -1;
  }

  // Parse both the maximum number of login attempts and the time interval
  // that we are looking at.
  const char *endptr = value, *ptr;
  int attempts, interval;
  errno = 0;
  if (((attempts = (int)strtoul(ptr = endptr, (char **)&endptr, 10)) < 1) ||
      ptr == endptr ||
      attempts > 100 ||
      errno ||
      (*endptr != ' ' && *endptr != '\t') ||
      ((interval = (int)strtoul(ptr = endptr, (char **)&endptr, 10)) < 1) ||
      ptr == endptr ||
      interval > 3600 ||
      errno) {
    free((void *)value);
    log_message(LOG_ERR, pamh, "Invalid RATE_LIMIT option. Check \"%s\".",
                secret_filename);
    return -1;
  }

  // Parse the time stamps of all previous login attempts.
  const unsigned int now = get_time();
  unsigned int *timestamps = malloc(sizeof(int));
  if (!timestamps) {
  oom:
    free((void *)value);
    log_message(LOG_ERR, pamh, "Out of memory");
    return -1;
  }
  timestamps[0] = now;
  int num_timestamps = 1;
  while (*endptr && *endptr != '\r' && *endptr != '\n') {
    unsigned int timestamp;
    errno = 0;
    if ((*endptr != ' ' && *endptr != '\t') ||
        ((timestamp = (int)strtoul(ptr = endptr, (char **)&endptr, 10)),
         errno) ||
        ptr == endptr) {
      free((void *)value);
      free(timestamps);
      log_message(LOG_ERR, pamh, "Invalid list of timestamps in RATE_LIMIT. "
                  "Check \"%s\".", secret_filename);
      return -1;
    }
    num_timestamps++;
    unsigned int *tmp = (unsigned int *)realloc(timestamps,
                                                sizeof(int) * num_timestamps);
    if (!tmp) {
      free(timestamps);
      goto oom;
    }
    timestamps = tmp;
    timestamps[num_timestamps-1] = timestamp;
  }
  free((void *)value);
  value = NULL;

  // Sort time stamps, then prune all entries outside of the current time
  // interval.
  qsort(timestamps, num_timestamps, sizeof(int), comparator);
  int start = 0, stop = -1;
  for (int i = 0; i < num_timestamps; ++i) {
    if (timestamps[i] < now - interval) {
      start = i+1;
    } else if (timestamps[i] > now) {
      break;
    }
    stop = i;
  }

  // Error out, if there are too many login attempts.
  int exceeded = 0;
  if (stop - start + 1 > attempts) {
    exceeded = 1;
    start = stop - attempts + 1;
  }

  // Construct new list of timestamps within the current time interval.
  char* list;
  {
    const size_t list_size = 25 * (2 + (stop - start + 1)) + 4;
    list = malloc(list_size);
    if (!list) {
      free(timestamps);
      goto oom;
    }
    snprintf(list, list_size, "%d %d", attempts, interval);
    char *prnt = strchr(list, '\000');
    for (int i = start; i <= stop; ++i) {
      prnt += snprintf(prnt, list_size-(prnt-list), " %u", timestamps[i]);
    }
    free(timestamps);
  }

  // Try to update RATE_LIMIT line.
  if (set_cfg_value(pamh, "RATE_LIMIT", list, buf) < 0) {
    free(list);
    return -1;
  }
  free(list);

  // Mark the state file as changed.
  *updated = 1;

  // If necessary, notify the user of the rate limiting that is in effect.
  if (exceeded) {
    log_message(LOG_ERR, pamh,
                "Too many concurrent login attempts (\"%s\"). Please try again.", secret_filename);
    return -1;
  }

  return 0;
}

static char *get_first_pass(pam_handle_t *pamh) {
  PAM_CONST void *password = NULL;
  if (pam_get_item(pamh, PAM_AUTHTOK, &password) == PAM_SUCCESS &&
      password) {
    return strdup((const char *)password);
  }
  return NULL;
}

// Show error message to the user.
static void
conv_error(pam_handle_t *pamh, const char* text) {
  PAM_CONST struct pam_message msg = {
    .msg_style = PAM_ERROR_MSG,
    .msg       = text,
  };
  PAM_CONST struct pam_message *msgs = &msg;
  struct pam_response *resp = NULL;
  const int retval = converse(pamh, 1, &msgs, &resp);
  if (retval != PAM_SUCCESS) {
    log_message(LOG_ERR, pamh, "Failed to inform user of error");
  }
  free(resp);
}

static char *request_pass(pam_handle_t *pamh, int echocode,
                          PAM_CONST char *prompt) {
  // Query user for verification code
  PAM_CONST struct pam_message msg = { .msg_style = echocode,
                                   .msg       = prompt };
  PAM_CONST struct pam_message *msgs = &msg;
  struct pam_response *resp = NULL;
  int retval = converse(pamh, 1, &msgs, &resp);
  char *ret = NULL;
  if (retval != PAM_SUCCESS || resp == NULL || resp->resp == NULL ||
      *resp->resp == '\000') {
    log_message(LOG_ERR, pamh, "Did not receive verification code from user");
    if (retval == PAM_SUCCESS && resp && resp->resp) {
      ret = resp->resp;
    }
  } else {
    ret = resp->resp;
  }

  // Deallocate temporary storage
  if (resp) {
    if (!ret) {
      free(resp->resp);
    }
    free(resp);
  }

  return ret;
}

/* Checks for possible use of scratch codes. Returns -1 on error, 0 on success,
 * and 1, if no scratch code had been entered, and subsequent tests should be
 * applied.
 */
static int check_scratch_codes(pam_handle_t *pamh,
                               const Params *params,
                               const char *secret_filename,
                               int *updated, char *buf, int code) {
  // Skip the first line. It contains the shared secret.
  char *ptr = buf + strcspn(buf, "\n");

  // Check if this is one of the scratch codes
  char *endptr = NULL;
  for (;;) {
    // Skip newlines and blank lines
    while (*ptr == '\r' || *ptr == '\n') {
      ptr++;
    }

    // Skip any lines starting with double-quotes. They contain option fields
    if (*ptr == '"') {
      ptr += strcspn(ptr, "\n");
      continue;
    }

    // Try to interpret the line as a scratch code
    errno = 0;
    const int scratchcode = (int)strtoul(ptr, &endptr, 10);

    // Sanity check that we read a valid scratch code. Scratchcodes are all
    // numeric eight-digit codes. There must not be any other information on
    // that line.
    if (errno ||
        ptr == endptr ||
        (*endptr != '\r' && *endptr != '\n' && *endptr) ||
        scratchcode  <  10*1000*1000 ||
        scratchcode >= 100*1000*1000) {
      break;
    }

    // Check if the code matches
    if (scratchcode == code) {
      // Remove scratch code after using it
      while (*endptr == '\n' || *endptr == '\r') {
        ++endptr;
      }
      memmove(ptr, endptr, strlen(endptr) + 1);
      memset(strrchr(ptr, '\000'), 0, endptr - ptr + 1);

      // Mark the state file as changed
      *updated = 1;

      // Successfully removed scratch code. Allow user to log in.
      if(params->debug) {
        log_message(LOG_INFO, pamh, "debug: scratch code %d used and removed from \"%s\"", code, secret_filename);
      }
      return 0;
    }
    ptr = endptr;
  }

  // No scratch code has been used. Continue checking other types of codes.
  if(params->debug) {
    log_message(LOG_INFO, pamh, "debug: no scratch code used from \"%s\"", secret_filename);
  }
  return 1;
}

static int window_size(pam_handle_t *pamh, const char *secret_filename,
                       const char *buf) {
  const char *value = get_cfg_value(pamh, "WINDOW_SIZE", buf);
  if (!value) {
    // Default window size is 3. This gives us one STEP_SIZE second
    // window before and after the current one.
    return 3;
  } else if (value == &oom) {
    // Out of memory. This is a fatal error.
    return 0;
  }

  char *endptr;
  errno = 0;
  const int window = (int)strtoul(value, &endptr, 10);
  if (errno || !*value || value == endptr ||
      (*endptr && *endptr != ' ' && *endptr != '\t' &&
       *endptr != '\n' && *endptr != '\r') ||
      window < 1 || window > 100) {
    free((void *)value);
    log_message(LOG_ERR, pamh, "Invalid WINDOW_SIZE option in \"%s\"",
                secret_filename);
    return 0;
  }
  free((void *)value);
  return window;
}

/* If the DISALLOW_REUSE option has been set, record timestamps have been
 * used to log in successfully and disallow their reuse.
 *
 * Returns -1 on error, and 0 on success.
 */
static int invalidate_timebased_code(int tm, pam_handle_t *pamh,
                                     const char *secret_filename,
                                     int *updated, char **buf) {
  char *disallow = get_cfg_value(pamh, "DISALLOW_REUSE", *buf);
  if (!disallow) {
    // Reuse of tokens is not explicitly disallowed. Allow the login request
    // to proceed.
    return 0;
  } else if (disallow == &oom) {
    // Out of memory. This is a fatal error.
    return -1;
  }

  // Allow the user to customize the window size parameter.
  const int window = window_size(pamh, secret_filename, *buf);
  if (!window) {
    // The user configured a non-standard window size, but there was some
    // error with the value of this parameter.
    free((void *)disallow);
    return -1;
  }

  // The DISALLOW_REUSE option is followed by all known timestamps that are
  // currently unavailable for login.
  for (char *ptr = disallow; *ptr;) {
    // Skip white-space, if any
    ptr += strspn(ptr, " \t\r\n");
    if (!*ptr) {
      break;
    }

    // Parse timestamp value.
    char *endptr;
    errno = 0;
    const int blocked = (int)strtoul(ptr, &endptr, 10);

    // Treat syntactically invalid options as an error
    if (errno ||
        ptr == endptr ||
        (*endptr != ' ' && *endptr != '\t' &&
         *endptr != '\r' && *endptr != '\n' && *endptr)) {
      free((void *)disallow);
      return -1;
    }

    if (tm == blocked) {
      // The code is currently blocked from use. Disallow login.
      free((void *)disallow);
      const int step = step_size(pamh, secret_filename, *buf);
      if (!step) {
        return -1;
      }
      log_message(LOG_ERR, pamh,
                  "Trying to reuse a previously used time-based code (\"%s\")."
                  " Retry again in %d seconds. "
                  "Warning! This might mean, you are currently subject to a "
                  "man-in-the-middle attack.", secret_filename, step);
      return -1;
    }

    // If the blocked code is outside of the possible window of timestamps,
    // remove it from the file.
    if (blocked - tm >= window || tm - blocked >= window) {
      endptr += strspn(endptr, " \t");
      memmove(ptr, endptr, strlen(endptr) + 1);
    } else {
      ptr = endptr;
    }
  }

  // Add the current timestamp to the list of disallowed timestamps.
  {
    const size_t resized_size = strlen(disallow) + 40;
    char *resized = realloc(disallow, resized_size);
    if (!resized) {
      free((void *)disallow);
      log_message(LOG_ERR, pamh,
                  "Failed to allocate memory when updating \"%s\"",
                  secret_filename);
      return -1;
    }
    disallow = resized;
    char* pos = strrchr(disallow, '\000');
    snprintf(pos, resized_size-(pos-disallow), " %d" + !*disallow, tm);
    if (set_cfg_value(pamh, "DISALLOW_REUSE", disallow, buf) < 0) {
      free((void *)disallow);
      return -1;
    }
    free((void *)disallow);
  }

  // Mark the state file as changed
  *updated = 1;

  // Allow access.
  return 0;
}

/* Given an input value, this function computes the hash code that forms the
 * expected authentication token.
 */
#ifdef TESTING
int compute_code(const uint8_t *secret, int secretLen, unsigned long value)
  __attribute__((visibility("default")));
#else
static
#endif
int compute_code(const uint8_t *secret, int secretLen, unsigned long value) {
  uint8_t val[8];
  for (int i = 8; i--; value >>= 8) {
    val[i] = value;
  }
  uint8_t hash[SHA1_DIGEST_LENGTH];
  hmac_sha1(secret, secretLen, val, 8, hash, SHA1_DIGEST_LENGTH);
  explicit_bzero(val, sizeof(val));
  const int offset = hash[SHA1_DIGEST_LENGTH - 1] & 0xF;
  unsigned int truncatedHash = 0;
  for (int i = 0; i < 4; ++i) {
    truncatedHash <<= 8;
    truncatedHash  |= hash[offset + i];
  }
  explicit_bzero(hash, sizeof(hash));
  truncatedHash &= 0x7FFFFFFF;
  truncatedHash %= 1000000;
  return truncatedHash;
}

/* If a user repeated attempts to log in with the same time skew, remember
 * this skew factor for future login attempts.
 */
static int check_time_skew(pam_handle_t *pamh,
                           int *updated, char **buf, int skew, int tm) {
  int rc = -1;

  // Parse current RESETTING_TIME_SKEW line, if any.
  char *resetting = get_cfg_value(pamh, "RESETTING_TIME_SKEW", *buf);
  if (resetting == &oom) {
    // Out of memory. This is a fatal error.
    return -1;
  }

  // If the user can produce a sequence of three consecutive codes that fall
  // within a day of the current time. And if he can enter these codes in
  // quick succession, then we allow the time skew to be reset.
  // N.B. the number "3" was picked so that it would not trigger the rate
  // limiting limit if set up with default parameters.
  unsigned int tms[3];
  int skews[sizeof(tms)/sizeof(int)];

  int num_entries = 0;
  if (resetting) {
    char *ptr = resetting;

    // Read the three most recent pairs of time stamps and skew values into
    // our arrays.
    while (*ptr && *ptr != '\r' && *ptr != '\n') {
      char *endptr;
      errno = 0;
      const unsigned int i = (int)strtoul(ptr, &endptr, 10);
      if (errno || ptr == endptr || (*endptr != '+' && *endptr != '-')) {
        break;
      }
      ptr = endptr;
      int j = (int)strtoul(ptr + 1, &endptr, 10);
      if (errno ||
          ptr == endptr ||
          (*endptr != ' ' && *endptr != '\t' &&
           *endptr != '\r' && *endptr != '\n' && *endptr)) {
        break;
      }
      if (*ptr == '-') {
        j = -j;
      }
      if (num_entries == sizeof(tms)/sizeof(int)) {
        memmove(tms, tms+1, sizeof(tms)-sizeof(int));
        memmove(skews, skews+1, sizeof(skews)-sizeof(int));
      } else {
        ++num_entries;
      }
      tms[num_entries-1]   = i;
      skews[num_entries-1] = j;
      ptr = endptr;
    }

    // If the user entered an identical code, assume they are just getting
    // desperate. This doesn't actually provide us with any useful data,
    // though. Don't change any state and hope the user keeps trying a few
    // more times.
    if (num_entries &&
        tm + skew == tms[num_entries-1] + skews[num_entries-1]) {
      free((void *)resetting);
      return -1;
    }
  }
  free((void *)resetting);

  // Append new timestamp entry
  if (num_entries == sizeof(tms)/sizeof(int)) {
    memmove(tms, tms+1, sizeof(tms)-sizeof(int));
    memmove(skews, skews+1, sizeof(skews)-sizeof(int));
  } else {
    ++num_entries;
  }
  tms[num_entries-1]   = tm;
  skews[num_entries-1] = skew;

  // Check if we have the required amount of valid entries.
  if (num_entries == sizeof(tms)/sizeof(int)) {
    unsigned int last_tm = tms[0];
    int last_skew = skews[0];
    int avg_skew = last_skew;
    for (int i = 1; i < sizeof(tms)/sizeof(int); ++i) {
      // Check that we have a consecutive sequence of timestamps with no big
      // gaps in between. Also check that the time skew stays constant. Allow
      // a minor amount of fuzziness on all parameters.
      if (tms[i] <= last_tm || tms[i] > last_tm+2 ||
          last_skew - skew < -1 || last_skew - skew > 1) {
        goto keep_trying;
      }
      last_tm   = tms[i];
      last_skew = skews[i];
      avg_skew += last_skew;
    }
    avg_skew /= (int)(sizeof(tms)/sizeof(int));

    // The user entered the required number of valid codes in quick
    // succession. Establish a new valid time skew for all future login
    // attempts.
    char time_skew[40];
    snprintf(time_skew, sizeof time_skew, "%d", avg_skew);
    if (set_cfg_value(pamh, "TIME_SKEW", time_skew, buf) < 0) {
      return -1;
    }
    rc = 0;
  keep_trying:;
  }

  // Set the new RESETTING_TIME_SKEW line, while the user is still trying
  // to reset the time skew.
  {
    const size_t reset_size = 80 * (sizeof(tms)/sizeof(int));
    char reset[reset_size];
    *reset = '\000';
    if (rc) {
      for (int i = 0; i < num_entries; ++i) {
        char* pos = strrchr(reset, '\000');
        snprintf(pos, reset_size-(pos-reset), " %d%+d" + !*reset, tms[i], skews[i]);
      }
    }
    if (set_cfg_value(pamh, "RESETTING_TIME_SKEW", reset, buf) < 0) {
      return -1;
    }
  }

  // Mark the state file as changed
  *updated = 1;

  return rc;
}

/* Checks for time based verification code. Returns -1 on error, 0 on success,
 * and 1, if no time based code had been entered, and subsequent tests should
 * be applied.
 */
static int check_timebased_code(pam_handle_t *pamh, const char*secret_filename,
                                int *updated, char **buf, const uint8_t*secret,
                                int secretLen, int code, Params *params) {
  if (!is_totp(*buf)) {
    // The secret file does not actually contain information for a time-based
    // code. Return to caller and see if any other authentication methods
    // apply.
    return 1;
  }

  if (code < 0 || code >= 1000000) {
    // All time based verification codes are no longer than six digits.
    return 1;
  }

  // Compute verification codes and compare them with user input
  const int tm = get_timestamp(pamh, secret_filename, (const char **)buf);
  if (!tm) {
    return -1;
  }
  const char *skew_str = get_cfg_value(pamh, "TIME_SKEW", *buf);
  if (skew_str == &oom) {
    // Out of memory. This is a fatal error
    return -1;
  }

  int skew = 0;
  if (skew_str) {
    skew = (int)strtol(skew_str, NULL, 10);
  }
  free((void *)skew_str);

  const int window = window_size(pamh, secret_filename, *buf);
  if (!window) {
    return -1;
  }
  for (int i = -((window-1)/2); i <= window/2; ++i) {
    const unsigned int hash = compute_code(secret, secretLen, tm + skew + i);
    if (hash == (unsigned int)code) {
      return invalidate_timebased_code(tm + skew + i, pamh, secret_filename,
                                       updated, buf);
    }
  }

  if (!params->noskewadj) {
    // The most common failure mode is for the clocks to be insufficiently
    // synchronized. We can detect this and store a skew value for future
    // use.
    skew = 1000000;
    for (int i = 0; i < 25*60; ++i) {
      unsigned int hash = compute_code(secret, secretLen, tm - i);
      if (hash == (unsigned int)code && skew == 1000000) {
        // Don't short-circuit out of the loop as the obvious difference in
        // computation time could be a signal that is valuable to an attacker.
        skew = -i;
      }
      hash = compute_code(secret, secretLen, tm + i);
      if (hash == (unsigned int)code && skew == 1000000) {
        skew = i;
      }
    }
    if (skew != 1000000) {
      if(params->debug) {
        log_message(LOG_INFO, pamh, "debug: time skew adjusted");
      }
      return check_time_skew(pamh, updated, buf, skew, tm);
    }
  }

  return 1;
}

/*
 * Add a 'config' variable that says we logged in from a particular place
 * at a particular time. Only remembers the last 10 logins, which
 * are replaced in LRU order.
 *
 * Returns 0 on success.
 */
int
update_logindetails(pam_handle_t *pamh, const Params *params, char **buf) {
  const char *rhost = get_rhost(pamh, params);
  const time_t now = get_time();
  time_t oldest = now;    // Oldest entry seen so far.
  int oldest_index = -1;  // Index of oldest entry, due for replacement.
  int found = 0;          // Entry for this rhost found.
  char name[] = "LAST ";  // Config name template.

  if (rhost == NULL) {
    return -1;
  }

  for (int i = 0; i < 10; i++) {
    //
    // Get LAST<n> cfg value.
    //
    name[4] = i + '0';
    char *line = get_cfg_value(pamh, name, *buf);
    if (line == &oom) {
      /* Fatal! */
      return -1;
    }

    if (!line) {
      /* Make first empty line the oldest */
      if (oldest) {
        oldest_index = i;
        oldest = 0;
      }
      continue;
    }

    //
    // Parse value.
    //

    // Max len of ipv6 address is 8*4 digits plus 7 colons.
    // Plus trailing NUL is 40.
    // But RHOST can be FQDN, and by RFC1035 that's 255 characters as max.
    char host[256];
    unsigned long when = 0; // Timestamp of current entry.
    const int scanf_rc = sscanf(line, " %255[0-9a-zA-Z:.-] %lu ", host, &when);
    free(line);

    if (scanf_rc != 2) {
      log_message(LOG_ERR, pamh, "Malformed LAST%d line", i);
      continue;
    }

    if (!strcmp(host, rhost)) {
      found = 1;
      break;
    }

    if (when < oldest) {
      oldest_index = i;
      oldest = when;
    }
  }

  if (!found) {
    /* Loop completed all ten iterations */
    name[4] = oldest_index + '0';
  }

  /*
   * Max length in decimal digits of a 64 bit number is (64 log 2) + 1
   * Plus space and NUL termination, is 23.
   * Max len of hostname is 255.
   */
  char value[255+23+1];
  memset(value, 0, sizeof value);

  snprintf(value, sizeof value, "%s %lu", rhost, (unsigned long)now);
  if (set_cfg_value(pamh, name, value, buf) < 0) {
    log_message(LOG_WARNING, pamh, "Failed to set cfg value for login host");
  }

  return 0;
}

/*
 * Return non-zero if the last login from the same host as this one was
 * successfully authenticated within the grace period.
 */
int
within_grace_period(pam_handle_t *pamh, const Params *params,
                    const char *buf) {
  const char *rhost = get_rhost(pamh, params);
  const time_t now = get_time();
  const time_t grace = params->grace_period;
  unsigned long when = 0;
  char match[128];

  if (rhost == NULL) {
    return 0;
  }
  snprintf(match, sizeof match, " %s %%lu ", rhost);

  for (int i = 0; i < 10; i++) {
    static char name[] = "LAST0";
    name[4] = i + '0';
    char* line = get_cfg_value(pamh, name, buf);

    if (line == &oom) {
      /* Fatal! */
      return 0;
    }
    if (!line) {
      continue;
    }
    if (sscanf(line, match, &when) == 1) {
      free(line);
      break;
    }
    free(line);
  }

  if (when == 0) {
    /* No match */
    return 0;
  }

  return (when + grace > now);
}

/* Checks for counter based verification code. Returns -1 on error, 0 on
 * success, and 1, if no counter based code had been entered, and subsequent
 * tests should be applied.
 */
static int check_counterbased_code(pam_handle_t *pamh,
                                   const char*secret_filename, int *updated,
                                   char **buf, const uint8_t*secret,
                                   int secretLen, int code,
                                   long hotp_counter,
                                   int *must_advance_counter) {
  if (hotp_counter < 1) {
    // The secret file did not actually contain information for a counter-based
    // code. Return to caller and see if any other authentication methods
    // apply.
    return 1;
  }

  if (code < 0 || code >= 1000000) {
    // All counter based verification codes are no longer than six digits.
    return 1;
  }

  // Compute [window_size] verification codes and compare them with user input.
  // Future codes are allowed in case the user computed but did not use a code.
  const int window = window_size(pamh, secret_filename, *buf);
  if (!window) {
    return -1;
  }
  for (int i = 0; i < window; ++i) {
    const unsigned int hash = compute_code(secret, secretLen, hotp_counter + i);
    if (hash == (unsigned int)code) {
      char counter_str[40];
      snprintf(counter_str, sizeof counter_str, "%ld", hotp_counter + i + 1);
      if (set_cfg_value(pamh, "HOTP_COUNTER", counter_str, buf) < 0) {
        return -1;
      }
      *updated = 1;
      *must_advance_counter = 0;
      return 0;
    }
  }

  *must_advance_counter = 1;
  return 1;
}

// parse a user name.
// input: user name
// output: uid
// return: 0 on success.
static int parse_user(pam_handle_t *pamh, const char *name, uid_t *uid) {
  char *endptr;
  errno = 0;
  const long l = strtol(name, &endptr, 10);
  if (!errno && endptr != name && l >= 0 && l <= INT_MAX) {
    *uid = (uid_t)l;
    return 0;
  }
  const size_t len = getpwnam_buf_max_size();
  char *buf = malloc(len);
  if (!buf) {
    log_message(LOG_ERR, pamh, "Out of memory");
    return -1;
  }
  struct passwd pwbuf, *pw;
  if (getpwnam_r(name, &pwbuf, buf, len, &pw) || !pw) {
    free(buf);
    log_message(LOG_ERR, pamh, "Failed to look up user \"%s\"", name);
    return -1;
  }
  *uid = pw->pw_uid;
  free(buf);
  return 0;
}

static int parse_args(pam_handle_t *pamh, int argc, const char **argv,
                      Params *params) {
  params->debug = 0;
  params->echocode = PAM_PROMPT_ECHO_OFF;
  for (int i = 0; i < argc; ++i) {
    if (!strncmp(argv[i], "secret=", 7)) {
      params->secret_filename_spec = argv[i] + 7;
    } else if (!strncmp(argv[i], "authtok_prompt=", 15)) {
      params->authtok_prompt = argv[i] + 15;
    } else if (!strncmp(argv[i], "user=", 5)) {
      uid_t uid;
      if (parse_user(pamh, argv[i] + 5, &uid) < 0) {
        return -1;
      }
      params->fixed_uid = 1;
      params->uid = uid;
    } else if (!strncmp(argv[i], "allowed_perm=", 13)) {
      char *remainder = NULL;
      const int perm = (int)strtol(argv[i] + 13, &remainder, 8);
      if (perm == 0 || strlen(remainder) != 0) {
        log_message(LOG_ERR, pamh,
                    "Invalid permissions in setting \"%s\"."
                    " allowed_perm setting must be a positive octal integer.",
                    argv[i]);
        return -1;
      }
      params->allowed_perm = perm;
    } else if (!strcmp(argv[i], "no_strict_owner")) {
      params->no_strict_owner = 1;
    } else if (!strcmp(argv[i], "debug")) {
      params->debug = 1;
    } else if (!strcmp(argv[i], "try_first_pass")) {
      params->pass_mode = TRY_FIRST_PASS;
    } else if (!strcmp(argv[i], "use_first_pass")) {
      params->pass_mode = USE_FIRST_PASS;
    } else if (!strcmp(argv[i], "forward_pass")) {
      params->forward_pass = 1;
    } else if (!strcmp(argv[i], "noskewadj")) {
      params->noskewadj = 1;
    } else if (!strcmp(argv[i], "no_increment_hotp")) {
      params->no_increment_hotp = 1;
    } else if (!strcmp(argv[i], "nullok")) {
      params->nullok = NULLOK;
    } else if (!strcmp(argv[i], "allow_readonly")) {
      params->allow_readonly = 1;
    } else if (!strcmp(argv[i], "echo-verification-code") ||
               !strcmp(argv[i], "echo_verification_code")) {
      params->echocode = PAM_PROMPT_ECHO_ON;
    } else if (!strncmp(argv[i], "grace_period=", 13)) {
      char *remainder = NULL;
      const time_t grace = (time_t)strtol(argv[i] + 13, &remainder, 10);
      if (grace < 0 || *remainder) {
        log_message(LOG_ERR, pamh,
                    "Invalid value in setting \"%s\"."
                    "grace_period must be a positive number of seconds.",
                    argv[i]);
        return -1;
      }
      params->grace_period = grace;
    } else {
      log_message(LOG_ERR, pamh, "Unrecognized option \"%s\"", argv[i]);
      return -1;
    }
  }
  return 0;
}

static int google_authenticator(pam_handle_t *pamh,
                                int argc, const char **argv) {
  int        rc = PAM_AUTH_ERR;
  uid_t      uid = -1;
  int        old_uid = -1, old_gid = -1, fd = -1;
  char       *buf = NULL;
  struct stat orig_stat = { 0 };
  uint8_t    *secret = NULL;
  int        secretLen = 0;

  // Handle optional arguments that configure our PAM module
  Params params = { 0 };
  params.allowed_perm = 0600;
  if (parse_args(pamh, argc, argv, &params) < 0) {
    return rc;
  }

  const char *prompt = params.authtok_prompt
    ? params.authtok_prompt
    : (params.forward_pass ? PWCODE_PROMPT : CODE_PROMPT);

  // Read and process status file, then ask the user for the verification code.
  int early_updated = 0, updated = 0;

  const char* const username = get_user_name(pamh, &params);
  char* const secret_filename = get_secret_filename(pamh, &params,
                                                    username, &uid);
  int stopped_by_rate_limit = 0;

  // Drop privileges.
  {
    const char* drop_username = username;

    // If user doesn't exist, use 'nobody'.
    if (uid == -1) {
      drop_username = nobody;
      if (parse_user(pamh, drop_username, &uid)) {
        // If 'nobody' doesn't exist, bail. We need to drop privs to *someone*.
        goto out;
      }
    }

    if (drop_privileges(pamh, drop_username, uid, &old_uid, &old_gid)) {
      // Don't allow to continue without dropping privs.
      goto out;
    }
  }

  if (secret_filename) {
    fd = open_secret_file(pamh, secret_filename, &params, username, uid, &orig_stat);
    if (fd >= 0) {
      buf = read_file_contents(pamh, &params, secret_filename, &fd, orig_stat.st_size);
    }

    if (buf) {
      if (rate_limit(pamh, secret_filename, &early_updated, &buf) >= 0) {
        secret = get_shared_secret(pamh, &params, secret_filename, buf, &secretLen);
      } else {
        stopped_by_rate_limit=1;
      }
    }
  }

  const long hotp_counter = get_hotp_counter(pamh, buf);

  /*
   * Check to see if a successful login from the same host happened
   * within the grace period. If it did, then allow login without
   * an additional code.
   */
  if (buf && within_grace_period(pamh, &params, buf)) {
    rc = PAM_SUCCESS;
    log_message(LOG_INFO, pamh,
                "within grace period: \"%s\"", username);
    goto out;
  }

  // Only if nullok and we do not have a code will we NOT ask for a code.
  // In all other cases (i.e "have code" and "no nullok and no code") we DO ask for a code.
  if (!stopped_by_rate_limit &&
        ( secret || params.nullok != SECRETNOTFOUND )
     ) {

    if (!secret) {
      log_message(LOG_WARNING , pamh, "No secret configured for user %s, asking for code anyway.", username);
    }

    int must_advance_counter = 0;
    char *pw = NULL, *saved_pw = NULL;
    for (int mode = 0; mode < 4; ++mode) {
      // In the case of TRY_FIRST_PASS, we don't actually know whether we
      // get the verification code from the system password or from prompting
      // the user. We need to attempt both.
      // This only works correctly, if all failed attempts leave the global
      // state unchanged.
      if (updated || pw) {
        // Oops. There is something wrong with the internal logic of our
        // code. This error should never trigger. The unittest checks for
        // this.
        if (pw) {
          explicit_bzero(pw, strlen(pw));
          free(pw);
          pw = NULL;
        }
        rc = PAM_AUTH_ERR;
        break;
      }
      switch (mode) {
      case 0: // Extract possible verification code
      case 1: // Extract possible scratch code
        if (params.pass_mode == USE_FIRST_PASS ||
            params.pass_mode == TRY_FIRST_PASS) {
          pw = get_first_pass(pamh);
        }
        break;
      default:
        if (mode != 2 && // Prompt for pw and possible verification code
            mode != 3) { // Prompt for pw and possible scratch code
          rc = PAM_AUTH_ERR;
          continue;
        }
        if (params.pass_mode == PROMPT ||
            params.pass_mode == TRY_FIRST_PASS) {
          if (!saved_pw) {
            // If forwarding the password to the next stacked PAM module,
            // we cannot tell the difference between an eight digit scratch
            // code or a two digit password immediately followed by a six
            // digit verification code. We have to loop and try both
            // options.
            saved_pw = request_pass(pamh, params.echocode, prompt);
          }
          if (saved_pw) {
            pw = strdup(saved_pw);
          }
        }
        break;
      }
      if (!pw) {
        continue;
      }

      // We are often dealing with a combined password and verification
      // code. Separate them now.
      const int pw_len = strlen(pw);
      const int expected_len = mode & 1 ? 8 : 6;
      char ch;

      // Full OpenSSH "bad password" is "\b\n\r\177INCORRECT", capped
      // to original password length.
      if (pw_len > 0 && pw[0] == '\b') {
        log_message(LOG_INFO, pamh,
                    "Dummy password supplied by PAM."
                    " Did OpenSSH 'PermitRootLogin <anything but yes>' or some"
                    " other config block this login?");
      }

      if (pw_len < expected_len ||
          // Verification are six digits starting with '0'..'9',
          // scratch codes are eight digits starting with '1'..'9'
          (ch = pw[pw_len - expected_len]) > '9' ||
          ch < (expected_len == 8 ? '1' : '0')) {
      invalid:
        explicit_bzero(pw, pw_len);
        free(pw);
        pw = NULL;
        continue;
      }
      char *endptr;
      errno = 0;
      const long l = strtol(pw + pw_len - expected_len, &endptr, 10);
      if (errno || l < 0 || *endptr) {
        goto invalid;
      }
      const int code = (int)l;
      memset(pw + pw_len - expected_len, 0, expected_len);

      if ((mode == 2 || mode == 3) && !params.forward_pass) {
        // We are explicitly configured so that we don't try to share
        // the password with any other stacked PAM module. We must
        // therefore verify that the user entered just the verification
        // code, but no password.
        if (*pw) {
          goto invalid;
        }
      }

      // Only if we actually have a secret will we try to verify the code
      // In all other cases will we just remain at PAM_AUTH_ERR
      if (secret) {
        // Check all possible types of verification codes.
        switch (check_scratch_codes(pamh, &params, secret_filename, &updated, buf, code)) {
        case 1:
          if (hotp_counter > 0) {
            switch (check_counterbased_code(pamh, secret_filename, &updated,
                                            &buf, secret, secretLen, code,
                                            hotp_counter,
                                            &must_advance_counter)) {
            case 0:
              rc = PAM_SUCCESS;
              break;
            case 1:
              goto invalid;
            default:
              break;
            }
          } else {
            switch (check_timebased_code(pamh, secret_filename, &updated, &buf,
                                         secret, secretLen, code, &params)) {
            case 0:
              rc = PAM_SUCCESS;
              break;
            case 1:
              goto invalid;
            default:
              break;
            }
          }
          break;
        case 0:
          rc = PAM_SUCCESS;
          break;
        default:
          break;
        }

        break;
      }
    }

    // Update the system password, if we were asked to forward
    // the system password. We already removed the verification
    // code from the end of the password.
    if (rc == PAM_SUCCESS && params.forward_pass) {
      if (!pw || pam_set_item(pamh, PAM_AUTHTOK, pw) != PAM_SUCCESS) {
        rc = PAM_AUTH_ERR;
      }
    }

    // Clear out password and deallocate memory
    if (pw) {
      explicit_bzero(pw,strlen(pw));
      free(pw);
    }
    if (saved_pw) {
      explicit_bzero(saved_pw, strlen(saved_pw));
      free(saved_pw);
    }

    // If an hotp login attempt has been made, the counter must always be
    // advanced by at least one, unless this has been disabled.
    if (!params.no_increment_hotp && must_advance_counter) {
      char counter_str[40];
      snprintf(counter_str, sizeof counter_str, "%ld", hotp_counter + 1);
      if (set_cfg_value(pamh, "HOTP_COUNTER", counter_str, &buf) < 0) {
        rc = PAM_AUTH_ERR;
      }
      updated = 1;
    }

    // Display a success or error message
    if (rc == PAM_SUCCESS) {
      log_message(LOG_INFO , pamh, "Accepted google_authenticator for %s", username);
      if (params.grace_period != 0) {
        updated = 1;
        if (update_logindetails(pamh, &params, &buf)) {
          log_message(LOG_ERR, pamh, "Failed to store grace_period timestamp in config");
        }
      }
    } else {
      log_message(LOG_ERR, pamh, "Invalid verification code for %s", username);
    }
  }

  // If the user has not created a state file with a shared secret, and if
  // the administrator set the "nullok" option, this PAM module completes
  // without saying success or failure, without ever prompting the user.
  // It's not a failure since "nullok" was specified, and it's not a success
  // because it must be distinguishable from "good credentials given" in
  // case the PAM config considers this module "sufficient".
  // (or more complex equivalents)
  if (params.nullok == SECRETNOTFOUND) {
    rc = PAM_IGNORE;
  }

  // Persist the new state.
  if (early_updated || updated) {
    int err;
    if ((err = write_file_contents(pamh, &params, secret_filename, &orig_stat, buf))) {
      // Inform user of error if the error is clearly a system error
      // and not an auth error.
      char s[1024];
      switch (err) {
      case EPERM:
      case ENOSPC:
      case EROFS:
      case EIO:
      case EDQUOT:
        snprintf(s, sizeof(s), "Error \"%s\" while writing config", strerror(err));
        conv_error(pamh, s);
      }

      // If allow_readonly parameter is defined than ignore write errors and
      // allow user to login.
      if (!params.allow_readonly) {
        // Could not persist new state. Deny access.
        rc = PAM_AUTH_ERR;
      }
    }
  }

out:
  if (params.debug) {
    log_message(LOG_INFO, pamh,
                "debug: end of google_authenticator for \"%s\". Result: %s",
                username, pam_strerror(pamh, rc));
  }
  if (fd >= 0) {
    close(fd);
  }
  if (old_gid >= 0) {
    if (setgroup(old_gid) >= 0 && setgroup(old_gid) == old_gid) {
      old_gid = -1;
    }
  }
  if (old_uid >= 0) {
    if (setuser(old_uid) < 0 || setuser(old_uid) != old_uid) {
      log_message(LOG_EMERG, pamh, "We switched users from %d to %d, "
                  "but can't switch back", old_uid, uid);
    }
  }
  free(secret_filename);

  // Clean up
  if (buf) {
    explicit_bzero(buf, strlen(buf));
    free(buf);
  }
  if (secret) {
    explicit_bzero(secret, secretLen);
    free(secret);
  }
  return rc;
}

#ifndef UNUSED_ATTR
# if __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
#  define UNUSED_ATTR __attribute__((__unused__))
# else
#  define UNUSED_ATTR
# endif
#endif

PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED_ATTR,
                                   int argc, const char **argv) {
  return google_authenticator(pamh, argc, argv);
}

PAM_EXTERN int
pam_sm_setcred (pam_handle_t *pamh UNUSED_ATTR,
                int flags UNUSED_ATTR,
                int argc UNUSED_ATTR,
                const char **argv UNUSED_ATTR) {
  return PAM_SUCCESS;
}

#ifdef PAM_STATIC
struct pam_module _pam_listfile_modstruct = {
  MODULE_NAME,
  pam_sm_authenticate,
  pam_sm_setcred,
  NULL,
  NULL,
  NULL,
  NULL
};
#endif
/* ---- Emacs Variables ----
 * Local Variables:
 * c-basic-offset: 2
 * indent-tabs-mode: nil
 * End:
 */
07070100000024000081A40000000000000000000000016627DD8100002C2A000000000000000000000000000000000000002C00000000google-authenticator-libpam-1.10/src/sha1.c/*
 * Copyright 2010 Google Inc.
 * Author: Markus Gutschke
 *
 * 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.
 * 
 *
 * An earlier version of this file was originally released into the public
 * domain by its authors. It has been modified to make the code compile and
 * link as part of the Google Authenticator project. These changes are
 * copyrighted by Google Inc. and released under the Apache License,
 * Version 2.0.
 *
 * The previous authors' terms are included below:
 */

/*****************************************************************************
 *
 * File:    sha1.c
 *
 * Purpose: Implementation of the SHA1 message-digest algorithm.
 *
 * NIST Secure Hash Algorithm
 *   Heavily modified by Uwe Hollerbach <uh@alumni.caltech edu>
 *   from Peter C. Gutmann's implementation as found in
 *   Applied Cryptography by Bruce Schneier
 *   Further modifications to include the "UNRAVEL" stuff, below
 *
 * This code is in the public domain
 *
 *****************************************************************************
*/
#define _BSD_SOURCE
#define _DEFAULT_SOURCE
#include <sys/types.h> // Defines BYTE_ORDER, iff _BSD_SOURCE is defined
#include <string.h>

#include "sha1.h"

#if !defined(BYTE_ORDER)
#if defined(_BIG_ENDIAN)
#define BYTE_ORDER 4321
#elif defined(_LITTLE_ENDIAN)
#define BYTE_ORDER 1234
#else
#error Need to define BYTE_ORDER
#endif
#endif

#ifndef TRUNC32
  #define TRUNC32(x)  ((x) & 0xffffffffL)
#endif

/* SHA f()-functions */
#define f1(x,y,z)    ((x & y) | (~x & z))
#define f2(x,y,z)    (x ^ y ^ z)
#define f3(x,y,z)    ((x & y) | (x & z) | (y & z))
#define f4(x,y,z)    (x ^ y ^ z)

/* SHA constants */
#define CONST1        0x5a827999L
#define CONST2        0x6ed9eba1L
#define CONST3        0x8f1bbcdcL
#define CONST4        0xca62c1d6L

/* truncate to 32 bits -- should be a null op on 32-bit machines */
#define T32(x)    ((x) & 0xffffffffL)

/* 32-bit rotate */
#define R32(x,n)    T32(((x << n) | (x >> (32 - n))))

/* the generic case, for when the overall rotation is not unraveled */
#define FG(n)    \
    T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n);    \
    E = D; D = C; C = R32(B,30); B = A; A = T

/* specific cases, for when the overall rotation is unraveled */
#define FA(n)    \
    T = T32(R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n); B = R32(B,30)

#define FB(n)    \
    E = T32(R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n); A = R32(A,30)

#define FC(n)    \
    D = T32(R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n); T = R32(T,30)

#define FD(n)    \
    C = T32(R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n); E = R32(E,30)

#define FE(n)    \
    B = T32(R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n); D = R32(D,30)

#define FT(n)    \
    A = T32(R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n); C = R32(C,30)


static void
sha1_transform(SHA1_INFO *sha1_info)
{
    int i;
    uint8_t *dp;
    uint32_t T, A, B, C, D, E, W[80], *WP;

    dp = sha1_info->data;

#undef SWAP_DONE

#if BYTE_ORDER == 1234
#define SWAP_DONE
    for (i = 0; i < 16; ++i) {
        T = *((uint32_t *) dp);
        dp += 4;
        W[i] = 
            ((T << 24) & 0xff000000) |
            ((T <<  8) & 0x00ff0000) |
            ((T >>  8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
    }
#endif

#if BYTE_ORDER == 4321
#define SWAP_DONE
    for (i = 0; i < 16; ++i) {
        T = *((uint32_t *) dp);
        dp += 4;
        W[i] = TRUNC32(T);
    }
#endif

#if BYTE_ORDER == 12345678
#define SWAP_DONE
    for (i = 0; i < 16; i += 2) {
        T = *((uint32_t *) dp);
        dp += 8;
        W[i] =  ((T << 24) & 0xff000000) | ((T <<  8) & 0x00ff0000) |
            ((T >>  8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
        T >>= 32;
        W[i+1] = ((T << 24) & 0xff000000) | ((T <<  8) & 0x00ff0000) |
            ((T >>  8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
    }
#endif

#if BYTE_ORDER == 87654321
#define SWAP_DONE
    for (i = 0; i < 16; i += 2) {
        T = *((uint32_t *) dp);
        dp += 8;
        W[i] = TRUNC32(T >> 32);
        W[i+1] = TRUNC32(T);
    }
#endif

#ifndef SWAP_DONE
#define SWAP_DONE
    for (i = 0; i < 16; ++i) {
        T = *((uint32_t *) dp);
        dp += 4;
        W[i] = TRUNC32(T);
    }
#endif /* SWAP_DONE */

    for (i = 16; i < 80; ++i) {
    W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16];
    W[i] = R32(W[i], 1);
    }
    A = sha1_info->digest[0];
    B = sha1_info->digest[1];
    C = sha1_info->digest[2];
    D = sha1_info->digest[3];
    E = sha1_info->digest[4];
    WP = W;
#ifdef UNRAVEL
    FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1);
    FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1);
    FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); FE(2); FT(2);
    FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2);
    FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); FA(3); FB(3);
    FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3);
    FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); FC(4); FD(4);
    FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4);
    sha1_info->digest[0] = T32(sha1_info->digest[0] + E);
    sha1_info->digest[1] = T32(sha1_info->digest[1] + T);
    sha1_info->digest[2] = T32(sha1_info->digest[2] + A);
    sha1_info->digest[3] = T32(sha1_info->digest[3] + B);
    sha1_info->digest[4] = T32(sha1_info->digest[4] + C);
#else /* !UNRAVEL */
#ifdef UNROLL_LOOPS
    FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1);
    FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1);
    FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2);
    FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2);
    FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3);
    FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3);
    FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4);
    FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4);
#else /* !UNROLL_LOOPS */
    for (i =  0; i < 20; ++i) { FG(1); }
    for (i = 20; i < 40; ++i) { FG(2); }
    for (i = 40; i < 60; ++i) { FG(3); }
    for (i = 60; i < 80; ++i) { FG(4); }
#endif /* !UNROLL_LOOPS */
    sha1_info->digest[0] = T32(sha1_info->digest[0] + A);
    sha1_info->digest[1] = T32(sha1_info->digest[1] + B);
    sha1_info->digest[2] = T32(sha1_info->digest[2] + C);
    sha1_info->digest[3] = T32(sha1_info->digest[3] + D);
    sha1_info->digest[4] = T32(sha1_info->digest[4] + E);
#endif /* !UNRAVEL */
}

/* initialize the SHA digest */

void
sha1_init(SHA1_INFO *sha1_info)
{
    sha1_info->digest[0] = 0x67452301L;
    sha1_info->digest[1] = 0xefcdab89L;
    sha1_info->digest[2] = 0x98badcfeL;
    sha1_info->digest[3] = 0x10325476L;
    sha1_info->digest[4] = 0xc3d2e1f0L;
    sha1_info->count_lo = 0L;
    sha1_info->count_hi = 0L;
    sha1_info->local = 0;
}

/* update the SHA digest */

void
sha1_update(SHA1_INFO *sha1_info, const uint8_t *buffer, int count)
{
    uint32_t clo;

    clo = T32(sha1_info->count_lo + ((uint32_t) count << 3));
    if (clo < sha1_info->count_lo) {
        ++sha1_info->count_hi;
    }
    sha1_info->count_lo = clo;
    sha1_info->count_hi += (uint32_t) count >> 29;
    if (sha1_info->local) {
        int i = SHA1_BLOCKSIZE - sha1_info->local;
        if (i > count) {
            i = count;
        }
        memcpy(((uint8_t *) sha1_info->data) + sha1_info->local, buffer, i);
        count -= i;
        buffer += i;
        sha1_info->local += i;
        if (sha1_info->local == SHA1_BLOCKSIZE) {
            sha1_transform(sha1_info);
        } else {
            return;
        }
    }
    while (count >= SHA1_BLOCKSIZE) {
        memcpy(sha1_info->data, buffer, SHA1_BLOCKSIZE);
        buffer += SHA1_BLOCKSIZE;
        count -= SHA1_BLOCKSIZE;
        sha1_transform(sha1_info);
    }
    memcpy(sha1_info->data, buffer, count);
    sha1_info->local = count;
}


static void
sha1_transform_and_copy(unsigned char digest[20], SHA1_INFO *sha1_info)
{
    sha1_transform(sha1_info);
    digest[ 0] = (unsigned char) ((sha1_info->digest[0] >> 24) & 0xff);
    digest[ 1] = (unsigned char) ((sha1_info->digest[0] >> 16) & 0xff);
    digest[ 2] = (unsigned char) ((sha1_info->digest[0] >>  8) & 0xff);
    digest[ 3] = (unsigned char) ((sha1_info->digest[0]      ) & 0xff);
    digest[ 4] = (unsigned char) ((sha1_info->digest[1] >> 24) & 0xff);
    digest[ 5] = (unsigned char) ((sha1_info->digest[1] >> 16) & 0xff);
    digest[ 6] = (unsigned char) ((sha1_info->digest[1] >>  8) & 0xff);
    digest[ 7] = (unsigned char) ((sha1_info->digest[1]      ) & 0xff);
    digest[ 8] = (unsigned char) ((sha1_info->digest[2] >> 24) & 0xff);
    digest[ 9] = (unsigned char) ((sha1_info->digest[2] >> 16) & 0xff);
    digest[10] = (unsigned char) ((sha1_info->digest[2] >>  8) & 0xff);
    digest[11] = (unsigned char) ((sha1_info->digest[2]      ) & 0xff);
    digest[12] = (unsigned char) ((sha1_info->digest[3] >> 24) & 0xff);
    digest[13] = (unsigned char) ((sha1_info->digest[3] >> 16) & 0xff);
    digest[14] = (unsigned char) ((sha1_info->digest[3] >>  8) & 0xff);
    digest[15] = (unsigned char) ((sha1_info->digest[3]      ) & 0xff);
    digest[16] = (unsigned char) ((sha1_info->digest[4] >> 24) & 0xff);
    digest[17] = (unsigned char) ((sha1_info->digest[4] >> 16) & 0xff);
    digest[18] = (unsigned char) ((sha1_info->digest[4] >>  8) & 0xff);
    digest[19] = (unsigned char) ((sha1_info->digest[4]      ) & 0xff);
}

/* finish computing the SHA digest */
void
sha1_final(SHA1_INFO *sha1_info, uint8_t digest[20])
{
    int count;
    uint32_t lo_bit_count, hi_bit_count;

    lo_bit_count = sha1_info->count_lo;
    hi_bit_count = sha1_info->count_hi;
    count = (int) ((lo_bit_count >> 3) & 0x3f);
    ((uint8_t *) sha1_info->data)[count++] = 0x80;
    if (count > SHA1_BLOCKSIZE - 8) {
        memset(((uint8_t *) sha1_info->data) + count, 0, SHA1_BLOCKSIZE - count);
        sha1_transform(sha1_info);
        memset((uint8_t *) sha1_info->data, 0, SHA1_BLOCKSIZE - 8);
    } else {
        memset(((uint8_t *) sha1_info->data) + count, 0,
        SHA1_BLOCKSIZE - 8 - count);
    }
    sha1_info->data[56] = (uint8_t)((hi_bit_count >> 24) & 0xff);
    sha1_info->data[57] = (uint8_t)((hi_bit_count >> 16) & 0xff);
    sha1_info->data[58] = (uint8_t)((hi_bit_count >>  8) & 0xff);
    sha1_info->data[59] = (uint8_t)((hi_bit_count >>  0) & 0xff);
    sha1_info->data[60] = (uint8_t)((lo_bit_count >> 24) & 0xff);
    sha1_info->data[61] = (uint8_t)((lo_bit_count >> 16) & 0xff);
    sha1_info->data[62] = (uint8_t)((lo_bit_count >>  8) & 0xff);
    sha1_info->data[63] = (uint8_t)((lo_bit_count >>  0) & 0xff);
    sha1_transform_and_copy(digest, sha1_info);
}

/***EOF***/
07070100000025000081A40000000000000000000000016627DD81000004A5000000000000000000000000000000000000002C00000000google-authenticator-libpam-1.10/src/sha1.h// SHA1 header file
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
//
// 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.

#ifndef SHA1_H__
#define SHA1_H__

#include <stdint.h>

#define SHA1_BLOCKSIZE     64
#define SHA1_DIGEST_LENGTH 20

typedef struct {
  uint32_t digest[8];
  uint32_t count_lo, count_hi;
  uint8_t  data[SHA1_BLOCKSIZE];
  int      local;
} SHA1_INFO;

void sha1_init(SHA1_INFO *sha1_info) __attribute__((visibility("hidden")));
void sha1_update(SHA1_INFO *sha1_info, const uint8_t *buffer, int count)
  __attribute__((visibility("hidden")));
void sha1_final(SHA1_INFO *sha1_info, uint8_t digest[20])
  __attribute__((visibility("hidden")));

#endif
07070100000026000081A40000000000000000000000016627DD8100000594000000000000000000000000000000000000002C00000000google-authenticator-libpam-1.10/src/util.c/*
 * Copyright 2017 Google Inc.
 *
 * 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.
 *
 *
 * An earlier version of this file was originally released into the public
 * domain by its authors. It has been modified to make the code compile and
 * link as part of the Google Authenticator project. These changes are
 * copyrighted by Google Inc. and released under the Apache License,
 * Version 2.0.
 *
 * The previous authors' terms are included below:
 */

/*****************************************************************************
 *
 * File:    util.c
 *
 * Purpose: Collection of cross file utility functions.
 *
 * This code is in the public domain
 *
 *****************************************************************************
 */

#include "config.h"

#include <string.h>

#ifndef HAVE_EXPLICIT_BZERO
void
explicit_bzero(void *s, size_t len)
{
  memset(s, '\0', len);
  asm volatile ("":::"memory");
}
#endif
07070100000027000081A40000000000000000000000016627DD81000002C7000000000000000000000000000000000000002C00000000google-authenticator-libpam-1.10/src/util.h// util header file
//
// Copyright 2017 Google Inc.
//
// 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.

#include "config.h"

#ifndef HAVE_EXPLICIT_BZERO
void explicit_bzero(void *s, size_t len);
#endif
07070100000028000041ED0000000000000000000000026627DD8100000000000000000000000000000000000000000000002700000000google-authenticator-libpam-1.10/tests07070100000029000081ED0000000000000000000000016627DD8100000119000000000000000000000000000000000000003600000000google-authenticator-libpam-1.10/tests/base32_test.sh#!/bin/bash

a=0
while [ $a -lt 150 ] ;do
	dd if=/dev/urandom bs=$RANDOM count=1 of=testfile > /dev/null 2>&1
	cat testfile | ./base32 -e | ./base32 -d > testfile.out
	if ! cmp -s testfile testfile.out ; then
		echo FAILED
		exit 1
	fi
	a=$((a + 1))
done

rm testfile testfile.out
0707010000002A000081A40000000000000000000000016627DD8100005C94000000000000000000000000000000000000004B00000000google-authenticator-libpam-1.10/tests/pam_google_authenticator_unittest.c// Unittest for the PAM module. This is part of the Google Authenticator
// project.
//
// Copyright 2010 Google Inc.
// Author: Markus Gutschke
//
// 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.
#include "config.h"

#include <assert.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "../src/base32.h"
#include "../src/hmac.h"

#if !defined(PAM_BAD_ITEM)
// FreeBSD does not know about PAM_BAD_ITEM. And PAM_SYMBOL_ERR is an "enum",
// we can't test for it at compile-time.
#define PAM_BAD_ITEM PAM_SYMBOL_ERR
#endif

static PAM_CONST char pw[] = "0123456789";
static char *response = "";
static void *pam_module;
static enum { TWO_PROMPTS, COMBINED_PASSWORD, COMBINED_PROMPT } conv_mode;
static int num_prompts_shown = 0;

static int conversation(int num_msg, PAM_CONST struct pam_message **msg,
                        struct pam_response **resp, void *appdata_ptr) {
  // Keep track of how often the conversation callback is executed.
  ++num_prompts_shown;
  if (conv_mode == COMBINED_PASSWORD) {
    return PAM_CONV_ERR;
  }
  if (num_msg == 1 && msg[0]->msg_style == PAM_PROMPT_ECHO_OFF) {
    *resp = malloc(sizeof(struct pam_response));
    assert(*resp);
    (*resp)->resp = conv_mode == TWO_PROMPTS
      ? strdup(response)
      : strcat(strcpy(malloc(sizeof(pw) + strlen(response)), pw), response);
    (*resp)->resp_retcode = 0;
    return PAM_SUCCESS;
  }
  return PAM_CONV_ERR;
}

int pam_get_user(pam_handle_t *pamh, PAM_CONST char **user,
                 PAM_CONST char *prompt)
  __attribute__((visibility("default")));
int pam_get_user(pam_handle_t *pamh, PAM_CONST char **user,
                 PAM_CONST char *prompt) {
  return pam_get_item(pamh, PAM_USER, (void *)user);
}

int pam_get_item(const pam_handle_t *pamh, int item_type,
                 PAM_CONST void **item)
  __attribute__((visibility("default")));
int pam_get_item(const pam_handle_t *pamh, int item_type,
                 PAM_CONST void **item) {
  switch (item_type) {
    case PAM_SERVICE: {
      static const char *service = "google_authenticator_unittest";
      *item = service;
      return PAM_SUCCESS;
    }
    case PAM_USER: {
      char *user = getenv("USER");
      *item = user;
      return PAM_SUCCESS;
    }
    case PAM_CONV: {
      static struct pam_conv conv = { .conv = conversation }, *p_conv = &conv;
      *item = p_conv;
      return PAM_SUCCESS;
    }
    case PAM_RHOST: {
        static const char *rhost = "::1";
        *item = rhost;
        return PAM_SUCCESS;
    }
    case PAM_AUTHTOK: {
      static char *authtok = NULL;
      if (conv_mode == COMBINED_PASSWORD) {
        authtok = realloc(authtok, sizeof(pw) + strlen(response));
        *item = strcat(strcpy(authtok, pw), response);
      } else {
        *item = pw;
      }
      return PAM_SUCCESS;
    }
    default:
      return PAM_BAD_ITEM;
  }
}

int pam_set_item(pam_handle_t *pamh, int item_type,
                 const void *item)
  __attribute__((visibility("default")));
int pam_set_item(pam_handle_t *pamh, int item_type,
                 const void *item) {
  switch (item_type) {
    case PAM_AUTHTOK:
      if (strcmp((char *)item, pw)) {
        return PAM_BAD_ITEM;
      }
      return PAM_SUCCESS;
    default:
      return PAM_BAD_ITEM;
  }
}

// Return the last line of the error message.
static const char *get_error_msg(void) {
  const char *(*get_error_msg)(void) =
    (const char *(*)(void))dlsym(pam_module, "get_error_msg");
  const char* msg = get_error_msg ? get_error_msg() : "";
  const char* p = strrchr(msg, '\n');
  if (p) {
    msg = p+1;
  }
  return msg;
}

static void print_diagnostics(int signo) {
  if (*get_error_msg()) {
    fprintf(stderr, "%s\n", get_error_msg());
  }
  _exit(1);
}

#define verify_prompts_shown(expected_prompts_shown) do { \
  assert(num_prompts_shown == (expected_prompts_shown)); \
  num_prompts_shown = 0; /* Reset for the next count. */ \
} while(0)

int main(int argc, char *argv[]) {
  // Testing Base32 encoding
  puts("Testing base32 encoding");
  static const uint8_t dat[] = "Hello world...";
  uint8_t enc[((sizeof(dat) + 4)/5)*8 + 1];
  assert(base32_encode(dat, sizeof(dat), enc, sizeof(enc)) == sizeof(enc)-1);
  assert(!strcmp((char *)enc, "JBSWY3DPEB3W64TMMQXC4LQA"));
 
  puts("Testing base32 decoding");
  uint8_t dec[sizeof(dat)];
  assert(base32_decode(enc, dec, sizeof(dec)) == sizeof(dec));
  assert(!memcmp(dat, dec, sizeof(dat)));

  // Testing HMAC_SHA1
  puts("Testing HMAC_SHA1");
  uint8_t hmac[20];
  hmac_sha1((uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C"
                       "\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19"
                       "\x1A\x1B\x1C\x1D\x1E\x1F !\"#$%&'()*+,-./0123456789:"
                       ";<=>?", 64,
            (uint8_t *)"Sample #1", 9,
            hmac, sizeof(hmac));
  assert(!memcmp(hmac,
                 (uint8_t []) { 0x4F, 0x4C, 0xA3, 0xD5, 0xD6, 0x8B, 0xA7, 0xCC,
                                0x0A, 0x12, 0x08, 0xC9, 0xC6, 0x1E, 0x9C, 0x5D,
                                0xA0, 0x40, 0x3C, 0x0A },
                 sizeof(hmac)));
  hmac_sha1((uint8_t *)"0123456789:;<=>?@ABC", 20,
            (uint8_t *)"Sample #2", 9,
            hmac, sizeof(hmac));
  assert(!memcmp(hmac,
                 (uint8_t []) { 0x09, 0x22, 0xD3, 0x40, 0x5F, 0xAA, 0x3D, 0x19,
                                0x4F, 0x82, 0xA4, 0x58, 0x30, 0x73, 0x7D, 0x5C,
                                0xC6, 0xC7, 0x5D, 0x24 },
                 sizeof(hmac)));
  hmac_sha1((uint8_t *)"PQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
                       "\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A"
                       "\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96"
                       "\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2"
                       "\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE"
                       "\xAF\xB0\xB1\xB2\xB3", 100,
            (uint8_t *)"Sample #3", 9,
            hmac, sizeof(hmac));
  assert(!memcmp(hmac,
                 (uint8_t []) { 0xBC, 0xF4, 0x1E, 0xAB, 0x8B, 0xB2, 0xD8, 0x02,
                                0xF3, 0xD0, 0x5C, 0xAF, 0x7C, 0xB0, 0x92, 0xEC,
                                0xF8, 0xD1, 0xA3, 0xAA },
                 sizeof(hmac)));
  hmac_sha1((uint8_t *)"pqrstuvwxyz{|}~\x7F\x80\x81\x82\x83\x84\x85\x86\x87"
                       "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94"
                       "\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0", 49,
            (uint8_t *)"Sample #4", 9,
            hmac, sizeof(hmac));
  assert(!memcmp(hmac,
                 (uint8_t []) { 0x9E, 0xA8, 0x86, 0xEF, 0xE2, 0x68, 0xDB, 0xEC,
                                0xCE, 0x42, 0x0C, 0x75, 0x24, 0xDF, 0x32, 0xE0,
                                0x75, 0x1A, 0x2A, 0x26 },
                 sizeof(hmac)));

  // Load the PAM module
  puts("Loading PAM module");
  pam_module = dlopen("./.libs/libpam_google_authenticator_testing.so",
                      RTLD_NOW | RTLD_GLOBAL);
  if (pam_module == NULL) {
    fprintf(stderr, "dlopen(): %s\n", dlerror());
    exit(1);
  }
  signal(SIGABRT, print_diagnostics);

  // Look up public symbols
  int (*pam_sm_authenticate)(pam_handle_t *, int, int, const char **) =
      (int (*)(pam_handle_t *, int, int, const char **))
      dlsym(pam_module, "pam_sm_authenticate");
  assert(pam_sm_authenticate != NULL);

  // Look up private test-only API
  void (*set_time)(time_t t) =
      (void (*)(time_t))dlsym(pam_module, "set_time");
  assert(set_time);
  int (*compute_code)(uint8_t *, int, unsigned long) =
      (int (*)(uint8_t*, int, unsigned long))dlsym(pam_module, "compute_code");
  assert(compute_code);

  for (int otp_mode = 0; otp_mode < 8; ++otp_mode) {
    // Create a secret file with a well-known test vector
    char fn[] = "/tmp/.google_authenticator_XXXXXX";
    mode_t orig_umask = umask(S_IRWXG|S_IRWXO); // Only for the current user.
    int fd = mkstemp(fn);
    (void)umask(orig_umask);
    assert(fd >= 0);
    static const uint8_t secret[] = "2SH3V3GDW7ZNMGYE";
    assert(write(fd, secret, sizeof(secret)-1) == sizeof(secret)-1);
    assert(write(fd, "\n\" TOTP_AUTH", 12) == 12);
    close(fd);
    uint8_t binary_secret[sizeof(secret)];
    size_t binary_secret_len = base32_decode(secret, binary_secret,
                                             sizeof(binary_secret));

    // Set up test argc/argv parameters to let the PAM module know where to
    // find our secret file
    const char *targv[] = { malloc(strlen(fn) + 8), NULL, NULL, NULL, NULL };
    strcat(strcpy((char *)targv[0], "secret="), fn);
    int targc;
    int expected_good_prompts_shown;
    int expected_bad_prompts_shown;
    int password_is_provided_from_external;

    switch (otp_mode) {
    case 0:
      puts("\nRunning tests, querying for verification code");
      conv_mode = TWO_PROMPTS;
      targc = 1;
      expected_good_prompts_shown = expected_bad_prompts_shown = 1;
      password_is_provided_from_external = 0;
      break;
    case 1:
      puts("\nRunning tests, querying for verification code, "
           "forwarding system pass");
      conv_mode = COMBINED_PROMPT;
      targv[1] = strdup("forward_pass");
      targc = 2;
      expected_good_prompts_shown = expected_bad_prompts_shown = 1;
      password_is_provided_from_external = 0;
      break;
    case 2:
      puts("\nRunning tests with use_first_pass");
      conv_mode = COMBINED_PASSWORD;
      targv[1] = strdup("use_first_pass");
      targc = 2;
      expected_good_prompts_shown = expected_bad_prompts_shown = 0;
      password_is_provided_from_external = 1;
      break;
    case 3:
      puts("\nRunning tests with use_first_pass, forwarding system pass");
      conv_mode = COMBINED_PASSWORD;
      targv[1] = strdup("use_first_pass");
      targv[2] = strdup("forward_pass");
      targc = 3;
      expected_good_prompts_shown = expected_bad_prompts_shown = 0;
      password_is_provided_from_external = 1;
      break;
    case 4:
      puts("\nRunning tests with try_first_pass, combining codes");
      conv_mode = COMBINED_PASSWORD;
      targv[1] = strdup("try_first_pass");
      targc = 2;
      expected_good_prompts_shown = 0;
      expected_bad_prompts_shown = 2;
      password_is_provided_from_external = 1;
      break;
    case 5:
      puts("\nRunning tests with try_first_pass, combining codes, "
           "forwarding system pass");
      conv_mode = COMBINED_PASSWORD;
      targv[1] = strdup("try_first_pass");
      targv[2] = strdup("forward_pass");
      targc = 3;
      expected_good_prompts_shown = 0;
      expected_bad_prompts_shown = 2;
      password_is_provided_from_external = 1;
      break;
    case 6:
      puts("\nRunning tests with try_first_pass, querying for codes");
      conv_mode = TWO_PROMPTS;
      targv[1] = strdup("try_first_pass");
      targc = 2;
      expected_good_prompts_shown = expected_bad_prompts_shown = 1;
      password_is_provided_from_external = 1;
      break;
    default:
      assert(otp_mode == 7);
      puts("\nRunning tests with try_first_pass, querying for codes, "
           "forwarding system pass");
      conv_mode = COMBINED_PROMPT;
      targv[1] = strdup("try_first_pass");
      targv[2] = strdup("forward_pass");
      targc = 3;
      expected_good_prompts_shown = expected_bad_prompts_shown = 1;
      password_is_provided_from_external = 1;
      break;
    }

    // Make sure num_prompts_shown is still 0.
    verify_prompts_shown(0);

    // Set the timestamp that this test vector needs
    set_time(10000*30);

    response = "123456";

    // Check if we can log in when using an invalid verification code
    puts("Testing failed login attempt");
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
    verify_prompts_shown(expected_bad_prompts_shown);

    // Check required number of digits
    if (conv_mode == TWO_PROMPTS) {
      puts("Testing required number of digits");
      response = "50548";
      assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
      verify_prompts_shown(expected_bad_prompts_shown);
      response = "0050548";
      assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
      verify_prompts_shown(expected_bad_prompts_shown);
      response = "00050548";
      assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
      verify_prompts_shown(expected_bad_prompts_shown);
    }

    // Test a blank response
    puts("Testing a blank response");
    response = "";
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
    verify_prompts_shown(expected_bad_prompts_shown);

    // Set the response that we should send back to the authentication module
    response = "050548";

    // Test handling of missing state files
    puts("Test handling of missing state files");
    const char *old_secret = targv[0];
    targv[0] = "secret=/NOSUCHFILE";
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
    verify_prompts_shown(password_is_provided_from_external ? 0 : expected_bad_prompts_shown);
    targv[targc++] = "nullok";
    targv[targc] = NULL;
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_IGNORE);
    verify_prompts_shown(0);
    targv[--targc] = NULL;
    targv[0] = old_secret;

    // Check if we can log in when using a valid verification code
    puts("Testing successful login");
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_SUCCESS);
    verify_prompts_shown(expected_good_prompts_shown);

    // Test the STEP_SIZE option
    puts("Testing STEP_SIZE option");
    assert(!chmod(fn, 0600));
    assert((fd = open(fn, O_APPEND | O_WRONLY)) >= 0);
    assert(write(fd, "\n\" STEP_SIZE 60\n", 16) == 16);
    close(fd);
    for (int *tm  = (int []){ 9998, 9999, 10001, 10002, 10000, -1 },
             *res = (int []){ PAM_AUTH_ERR, PAM_SUCCESS, PAM_SUCCESS,
                              PAM_AUTH_ERR, PAM_SUCCESS };
         *tm >= 0;) {
      set_time(*tm++ * 60);
      assert(pam_sm_authenticate(NULL, 0, targc, targv) == *res++);
      verify_prompts_shown(expected_good_prompts_shown);
    }

    // Reset secret file after step size testing.
    assert(!chmod(fn, 0600));
    assert((fd = open(fn, O_TRUNC | O_WRONLY)) >= 0);
    assert(write(fd, secret, sizeof(secret)-1) == sizeof(secret)-1);
    assert(write(fd, "\n\" TOTP_AUTH", 12) == 12);
    close(fd);

    // Test the WINDOW_SIZE option
    puts("Testing WINDOW_SIZE option");
    for (int *tm  = (int []){ 9998, 9999, 10001, 10002, 10000, -1 },
             *res = (int []){ PAM_AUTH_ERR, PAM_SUCCESS, PAM_SUCCESS,
                              PAM_AUTH_ERR, PAM_SUCCESS };
         *tm >= 0;) {
      set_time(*tm++ * 30);
      assert(pam_sm_authenticate(NULL, 0, targc, targv) == *res++);
      verify_prompts_shown(expected_good_prompts_shown);
    }
    assert(!chmod(fn, 0600));
    assert((fd = open(fn, O_APPEND | O_WRONLY)) >= 0);
    assert(write(fd, "\n\" WINDOW_SIZE 6\n", 17) == 17);
    close(fd);
    for (int *tm  = (int []){ 9996, 9997, 10002, 10003, 10000, -1 },
             *res = (int []){ PAM_AUTH_ERR, PAM_SUCCESS, PAM_SUCCESS,
                              PAM_AUTH_ERR, PAM_SUCCESS };
         *tm >= 0;) {
      set_time(*tm++ * 30);
      assert(pam_sm_authenticate(NULL, 0, targc, targv) == *res++);
      verify_prompts_shown(expected_good_prompts_shown);
    }

    // Test the DISALLOW_REUSE option
    puts("Testing DISALLOW_REUSE option");
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_SUCCESS);
    verify_prompts_shown(expected_good_prompts_shown);
    assert(!chmod(fn, 0600));
    assert((fd = open(fn, O_APPEND | O_WRONLY)) >= 0);
    assert(write(fd, "\" DISALLOW_REUSE\n", 17) == 17);
    close(fd);
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_SUCCESS);
    verify_prompts_shown(expected_good_prompts_shown);
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
    verify_prompts_shown(expected_good_prompts_shown);

    // Test that DISALLOW_REUSE expires old entries from the re-use list
    char *old_response = response;
    for (int i = 10001; i < 10008; ++i) {
      set_time(i * 30);
      char buf[7];
      response = buf;
      sprintf(response, "%06d", compute_code(binary_secret,
                                             binary_secret_len, i));
      assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_SUCCESS);
      verify_prompts_shown(expected_good_prompts_shown);
    }
    set_time(10000 * 30);
    response = old_response;
    assert((fd = open(fn, O_RDONLY)) >= 0);
    char state_file_buf[4096] = { 0 };
    assert(read(fd, state_file_buf, sizeof(state_file_buf)-1) > 0);
    close(fd);
    const char *disallow = strstr(state_file_buf, "\" DISALLOW_REUSE ");
    assert(disallow);
    assert(!memcmp(disallow + 17,
                   "10002 10003 10004 10005 10006 10007\n", 36));

    // Test the RATE_LIMIT option
    puts("Testing RATE_LIMIT option");
    assert(!chmod(fn, 0600));
    assert((fd = open(fn, O_APPEND | O_WRONLY)) >= 0);
    assert(write(fd, "\" RATE_LIMIT 4 120\n", 19) == 19);
    close(fd);
    for (int *tm  = (int []){ 20000, 20001, 20002, 20003, 20004, 20006, -1 },
             *res = (int []){ PAM_SUCCESS, PAM_SUCCESS, PAM_SUCCESS,
                              PAM_SUCCESS, PAM_AUTH_ERR, PAM_SUCCESS, -1 };
         *tm >= 0;) {
      set_time(*tm * 30);
      char buf[7];
      response = buf;
      sprintf(response, "%06d",
              compute_code(binary_secret, binary_secret_len, *tm++));
      assert(pam_sm_authenticate(NULL, 0, targc, targv) == *res);
      verify_prompts_shown(
          *res != PAM_SUCCESS ? 0 : expected_good_prompts_shown);
      ++res;
    }
    set_time(10000 * 30);
    response = old_response;
    assert(!chmod(fn, 0600));
    assert((fd = open(fn, O_RDWR)) >= 0);
    memset(state_file_buf, 0, sizeof(state_file_buf));
    assert(read(fd, state_file_buf, sizeof(state_file_buf)-1) > 0);
    const char *rate_limit = strstr(state_file_buf, "\" RATE_LIMIT ");
    assert(rate_limit);
    assert(!memcmp(rate_limit + 13,
                   "4 120 600060 600090 600120 600180\n", 35));

    // Test trailing space in RATE_LIMIT. This is considered a file format
    // error.
    char *eol = strchr(rate_limit, '\n');
    *eol = ' ';
    assert(!lseek(fd, 0, SEEK_SET));
    assert(write(fd, state_file_buf, strlen(state_file_buf)) ==
           strlen(state_file_buf));
    close(fd);
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
    verify_prompts_shown(0);
    assert(!strncmp(get_error_msg(),
                    "Invalid list of timestamps in RATE_LIMIT", 40));
    *eol = '\n';
    assert(!chmod(fn, 0600));
    assert((fd = open(fn, O_WRONLY)) >= 0);
    assert(write(fd, state_file_buf, strlen(state_file_buf)) ==
           strlen(state_file_buf));
    close(fd);

    // Test TIME_SKEW option
    puts("Testing TIME_SKEW");
    for (int i = 0; i < 4; ++i) {
      set_time((12000 + i)*30);
      char buf[7];
      response = buf;
      sprintf(response, "%06d",
              compute_code(binary_secret, binary_secret_len, 11000 + i));
      assert(pam_sm_authenticate(NULL, 0, targc, targv) ==
             (i >= 2 ? PAM_SUCCESS : PAM_AUTH_ERR));
      verify_prompts_shown(expected_good_prompts_shown);
    }

    puts("Testing TIME_SKEW - noskewadj");
    set_time(12020 * 30);
    char buf[7];
    response = buf;
    sprintf(response, "%06d", compute_code(binary_secret,
                                           binary_secret_len, 11010));
    targv[targc] = "noskewadj";
    assert(pam_sm_authenticate(NULL, 0, targc+1, targv) == PAM_AUTH_ERR);
    targv[targc] = NULL;
    verify_prompts_shown(expected_bad_prompts_shown);
    set_time(10000*30);

    // Test scratch codes
    puts("Testing scratch codes");
    response = "12345678";
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
    verify_prompts_shown(expected_bad_prompts_shown);
    assert(!chmod(fn, 0600));
    assert((fd = open(fn, O_APPEND | O_WRONLY)) >= 0);
    assert(write(fd, "12345678\n", 9) == 9);
    close(fd);
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_SUCCESS);
    verify_prompts_shown(expected_good_prompts_shown);
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
    verify_prompts_shown(expected_bad_prompts_shown);

    // Set up secret file for counter-based codes.
    assert(!chmod(fn, 0600));
    assert((fd = open(fn, O_TRUNC | O_WRONLY)) >= 0);
    assert(write(fd, secret, sizeof(secret)-1) == sizeof(secret)-1);
    assert(write(fd, "\n\" HOTP_COUNTER 1\n", 18) == 18);
    close(fd);

    response = "293240";

    // Check if we can log in when using a valid verification code
    puts("Testing successful counter-based login");
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_SUCCESS);
    verify_prompts_shown(expected_good_prompts_shown);

    // Verify that the hotp counter incremented
    assert((fd = open(fn, O_RDONLY)) >= 0);
    memset(state_file_buf, 0, sizeof(state_file_buf));
    assert(read(fd, state_file_buf, sizeof(state_file_buf)-1) > 0);
    close(fd);
    const char *hotp_counter = strstr(state_file_buf, "\" HOTP_COUNTER ");
    assert(hotp_counter);
    assert(!memcmp(hotp_counter + 15, "2\n", 2));

    // Check if we can log in when using an invalid verification code
    // (including the same code a second time)
    puts("Testing failed counter-based login attempt");
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_AUTH_ERR);
    verify_prompts_shown(expected_bad_prompts_shown);

    // Verify that the hotp counter incremented
    assert((fd = open(fn, O_RDONLY)) >= 0);
    memset(state_file_buf, 0, sizeof(state_file_buf));
    assert(read(fd, state_file_buf, sizeof(state_file_buf)-1) > 0);
    close(fd);
    hotp_counter = strstr(state_file_buf, "\" HOTP_COUNTER ");
    assert(hotp_counter);
    assert(!memcmp(hotp_counter + 15, "3\n", 2));

    response = "932068";

    // Check if we can log in using a future valid verification code (using
    // default window_size of 3)
    puts("Testing successful future counter-based login");
    assert(pam_sm_authenticate(NULL, 0, targc, targv) == PAM_SUCCESS);
    verify_prompts_shown(expected_good_prompts_shown);

    // Verify that the hotp counter incremented
    assert((fd = open(fn, O_RDONLY)) >= 0);
    memset(state_file_buf, 0, sizeof(state_file_buf));
    assert(read(fd, state_file_buf, sizeof(state_file_buf)-1) > 0);
    close(fd);
    hotp_counter = strstr(state_file_buf, "\" HOTP_COUNTER ");
    assert(hotp_counter);
    assert(!memcmp(hotp_counter + 15, "6\n", 2));

    // Remove the temporarily created secret file
    unlink(fn);

    // Release memory for the test arguments
    for (int i = 0; i < targc; ++i) {
      free((void *)targv[i]);
    }
  }

  // Unload the PAM module
  dlclose(pam_module);

  puts("DONE");
  return 0;
}
0707010000002B000081A40000000000000000000000016627DD81000024CF000000000000000000000000000000000000002B00000000google-authenticator-libpam-1.10/totp.html<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!-- TOTP Debugger
  --
  -- Copyright 2011 Google Inc.
  -- Author: Markus Gutschke
  --
  -- 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.
-->
<head>
  <link rel="shortcut icon" href="http://code.google.com/p/google-authenticator/logo" type="image/png">
  <title>TOTP Debugger</title>
  <script src="https://utc-time.appspot.com"></script>
    <!-- Returns a line of the form:
      --   var timeskew = new Date().getTime() - XXX.XX;
      -->

  <script> <!--
// Given a secret key "K" and a timestamp "t" (in 30s units since the
// beginning of the epoch), return a TOTP code.
function totp(K,t) {
  function sha1(C){
    function L(x,b){return x<<b|x>>>32-b;}
    var l=C.length,D=C.concat([1<<31]),V=0x67452301,W=0x88888888,
        Y=271733878,X=Y^W,Z=0xC3D2E1F0;W^=V;
    do D.push(0);while(D.length+1&15);D.push(32*l);
    while (D.length){
      var E=D.splice(0,16),a=V,b=W,c=X,d=Y,e=Z,f,k,i=12;
      function I(x){var t=L(a,5)+f+e+k+E[x];e=d;d=c;c=L(b,30);b=a;a=t;}
      for(;++i<77;)E.push(L(E[i]^E[i-5]^E[i-11]^E[i-13],1));
      k=0x5A827999;for(i=0;i<20;I(i++))f=b&c|~b&d;
      k=0x6ED9EBA1;for(;i<40;I(i++))f=b^c^d;
      k=0x8F1BBCDC;for(;i<60;I(i++))f=b&c|b&d|c&d;
      k=0xCA62C1D6;for(;i<80;I(i++))f=b^c^d;
      V+=a;W+=b;X+=c;Y+=d;Z+=e;}
    return[V,W,X,Y,Z];
  }
  var k=[],l=[],i=0,j=0,c=0;
  for (;i<K.length;){
    c=c*32+'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.
      indexOf(K.charAt(i++).toUpperCase());
    if((j+=5)>31)k.push(Math.floor(c/(1<<(j-=32)))),c&=31;}
  j&&k.push(c<<(32-j));
  for(i=0;i<16;++i)l.push(0x6A6A6A6A^(k[i]=k[i]^0x5C5C5C5C));
  var s=sha1(k.concat(sha1(l.concat([0,t])))),o=s[4]&0xF;
  return ((s[o>>2]<<8*(o&3)|(o&3?s[(o>>2)+1]>>>8*(4-o&3):0))&-1>>>1)%1000000;
}

// Periodically check whether we need to update the UI. It would be a little
// more efficient to only call this function as a direct result of
// significant state changes. But polling is cheap, and keeps the code a
// little easier.
var lastsecret,lastlabel,lastepochseconds,lastoverrideepoch;
var lasttimestamp,lastoverride,lastsearch;
function refresh() {
  // Compute current TOTP code
  var k=document.getElementById('secret').value.
    replace(/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ234567]/gi, '');
  var d=document.getElementById('overrideepoch').value.replace(/[^0-9]/g, '');
  if (d) e=parseInt(d); else e=Math.floor(new Date().getTime()/1000);
  var t=Math.floor(e/30);
  var s=document.getElementById('override').value.replace(/[^0-9]/g, '');
  if (s) { t=parseInt(s); e=30*t; }
  var label=escape(document.getElementById('label').value);
  var search=document.getElementById('search').value;

  // If TOTP code has changed (either because of user edits, or
  // because of elapsed time), update the user interface.
  if (k != lastsecret || label != lastlabel || e != lastepochseconds ||
      d != lastoverrideepoch || t != lasttimestamp || s != lastoverride ||
      search != lastsearch) {
    if (d != lastoverrideepoch) {
      document.getElementById('override').value = '';
      s = '';
    } else if (s != lastoverride) {
      document.getElementById('overrideepoch').value = '';
      d = '';
    }
    lastsecret=k;
    lastlabel=label;
    lastepochseconds=e;
    lastoverrideepoch=d;
    lasttimestamp=t;
    lastoverride=s;
    lastsearch=search;

    var code=totp(k,t);

    // Compute the OTPAuth URL and the associated QR code
    var h='https://www.google.com/chart?chs=200x200&chld=M|0&'+
      'cht=qr&chl=otpauth://totp/'+encodeURI(label)+'%3Fsecret%3D'+k;
    var a=document.getElementById('authurl')
    a.innerHTML='otpauth://totp/'+label+'?secret='+k;
    a.href=h;
    document.getElementById('aqr').href=h;
    var q=document.getElementById('qr');
    q.src=h;
    q.alt=label+' '+k;
    q.title=label+' '+k;

    // Show the current time in seconds and in 30s increments since midnight
    // Jan 1st, 1970. Optionally, let the user override this timestamp.
    document.getElementById('epoch').innerHTML=e;
    document.getElementById('ts').innerHTML=t;

    // Show the current TOTP code.
    document.getElementById('totp').innerHTML=code;

    // If the user manually entered a TOTP code, try to find a matching code
    // within a 25h window.
    var result='';
    if (search && !!(search=parseInt(search))) {
      for (var i=0; i < 25*120; ++i) {
        if (search == totp(k, t+(i&1?-Math.floor(i/2):Math.floor(i/2)))) {
          if (i<2) {
            result='&nbsp;';
            break;
          }
          if (i >= 120) {
            result=result + Math.floor(i/120) + 'h ';
            i%=120;
          }
          if (i >= 4) {
            result=result + Math.floor(i/4) + 'min ';
            i%=4;
          }
          if (i&2) {
            result=result + '30s ';
          }
          if (i&1) {
            result='Code was valid ' + result + 'ago';
          } else {
            result='Code will be valid in ' + result;
          }
          break;
        }
      }
      if (!result) {
        result='No such code within a &#177;12h window';
      }
    }
    document.getElementById('searchresult').innerHTML=result + '&nbsp;';

    // If possible, compare the current time as reported by Javascript
    // to "official" time as reported by AppEngine. If there is any significant
    // difference, show a warning message. We always expect at least a minor
    // time skew due to round trip delays, which we are not bothering to
    // compensate for.
    if (typeof timeskew != undefined) {
      var ts=document.getElementById('timeskew');
      if (Math.abs(timeskew) < 2000) {
        ts.style.color='';
        ts.innerHTML="Your computer's time is set correctly. TOTP codes " +
                     "will be computed accurately.";
      } else if (Math.abs(timeskew) < 30000) {
        ts.style.color='';
        ts.innerHTML="Your computer's time is off by " +
          (Math.round(Math.abs(timeskew)/1000)) + " seconds. This is within " +
          "acceptable tolerances. Computed TOTP codes might be different " +
          "from the ones in the mobile application, but they will be " +
          "accepted by the server.";
      } else {
        ts.style.color='#dd0000';
        ts.innerHTML="<b>Your computer's time is off by " +
          (Math.round(Math.abs(timeskew)/1000)) + " seconds. Computed TOTP " +
          "codes are probably incorrect.</b>";
      }
    }
  }
}
  --></script>
</head>
<body style="font-family: sans-serif" onload="setInterval(refresh, 100)">
  <h1>TOTP Debugger</h1>
  <table>
    <tr><td colspan="7"><a style="text-decoration: none; color: black"
                           id="authurl"></a></td></tr>
    <tr><td colspan="3">Enter&nbsp;secret&nbsp;key&nbsp;in&nbsp;BASE32:&nbsp;</td>
        <td><input type="text" id="secret" /></td>
        <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
        <td rowspan="8"><a style="text-decoration: none" id="aqr">
          <img id="qr" border="0"></a></td><td width="100%">&nbsp;</td></tr>
    <tr><td colspan="3">Account&nbsp;label:&nbsp;</td>
        <td><input type="text" id="label" /></td></tr>
    <tr><td>Interval:&nbsp;</td><td align="right">30s</td><td>&nbsp;</td></tr>
    <tr><td>Time:&nbsp;</td><td align="right">
          <span id="epoch"></span></td><td>&nbsp;</td><td>
          <input type="text" id="overrideepoch" /></td></tr>
    <tr><td>Timestamp:&nbsp;</td><td align="right"><span id="ts"></span></td>
        <td>&nbsp;</td><td><input type="text" id="override" /></td></tr>
    <tr><td>TOTP:&nbsp;</td><td align="right"><span id="totp"></span></td>
        <td>&nbsp;</td><td><input type="text" id="search" /></td></tr>
    <tr><td colspan="4" id="searchresult"></td></tr>
  </table>
  <br />
  <div id="timeskew" style="width: 80%"></div>
  <br />
  <br />
  <div style="width: 80%; color: #dd0000; font-size: small">
    <p><b>WARNING!</b> This website is a development and debugging tool only.
    Do <b>not</b> use it to generate security tokens for logging into your
    account. You should never store, process or otherwise access the secret
    key on the same machine that you use for accessing your account. Doing so
    completely defeats the security of two-step verification.</p>
    <p>Instead, use one of the mobile phone clients made available by the
    <a href="https://github.com/google/google-authenticator">Google
    Authenticator</a> project. Or follow the <a href="http://www.google.com/support/accounts/bin/static.py?hl=en&page=guide.cs&guide=1056283&topic=1056285">instructions</a>
    provided by Google.</p>
    <p>If you ever entered your real secret key into this website, please
    immediately <a href="https://www.google.com/accounts/SmsAuthConfig">reset
    your secret key</a>.</p>
  </div>
</body>
</html>
0707010000002C000041ED0000000000000000000000026627DD8100000000000000000000000000000000000000000000002A00000000google-authenticator-libpam-1.10/utc-time0707010000002D000081A40000000000000000000000016627DD8100000050000000000000000000000000000000000000003300000000google-authenticator-libpam-1.10/utc-time/app.yamlruntime: python312

handlers:
- url: /.*
  script: utc-time.py
  secure: always
0707010000002E000081A40000000000000000000000016627DD81000004A4000000000000000000000000000000000000003200000000google-authenticator-libpam-1.10/utc-time/main.py#!/usr/bin/env python
# Copyright 2011 Google Inc.
#
# 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.
#
# gcloud app deploy --project utc-time2
import time
from flask import Flask, make_response, request
app = Flask(__name__)

@app.route('/')
def root():
    t = time.time()
    u = time.gmtime(t)
    s = time.strftime('%a, %e %b %Y %T GMT', u)

    resp = make_response('var timeskew = new Date().getTime() - ' + str(t*1000) + ';')
    resp.headers['Content-Type'] = 'text/javascript'
    resp.headers['Cache-Control'] = 'no-cache'
    resp.headers['Date'] = s
    resp.headers['Expires'] = s
    return resp

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=8080, debug=True)
0707010000002F000081A40000000000000000000000016627DD810000000D000000000000000000000000000000000000003B00000000google-authenticator-libpam-1.10/utc-time/requirements.txtFlask==3.0.0
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!463 blocks
openSUSE Build Service is sponsored by