pax_global_header00006660000000000000000000000064136026232520014513gustar00rootroot0000000000000052 comment=2c7415d950fb0b4a7f779f045910666447b100ef google-authenticator-libpam-1.08/000077500000000000000000000000001360262325200170315ustar00rootroot00000000000000google-authenticator-libpam-1.08/.gitignore000066400000000000000000000102231360262325200210170ustar00rootroot00000000000000### 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 google-authenticator-libpam-1.08/.travis.yml000066400000000000000000000002671360262325200211470ustar00rootroot00000000000000language: c compiler: - clang - gcc script: ./bootstrap.sh && ./configure && make && make check sudo: false addons: apt: packages: - libpam0g-dev - libqrencode3 google-authenticator-libpam-1.08/CONTRIBUTING.md000066400000000000000000000026541360262325200212710ustar00rootroot00000000000000Want 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). google-authenticator-libpam-1.08/FILEFORMAT000066400000000000000000000056431360262325200204140ustar00rootroot00000000000000All 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. google-authenticator-libpam-1.08/LICENSE000066400000000000000000000261361360262325200200460ustar00rootroot00000000000000 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. google-authenticator-libpam-1.08/Makefile.am000066400000000000000000000046651360262325200211000ustar00rootroot00000000000000AUTOMAKE_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) google-authenticator-libpam-1.08/README.md000066400000000000000000000205261360262325200203150ustar00rootroot00000000000000# Google Authenticator PAM module Example PAM module demonstrating two-factor authentication. [![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. google-authenticator-libpam-1.08/bootstrap.sh000077500000000000000000000011351360262325200214050ustar00rootroot00000000000000#!/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 google-authenticator-libpam-1.08/build/000077500000000000000000000000001360262325200201305ustar00rootroot00000000000000google-authenticator-libpam-1.08/build/.gitignore000066400000000000000000000000161360262325200221150ustar00rootroot00000000000000* !.gitignore google-authenticator-libpam-1.08/configure.ac000066400000000000000000000043731360262325200213260ustar00rootroot00000000000000AC_PREREQ(2.61) AC_INIT(google-authenticator, 1.08, 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 \ ]) AC_CHECK_HEADERS_ONCE([security/pam_appl.h]) # On Solaris at least, requires # to be included first AC_CHECK_HEADER([security/pam_modules.h], [], [], \ [#ifdef HAVE_SECURITY_PAM_APPL_H # include #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 #include ]],[[ 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 " google-authenticator-libpam-1.08/contrib/000077500000000000000000000000001360262325200204715ustar00rootroot00000000000000google-authenticator-libpam-1.08/contrib/README.rpm.md000066400000000000000000000024401360262325200225450ustar00rootroot00000000000000## 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. google-authenticator-libpam-1.08/contrib/build-rpm.sh000077500000000000000000000014431360262325200227250ustar00rootroot00000000000000#!/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 google-authenticator-libpam-1.08/contrib/rpm.spec.in000066400000000000000000000030331360262325200225470ustar00rootroot00000000000000Name: @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 - 1.04 - Added installing man pages * Wed Jan 13 2016 Dan Molik - 1.01 - A new and updated build for google-authenticator google-authenticator-libpam-1.08/examples/000077500000000000000000000000001360262325200206475ustar00rootroot00000000000000google-authenticator-libpam-1.08/examples/demo.c000066400000000000000000000115771360262325200217520ustar00rootroot00000000000000// 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 #include #include #include #include #include #include #include #include #include #include #include #include #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; } google-authenticator-libpam-1.08/man/000077500000000000000000000000001360262325200176045ustar00rootroot00000000000000google-authenticator-libpam-1.08/man/google-authenticator.1000066400000000000000000000136111360262325200240140ustar00rootroot00000000000000.\" 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 . google-authenticator-libpam-1.08/man/google-authenticator.1.md000066400000000000000000000126631360262325200244210ustar00rootroot00000000000000% 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 . google-authenticator-libpam-1.08/man/pam_google_authenticator.8000066400000000000000000000151121360262325200247400ustar00rootroot00000000000000.\" 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 . google-authenticator-libpam-1.08/man/pam_google_authenticator.8.md000066400000000000000000000140531360262325200253420ustar00rootroot00000000000000% 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 . google-authenticator-libpam-1.08/src/000077500000000000000000000000001360262325200176205ustar00rootroot00000000000000google-authenticator-libpam-1.08/src/base32.c000066400000000000000000000046471360262325200210560ustar00rootroot00000000000000// 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 #include "base32.h" int base32_decode(const uint8_t *encoded, uint8_t *result, int bufSize) { 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) { 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; } google-authenticator-libpam-1.08/src/base32.h000066400000000000000000000025531360262325200210550ustar00rootroot00000000000000// 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 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_ */ google-authenticator-libpam-1.08/src/base32_prog.c000066400000000000000000000133141360262325200220740ustar00rootroot00000000000000// 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 #include #include #include #include #include #include #include #include #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 []\n", argv0); fprintf(f, "Usage: %s -d []\n", argv0); fprintf(f, "Usage: %s -D \n", argv0); fprintf(f, " Emits 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: */ google-authenticator-libpam-1.08/src/google-authenticator.c000066400000000000000000000732711360262325200241220ustar00rootroot00000000000000// 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 #include #include #include #include #include #include #include #include #include #include #include #include #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, char **encoderURL, 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= 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; } if (encoderURL) { // Show a QR code. const char *encoder = "https://www.google.com/chart?chs=200x200&" "chld=M|0&cht=qr&chl="; const char *encodedURL = urlEncode(url); *encoderURL = strcat(strcpy(malloc(strlen(encoder) + strlen(encodedURL) + 1), encoder), encodedURL); free((void *)encodedURL); } 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; } char *encoderURL; const char *url = getURL(secret, label, &encoderURL, use_totp, issuer); printf("Warning: pasting the following URL into your browser exposes the OTP secret to Google:\n %s\n", encoderURL); // 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); free(encoderURL); } // 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 usage(void) { puts( "google-authenticator []\n" " -h, --help Print this message\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=