diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..58862a6 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,7 @@ +Original authors and current maintainers of Linux-PAM: + +Andrew G. Morgan +Dmitry V. Levin +Thorsten Kukuk +Sebastien Tricaud +Tomas Mraz diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..12ff8c5 --- /dev/null +++ b/COPYING @@ -0,0 +1,40 @@ +Unless otherwise *explicitly* stated the following text describes the +licensed conditions under which the contents of this Linux-PAM release +may be distributed: + +------------------------------------------------------------------------- +Redistribution and use in source and binary forms of Linux-PAM, with +or without modification, are permitted provided that the following +conditions are met: + +1. Redistributions of source code must retain any existing copyright + notice, and this entire permission notice in its entirety, + including the disclaimer of warranties. + +2. Redistributions in binary form must reproduce all prior and current + copyright notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +3. The name of any author may not be used to endorse or promote + products derived from this software without their specific prior + written permission. + +ALTERNATIVELY, this product may be distributed under the terms of the +GNU General Public License, in which case the provisions of the GNU +GPL are required INSTEAD OF the above restrictions. (This clause is +necessary due to a potential conflict between the GNU GPL and the +restrictions contained in a BSD-style copyright.) + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +------------------------------------------------------------------------- diff --git a/Copyright b/Copyright new file mode 100644 index 0000000..12ff8c5 --- /dev/null +++ b/Copyright @@ -0,0 +1,40 @@ +Unless otherwise *explicitly* stated the following text describes the +licensed conditions under which the contents of this Linux-PAM release +may be distributed: + +------------------------------------------------------------------------- +Redistribution and use in source and binary forms of Linux-PAM, with +or without modification, are permitted provided that the following +conditions are met: + +1. Redistributions of source code must retain any existing copyright + notice, and this entire permission notice in its entirety, + including the disclaimer of warranties. + +2. Redistributions in binary form must reproduce all prior and current + copyright notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +3. The name of any author may not be used to endorse or promote + products derived from this software without their specific prior + written permission. + +ALTERNATIVELY, this product may be distributed under the terms of the +GNU General Public License, in which case the provisions of the GNU +GPL are required INSTEAD OF the above restrictions. (This clause is +necessary due to a potential conflict between the GNU GPL and the +restrictions contained in a BSD-style copyright.) + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +------------------------------------------------------------------------- diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..73bdc05 --- /dev/null +++ b/NEWS @@ -0,0 +1,518 @@ +Linux-PAM NEWS -- history of user-visible changes. + +Release 1.7.0 +* build: changed build system from autotools to meson. +* libpam_misc: use ECHOCTL in the terminal input +* pam_access: support UID and GID in access.conf +* pam_env: install environment file in vendordir if vendordir is enabled +* pam_issue: only count class user if logind support is enabled +* pam_limits: use systemd-logind instead of utmp if logind support is enabled +* pam_unix: compare password hashes in constant time +* Multiple minor bug fixes, build fixes, portability fixes, + documentation improvements, and translation updates. + +Release 1.6.1 +* build: fail if specified configure options cannot be satisfied. +* pam_env: fixed --disable-econf --enable-vendordir support. +* pam_unix: do not warn if password aging is disabled. +* pam_unix: try to set uid to 0 before unix_chkpwd invocation. +* pam_unix: allow empty passwords with non-empty hashes. +* Multiple minor bug fixes, build fixes, portability fixes, + documentation improvements, and translation updates. + +Release 1.6.0 +* Added support of configuration files with arbitrarily long lines. +* build: fixed build outside of the source tree. +* libpam: added use of getrandom(2) as a source of randomness if available. +* libpam: fixed calculation of fail delay with very long delays. +* libpam: fixed potential infinite recursion with includes. +* libpam: implemented string to number conversions validation when parsing + controls in configuration. +* pam_access: added quiet_log option. +* pam_access: fixed truncation of very long group names. +* pam_canonicalize_user: new module to canonicalize user name. +* pam_echo: fixed file handling to prevent overflows and short reads. +* pam_env: added support of '\' character in environment variable values. +* pam_exec: allowed expose_authtok for password PAM_TYPE. +* pam_exec: fixed stack overflow with binary output of programs. +* pam_faildelay: implemented parameter ranges validation. +* pam_listfile: changed to treat \r and \n exactly the same in configuration. +* pam_mkhomedir: hardened directory creation against timing attacks. + Please note that using *at functions leads to more open file handles + during creation. +* pam_namespace: fixed potential local DoS (CVE-2024-22365). +* pam_nologin: fixed file handling to prevent short reads. +* pam_pwhistory: helper binary is now built only if SELinux support is enabled. +* pam_pwhistory: implemented reliable usernames handling when remembering + passwords. +* pam_shells: changed to allow shell entries with absolute paths only. +* pam_succeed_if: fixed treating empty strings as numerical value 0. +* pam_unix: added support of disabled password aging. +* pam_unix: synchronized password aging with shadow. +* pam_unix: implemented string to number conversions validation. +* pam_unix: fixed truncation of very long user names. +* pam_unix: corrected rounds retrieval for configured encryption method. +* pam_unix: implemented reliable usernames handling when remembering passwords. +* pam_unix: changed to always run the helper to obtain shadow password entries. +* pam_unix: unix_update helper binary is now built only if SELinux support + is enabled. +* pam_unix: added audit support to unix_update helper. +* pam_userdb: added gdbm support. +* Multiple minor bug fixes, portability fixes, documentation improvements, + and translation updates. + +Release 1.5.3 +* configure: added options to configure stylesheets. +* configure: added --enable-logind option to use logind instead of utmp + in pam_issue and pam_timestamp. +* pam_modutil_getlogin: changed to use getlogin() from libc instead of parsing utmp. +* Added libeconf support to pam_env and pam_shells. +* Added vendor directory support to pam_access, pam_env, pam_group, pam_faillock, + pam_limits, pam_namespace, pam_pwhistory, pam_sepermit, pam_shells, and pam_time. +* pam_limits: changed to not fail on missing config files. +* pam_pwhistory: added conf= option to specify config file location. +* pam_pwhistory: added file= option to specify password history file location. +* pam_shells: added shells.d support when libeconf and vendordir are enabled. +* Deprecated pam_lastlog: this module is no longer built by default because + it uses utmp, wtmp, btmp and lastlog, but none of them are Y2038 safe, + even on 64bit architectures. + pam_lastlog will be removed in one of the next releases, consider using + pam_lastlog2 (from https://github.com/thkukuk/lastlog2) and/or + pam_wtmpdb (from https://github.com/thkukuk/wtmpdb) instead. +* Deprecated _pam_overwrite(), _pam_overwrite_n(), and _pam_drop_reply() macros + provided by _pam_macros.h; the memory override performed by these macros can + be optimized out by the compiler and therefore can no longer be relied upon. +* Multiple minor bug fixes, portability fixes, documentation improvements, + and translation updates. + +Release 1.5.2 +* pam_exec: implemented quiet_log option. +* pam_mkhomedir: added support of HOME_MODE and UMASK from /etc/login.defs. +* pam_timestamp: changed hmac algorithm to call openssl instead of the bundled + sha1 implementation if selected, added option to select + the hash algorithm to use with HMAC. +* Added pkgconfig files for provided libraries. +* Added --with-systemdunitdir configure option to specify systemd unit + directory. +* Added --with-misc-conv-bufsize configure option to specify the buffer size + in libpam_misc's misc_conv() function, raised the default value for this + parameter from 512 to 4096. +* Multiple minor bug fixes, portability fixes, documentation improvements, + and translation updates. + +Release 1.5.1 +* pam_unix: fixed CVE-2020-27780 - authentication bypass when a user + doesn't exist and root password is blank +* pam_faillock: added nodelay option to not set pam_fail_delay +* pam_wheel: use pam_modutil_user_in_group to check for the group membership + with getgrouplist where it is available + +Release 1.5.0 +* Multiple minor bug fixes, portability fixes, and documentation improvements. +* Extended libpam API with pam_modutil_check_user_in_passwd function. +* configure: added --disable-unix option to disable build of pam_unix module. +* pam_faillock: changed /run/faillock/$USER permissions from 0600 to 0660. +* pam_limits: added support for nonewprivs item. +* pam_motd: read motd files with target user credentials skipping unreadable ones. +* pam_pwhistory: added a SELinux helper executable. +* pam_unix, pam_usertype: implemented avoidance of certain timing attacks. +* pam_wheel: implemented PAM_RUSER fallback for the case when getlogin fails. +* Removed deprecated pam_cracklib module, use pam_passwdqc (from passwdqc project) + or pam_pwquality (from libpwquality project) instead. +* Removed deprecated pam_tally and pam_tally2 modules, use pam_faillock instead. +* pam_env: Reading of the user environment is deprecated and will be removed + at some point in the future. +* libpam: pam_modutil_drop_priv() now correctly sets the target user's + supplementary groups, allowing pam_motd to filter messages accordingly + +Release 1.4.0 +* Multiple minor bug fixes and documentation improvements +* Fixed grammar of messages printed via pam_prompt +* Added support for a vendor directory and libeconf +* configure: Added --enable-Werror option to enable -Werror build +* configure: Allowed disabling documentation through --disable-doc +* pam_get_authtok_verify: Avoid duplicate password verification +* pam_cracklib: Fixed parsing of options without arguments +* pam_env: Changed the default to not read the user .pam_environment file +* pam_exec: Require a user name to be specified before the command is executed +* pam_faillock: New module for locking after multiple auth failures +* pam_group, pam_time: Fixed logical error with multiple ! operators +* pam_keyinit: In pam_sm_setcred do the same as in pam_sm_open_session +* pam_lastlog: Do not log info about failed login if the session was opened + with PAM_SILENT flag +* pam_lastlog: Limit lastlog file use by LASTLOG_UID_MAX option in login.defs +* pam_lastlog: With 'unlimited' option prevent SIGXFSZ due to reduced 'fsize' + limit +* pam_mkhomedir: Fixed return value when the user is unknown +* pam_motd: Export MOTD_SHOWN=pam after showing MOTD +* pam_motd: Support multiple motd paths specified, with filename overrides +* pam_namespace: Added a systemd service, which creates the namespaced + instance parent directories during boot +* pam_namespace: Support for noexec, nosuid and nodev flags for tmpfs mounts +* pam_selinux: Check unknown object classes or permissions in current policy +* pam_selinux: Fall back to log to syslog if audit logging fails +* pam_setquota: New module to set or modify disk quotas on session start +* pam_shells: Recognize /bin/sh as the default shell +* pam_succeed_if: Fixed potential override of the default prompt +* pam_succeed_if: Support lists in group membership checks +* pam_time: Added conffile= option to specify an alternative configuration file +* pam_tty_audit: If kernel audit is disabled return PAM_IGNORE +* pam_umask: Added new 'nousergroups' module argument and allowed specifying + the default for usergroups at build-time +* pam_unix: Added 'nullresetok' option to allow resetting blank passwords +* pam_unix: Report unusable hashes found by checksalt to syslog +* pam_unix: Return PAM_AUTHINFO_UNAVAIL when shadow entry is unavailable +* pam_unix: Support for (gost-)yescrypt hashing methods +* pam_unix: Use bcrypt b-variant when it bcrypt is chosen +* pam_usertype: New module to tell if uid is in login.defs ranges +* Fixed and documented possible values returned by pam_get_user() +* Added new API call pam_start_confdir() for special applications that + cannot use the system-default PAM configuration paths and need to + explicitly specify another path +* Deprecated pam_cracklib: this module is no longer built by default and will + be removed in the next release, use pam_passwdqc (from passwdqc project) + or pam_pwquality (from libpwquality project) instead +* Deprecated pam_tally and pam_tally2: these modules are no longer built + by default and will be removed in the next release, use pam_faillock instead + +Release 1.3.1 +* pam_motd: add support for a motd.d directory +* pam_umask: Fix documentation to align with order of loading umask +* pam_get_user.3: Fix missing word in documentation +* pam_tally2 --reset: avoid creating a missing tallylog file +* pam_mkhomedir: Allow creating parent of homedir under / +* access.conf.5: Add note about spaces around ':' +* pam.8: Workaround formatting problem +* pam_unix: Check return value of malloc used for setcred data +* pam_cracklib: Drop unused prompt macros +* pam_tty_audit: Support matching users by uid range +* pam_access: support parsing files in /etc/security/access.d/*.conf +* pam_localuser: Correct documentation +* pam_issue: Fix no prompting in parse escape codes mode +* Unification and cleanup of syslog log levels + + +Release 1.3.0 +* Remove of static modules support +* pam_unix: pass_not_set was removed +* Lot of documentation fixes +* Use TI-RPC function calls if we build against libtirpc +* Add support for new, IPv6 enabled libnsl +* Lot of bug fixes +* Use fedora.zanata.org for translations + + +Release 1.2.1 +* Fix CVE-2015-3238, affected PAM modules are pam_unix and pam_exec + + +Release 1.2.0 +* Update documentation +* Update translations +* pam_unix: add quiet option +* libpam: support alternative configuration files in /usr/lib/pam.d + as fallback +* pam_env: add support for @{HOME} and @{SHELL} +* libpam: add grantor field to audit records +* libpam: Introduce pam_modutil_sanitize_helper_fds + + +Release 1.1.8 +* pam_unix: bug fix for compiling with SELinux, fix crash at login time + + +Release 1.1.7 +* Update translations +* pam_exec: add stdout and type= options +* pam_tty_audit: add options to control logging of passwords +* pam_unix: Read defaults from /etc/login.defs +* pam_userdb: Allow modern password hashes +* pam_selinux/pam_tally2: Add tty and rhost to audit data +* Lot of docu and code fixes + + +Release 1.1.6 +* Update translations +* pam_cracklib: Add more checks for weak passwords +* pam_lastlog: Never lock out root +* Lot of bug fixes and smaller enhancements + + +Release 1.1.5 +* pam_env: Fix CVE-2011-3148 and CVE-2011-3149 +* pam_access: Add hostname resolution cache +* Documentation: Improvements/fixes + + +Release 1.1.4 + +* Add vietnamese translation +* pam_namespace: Add new functionality +* pam_securetty: Honour console= kernel option, add noconsole option +* pam_limits: Add %group syntax, drop change_uid option, add set_all option +* Lot of small bug fixes +* Lot of compiler warnings fixed +* Add support for libtirpc + + +Release 1.1.3 + +* pam_namespace: Clean environment for child processes (CVE-2010-3853) +* libpam: New interface to drop/regain privileges +* Drop root privileges in pam_env, pam_mail and pam_xauth before + accessing user files (CVE-2010-3430, CVE-2010-3431) +* pam_unix: Add minlen option, change default from 6 to 0 +* Documentation improvements +* Lot of small bug fixes + +Release 1.1.2 + +* pam_unix: Add minlen= option +* pam_group: Add support for UNIX groups beside netgroups +* pam_tally: Document that it is deprecated +* pam_rootok: Add support for chauthtok and acct_mgmt +* Update translations + +Release 1.1.1 + +* Update translations +* pam_access: Revert netgroup match to original behavior, add new + syntax for adding the local hostname to netgroup match +* libpam: Add new functions pam_get_authtok_noverify() and + pam_get_authtok_verify() +* Add sepermit.conf.5 manual page +* Lot of bug fixes + +Release 1.1.0 + +* Update translations +* Documentation updates and fixes + +Release 1.0.92 + +* Update translations +* pam_succeed_if: Use provided username +* pam_mkhomedir: Fix handling of options + +Release 1.0.91 + +* Fixed CVE-2009-0579 (minimum days limit on password change is ignored). +* Fix libpam internal config/argument parser +* Add optional file locking to pam_tally2 +* Update translations +* pam_access improvements +* Changes in the behavior of the password stack. Results of PRELIM_CHECK + are not used for the final run. + +Release 1.0.90 + +* Supply hostname of the machine to netgroup match call in pam_access +* Make pam_namespace to work safe on child directories of parent directories + owned by users +* Redefine LOCAL keyword of pam_access configuration file +* Add support for try_first_pass and use_first_pass to pam_cracklib +* Print informative messages for rejected login and add silent and + no_log_info options to pam_tally +* Add support for passing PAM_AUTHTOK to stdin of helpers from pam_exec +* New password quality tests in pam_cracklib +* New options for pam_lastlog to show last failed login attempt and + to disable lastlog update +* New pam_pwhistory module to store last used passwords +* New pam_tally2 module similar to pam_tally with wordsize independent + tally data format +* Make libpam not log missing module if its type is prepended with '-' +* New pam_timestamp module for authentication based on recent successful + login. +* Add blowfish support to pam_unix. +* Add support for user specific environment file to pam_env. +* Add pam_get_authtok to libpam as Linux-PAM extension. +* Rename type option of pam_cracklib to authtok_type. + +Release 1.0.3 + +* Small bug fix release + + +Release 1.0.2 + +* Regression fixed in pam_selinux +* Problem with big UIDs fixed in pam_loginuid + + +Release 1.0.1 + +* Regression fixed in pam_set_item() + + +Release 1.0.0 + +* Small bug fixes +* Translation updates + + +Release 0.99.10.0 + +* New substack directive in config file syntax. +* New module pam_tty_audit.so for enabling and disabling tty + auditing. +* New PAM items PAM_XDISPLAY and PAM_XAUTHDATA. +* Auditing login denials based by origin (pam_access), time (pam_time), + and number of sessions (pam_limits) to the Linux audit subsystem. +* Support sha256 and sha512 algorithms in pam_unix when they are supported + by crypt(). +* New pam_sepermit.so module for allowing/rejecting access based on + SELinux mode. +* Improved functionality of pam_namespace.so module (method flags, + namespace.d configuration directory, new options). +* Finally removed deprecated pam_rhosts_auth module. + + +Release 0.99.9.0 + +* misc_conv no longer blocks SIGINT; applications that don't want + user-interruptable prompts should block SIGINT themselves +* Merge fixes from Debian +* Fix parser for pam_group and pam_time + + +Release 0.99.8.1 + +* Fix a regression in audit code introduced with last release +* Fix compiling with --disable-nls + + +Release 0.99.8.0 + +* Add translations for ar, ca, da, ru, sv and zu. +* Update hungarian translation. +* Add support for limits.d directory to pam_limits. +* Improve pam_namespace module tobe more useful + for MLS, fixed crash with bad config files. +* Improve pam_selinux module to be more useful + for MLS. +* Add minclass option to pam_cracklib +* Add new group syntax to pam_access + + +Release 0.99.7.1 + +* Security fix for pam_unix.so (CVE-2007-0003). + + +Release 0.99.7.0 + +* Add manual page for pam_unix.so. +* Add pam_faildelay module to set pam_fail_delay() value. +* Fix possible seg.fault in libpam/pam_set_data(). +* Cleanup of configure options. +* Update hungarian translation, fix german translation. + + +Release 0.99.6.3 + +* pam_loginuid: New PAM module. +* pam_access, pam_succeed_if: Support passwd and session services. + + +Release 0.99.6.2 + +* pam_lastlog: Don't refuse login if lastlog file got lost. +* pam_cracklib: Fix a user triggerable crash. +* documentation: Regenerate with fixed docbook stylesheet. + + +Release 0.99.6.1 + +* Fix bootstrapping problems. +* Bug fixes: pam_keyinit, pam_umask + + +Release 0.99.6.0 + +* pam_namespace: Code cleanup, add init script to tar archive. +* pam_succeed_if: Add support for service match. +* Add xtests (to run after installation). +* Documentation: Convert sgml guides to XML, unify documentation + for PAM functions and modules. + + +Release 0.99.5.0 + +* pam_tally: Fix support for large UIDs +* Fixed all problems found by Coverity +* Add support for Intel C Compiler +* Add manual page for pam_mkhomedir, pam_umask, pam_filter, + pam_issue, pam_ftp, pam_group, pam_lastlog, pam_listfile, + pam_localuser, pam_mail, pam_motd, pam_nologin, pam_permit, + pam_rootok, pam_securetty, pam_shells, pam_userdb, pam_warn, + pam_time, pam_limits, pam_debug, pam_tally +* The libpam memory debug code was removed +* pam_keyinit: New module to initialise kernel session keyring. +* pam_namespace: New module to configure private namespace for a session. +* pam_rhosts: New module which replaces pam_rhosts_auth, now IPv6 capable. +* pam_rhosts_auth: This module is now deprecated. + + +Release 0.99.4.0 + +* Add test suite +* Fix building of static variants of libpam, libpamc and libpam_misc +* pam_listfile: Add support for password and session management +* pam_exec: New PAM module to execute arbitrary commands +* Fix building of a static libpam including all PAM modules +* New/updated translations for: nl, pt, pl, fi, km, tr, uk, fr +* pam_access: Add network(address) / netmask and IPv6 support +* Add manual pages for pam_cracklib, pam_deny and pam_access +* pam_pwdb: This deprecated module was removed +* Manual pages: Major rewrite/cleanup + + +Release 0.99.3.0 + +* Fix NULL pointer checks in libpam.so +* pam_succeed_if, pam_group, pam_time: Support netgroup matching +* New translations for: nb, hu, fi, de, es, fr, it, ja, pt_BR, zh_CN, zh_TW +* Audit PAM calls if Linux Audit is available +* Compile upperLOWER and unix_chkpwd as PIE binaries + + +Release 0.99.2.1 + +* Fix install of PS, PDF, TXT and HTML files +* pam_mail: Update README +* Use %m consistent +* pam_modutil_getlogin: Fix parsing of PAM_TTY variable + + +Release 0.99.2.0 + +* Fix parsing of full path tty name in various modules +* pam_xauth: Look for xauth executable in multiple places +* pam_unix: Disable user check in unix_chkpwd only if real uid + is 0 (CVE-2005-2977). Log failed password check attempt. +* pam_env: Support /etc/environment again, but don't treat it as + error if it is missing. +* pam_userdb: Fix memory leak. + + +Release 0.99.1.0 + +* Use autoconf/automake/libtool +* Add gettext support +* Add translations for cs, de, es, fr, hu, it, ja, nb, pa, pt_BR, + pt, zh_CN and zh_TW +* libpam: Remove pam_authenticate_secondary stub +* libpam: Add pam_prompt,pam_vprompt,pam_error,pam_verror,pam_info + and pam_vinfo functions for use by modules as extension +* libpam: Add pam_syslog function for unified syslog messages from + PAM modules +* libpam: Moved functions from pammodutil to libpam +* pam_umask: New module for setting umask from GECOS field, /etc/login.defs + or /etc/default/login +* pam_echo: New PAM module for message output +* pam_userdb: Fix regression (crash when crypt param not specified) +* pam_limits: Fix regression from RLIMIT_NICE support (wrong limit + values for other limits are applied) +* pam_access: Support for NULL tty - matches ALL and NONE keywords +* pam_lastlog: Enable log to wtmp by default. Add "nowtmp" option +* pam_radius: This module was removed diff --git a/README b/README new file mode 100644 index 0000000..1813227 --- /dev/null +++ b/README @@ -0,0 +1,50 @@ +Hello! + +Thanks for downloading Linux-PAM. + +NOTES: + +How to use it is as follows: + +Please look at the ci/install-dependencies.sh for the necessary +prerequisite packages to be able to build the Linux-PAM. The script +is targeted at Debian based Linux distributions so the package +names and availability might differ on other distributions. + +First, configure the build using meson setup: + + mkdir build + meson setup build + +Then compile: + + meson compile -C build + +To make sure everything was compiled correct, run: + + meson test -C build + +If a test fails, you should not continue to install this build. +These tests require a suitable file /etc/pam.d/other; if necessary, +create such a file containing, e.g., these five lines (not indented) + + #%PAM-1.0 + auth required pam_deny.so + account required pam_deny.so + password required pam_deny.so + session required pam_deny.so + + +Note, if you are worried - don't even think about doing the next line +(most Linux distributions already support PAM out of the box, so if +something goes wrong with installing the code from this version your +box may stop working..) + + meson install -C build + +That said, please report problems to the bug reporting database +at https://github.com/linux-pam/linux-pam/issues . + +To generate manual pages from the XML source files you need the +docbook-xsl stylesheets in version 1.69.1 or newer, older versions had +a bug which generates a broken layout. diff --git a/aux/chdir_meson_build_subdir.sh b/aux/chdir_meson_build_subdir.sh new file mode 100755 index 0000000..69c32fc --- /dev/null +++ b/aux/chdir_meson_build_subdir.sh @@ -0,0 +1,6 @@ +#!/bin/sh -efu + +exe=$1; shift +exe=$(readlink -ev -- "$exe") +cd "$MESON_BUILD_SUBDIR" +exec "$exe" "$@" diff --git a/aux/redir_exe.sh b/aux/redir_exe.sh new file mode 100755 index 0000000..3ab78e6 --- /dev/null +++ b/aux/redir_exe.sh @@ -0,0 +1,6 @@ +#!/bin/sh -efu +# stdin stdout ... + +exec < "$1"; shift +exec > "$1"; shift +exec "$@" diff --git a/ci/build.sh b/ci/build.sh new file mode 100755 index 0000000..c50bb7f --- /dev/null +++ b/ci/build.sh @@ -0,0 +1,53 @@ +#!/bin/sh -ex +# +# Copyright (c) 2018-2024 The strace developers. +# All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +opts='-Doptimization=2 -Dwerror=true -Dpam_lastlog=enabled' + +case "${VENDORDIR-}" in + */*) + opts="$opts -Dvendordir=$VENDORDIR" + ;; +esac + +case "${USE_OPENSSL-}" in + yes) + opts="$opts -Dopenssl=enabled" + ;; +esac + +case "${ENABLE_DEBUG-}" in + yes) + opts="$opts -Dpam-debug=true" + ;; +esac + +echo 'BEGIN OF BUILD ENVIRONMENT INFORMATION' +uname -a |head -1 +libc="$(ldd /bin/sh |sed -n 's|^[^/]*\(/[^ ]*/libc\.so[^ ]*\).*|\1|p' |head -1)" +$libc |head -1 +$CC --version |head -1 +meson --version |head -1 +ninja --version |head -1 +kver="$(printf '%s\n%s\n' '#include ' 'LINUX_VERSION_CODE' | $CC -E -P -)" +printf 'kernel-headers %s.%s.%s\n' $((kver/65536)) $((kver/256%256)) $((kver%256)) +echo 'END OF BUILD ENVIRONMENT INFORMATION' + +mkdir build +meson setup $opts build + +# If "meson dist" supported -v option, it could be used here +# instead of all subsequent individual meson commands. + +meson compile -v -C build +mkdir build/destdir +DESTDIR=$(pwd)/build/destdir meson install -C build +meson test -v -C build + +if git status --porcelain |grep '^?'; then + echo >&2 'git status reported untracked files' + exit 1 +fi diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh new file mode 100755 index 0000000..d38aba9 --- /dev/null +++ b/ci/install-dependencies.sh @@ -0,0 +1,77 @@ +#!/bin/sh -ex +# +# Copyright (c) 2018-2019 The strace developers. +# All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +j=-j`nproc` || j= +type sudo >/dev/null 2>&1 && sudo=sudo || sudo= +packages=" +bison +docbook5-xml +docbook-xsl-ns +flex +gettext +libaudit-dev +libdb-dev +libfl-dev +libselinux1-dev +libssl-dev +libxml2-utils +meson +pkg-config +sed +w3m +xsltproc +xz-utils +$CC" + +retry_if_failed() +{ + for i in `seq 0 99`; do + "$@" && i= && break || sleep 1 + done + [ -z "$i" ] +} + +updated= +apt_get_install() +{ + [ -n "$updated" ] || { + retry_if_failed $sudo apt-get -qq update + updated=1 + } + retry_if_failed $sudo \ + apt-get -qq --no-install-suggests --no-install-recommends \ + install -y "$@" +} + +case "$CC" in + gcc-*) + retry_if_failed \ + $sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + ;; +esac + +case "$TARGET" in + x32|x86) + packages="$packages gcc-multilib" + case "$CC" in + gcc-*) packages="$packages $CC-multilib" ;; + esac + ;; +esac + +apt_get_install $packages + +case "${CHECK-}" in + coverage) + apt_get_install lcov python-pip python-setuptools + retry_if_failed \ + pip install --user codecov + ;; + valgrind) + apt_get_install valgrind + ;; +esac diff --git a/conf/install_conf b/conf/install_conf new file mode 100755 index 0000000..7a2acd9 --- /dev/null +++ b/conf/install_conf @@ -0,0 +1,36 @@ +#!/bin/sh + +CONFILE="$FAKEROOT"$CONFIGED/pam.conf +IGNORE_AGE=./.ignore_age +CONF=./pam.conf + +echo + +if [ -f "$IGNORE_AGE" ]; then + echo "you don't want to be bothered with the age of your $CONFILE file" + yes="n" +elif [ ! -f "$CONFILE" ] || [ "$CONF" -nt "$CONFILE" ]; then + if [ -f "$CONFILE" ]; then + echo "\ +An older Linux-PAM configuration file already exists ($CONFILE)" + WRITE=overwrite + fi + echo -n "\ +Do you wish to copy the $CONF file in this distribution +to $CONFILE ? (y/n) [n] " + read yes +else + yes=n +fi + +if [ "$yes" = "y" ]; then + echo " copying $CONF to $CONFILE" + cp $CONF $CONFILE +else + touch "$IGNORE_AGE" + echo " Skipping $CONF installation" +fi + +echo + +exit 0 diff --git a/conf/md5itall b/conf/md5itall new file mode 100755 index 0000000..b5fecc4 --- /dev/null +++ b/conf/md5itall @@ -0,0 +1,43 @@ +#!/bin/bash +# +# $Id$ +# +# Created by Andrew G. Morgan (morgan@parc.power.net) +# + +MD5SUM=md5sum +CHKFILE1=./.md5sum +CHKFILE2=./.md5sum-new + +which $MD5SUM > /dev/null +result=$? + +if [ -x "$MD5SUM" ] || [ $result -eq 0 ]; then + rm -f $CHKFILE2 + echo -n "computing md5 checksums." + for x in `cat ../.filelist` ; do + (cd ../.. ; $MD5SUM $x) >> $CHKFILE2 + echo -n "." + done + echo + if [ -f "$CHKFILE1" ]; then + echo "\ +---> Note, since the last \`make check', the following file(s) have changed: +===========================================================================" + diff $CHKFILE1 $CHKFILE2 + if [ $? -eq 0 ]; then + echo "\ +--------------------------- Nothing has changed ---------------------------" + fi + echo "\ +===========================================================================" + fi + rm -f "$CHKFILE1" + mv "$CHKFILE2" "$CHKFILE1" + chmod 400 "$CHKFILE1" +else + echo "\ +Please install \`$MD5SUM'. +[It is used to check the integrity of this distribution] +---> no check done." +fi diff --git a/conf/pam.conf b/conf/pam.conf new file mode 100644 index 0000000..4961a0a --- /dev/null +++ b/conf/pam.conf @@ -0,0 +1,120 @@ +# ---------------------------------------------------------------------------# +# /etc/pam.conf # +# # +# Last modified by Andrew G. Morgan # +# ---------------------------------------------------------------------------# +# $Id$ +# ---------------------------------------------------------------------------# +# serv. module ctrl module [path] ...[args..] # +# name type flag # +# ---------------------------------------------------------------------------# +# +# The PAM configuration file for the `chfn' service +# +chfn auth required pam_unix.so +chfn account required pam_unix.so +chfn password required pam_unix.so shadow md5 use_authtok +# +# The PAM configuration file for the `chsh' service +# +chsh auth required pam_unix.so +chsh account required pam_unix.so +chsh password required pam_unix.so shadow md5 use_authtok +# +# The PAM configuration file for the `ftp' service +# +ftp auth requisite pam_listfile.so \ + item=user sense=deny file=/etc/ftpusers onerr=succeed +ftp auth requisite pam_shells.so +ftp auth required pam_unix.so +ftp account required pam_unix.so +# +# The PAM configuration file for the `imap' service +# +imap auth required pam_unix.so +imap account required pam_unix.so +# +# The PAM configuration file for the `login' service +# +login auth requisite pam_securetty.so +login auth required pam_unix.so +login auth optional pam_group.so +login account requisite pam_time.so +login account required pam_unix.so +login password required pam_unix.so shadow md5 use_authtok +login session required pam_unix.so +# +# The PAM configuration file for the `netatalk' service +# +netatalk auth required pam_unix.so +netatalk account required pam_unix.so +# +# The PAM configuration file for the `other' service +# +other auth required pam_deny.so +other auth required pam_warn.so +other account required pam_deny.so +other password required pam_deny.so +other password required pam_warn.so +other session required pam_deny.so +# +# The PAM configuration file for the `passwd' service +# +passwd password required pam_unix.so shadow md5 use_authtok +# +# The PAM configuration file for the `rexec' service +# +rexec auth requisite pam_securetty.so +rexec auth requisite pam_nologin.so +rexec auth sufficient pam_rhosts_auth.so +rexec auth required pam_unix.so +rexec account required pam_unix.so +rexec session required pam_unix.so +rexec session required pam_limits.so +# +# The PAM configuration file for the `rlogin' service +# this application passes control to `login' if it fails +# +rlogin auth requisite pam_securetty.so +rlogin auth requisite pam_nologin.so +rlogin auth required pam_rhosts_auth.so +rlogin account required pam_unix.so +rlogin password required pam_unix.so shadow md5 use_authtok +rlogin session required pam_unix.so +rlogin session required pam_limits.so +# +# The PAM configuration file for the `rsh' service +# +rsh auth requisite pam_securetty.so +rsh auth requisite pam_nologin.so +rsh auth sufficient pam_rhosts_auth.so +rsh auth required pam_unix.so +rsh account required pam_unix.so +rsh session required pam_unix.so +rsh session required pam_limits.so +# +# The PAM configuration file for the `samba' service +# +samba auth required pam_unix.so +samba account required pam_unix.so +# +# The PAM configuration file for the `su' service +# +su auth required pam_wheel.so +su auth sufficient pam_rootok.so +su auth required pam_unix.so +su account required pam_unix.so +su session required pam_unix.so +# +# The PAM configuration file for the `vlock' service +# +vlock auth required pam_unix.so +# +# The PAM configuration file for the `xdm' service +# +xdm auth required pam_unix.so +xdm account required pam_unix.so +# +# The PAM configuration file for the `xlock' service +# +xlock auth required pam_unix.so diff --git a/conf/pam_conv1/README b/conf/pam_conv1/README new file mode 100644 index 0000000..753d71f --- /dev/null +++ b/conf/pam_conv1/README @@ -0,0 +1,8 @@ + +This directory contains a utility to convert pam.conf files to a pam.d/ +tree. The conversion program takes pam.conf from the standard input and +creates the pam.d/ directory in the current directory. + +The program will fail if ./pam.d/ already exists. + +Andrew Morgan, February 1997 diff --git a/conf/pam_conv1/meson.build b/conf/pam_conv1/meson.build new file mode 100644 index 0000000..487acd6 --- /dev/null +++ b/conf/pam_conv1/meson.build @@ -0,0 +1,25 @@ +pam_conv_y = custom_target( + 'pam_conv_y.[ch]', + input: 'pam_conv_y.y', + output: ['pam_conv_y.c', 'pam_conv_y.h'], + command: yacc_cmd, +) + +pam_conv_l = custom_target( + 'pam_conv_l.c', + input: 'pam_conv_l.l', + output: 'pam_conv_l.c', + depends: pam_conv_y, + command: [prog_flex, '-o', '@OUTPUT@', '@INPUT@'], +) + +executable( + 'pam_conv1', + sources: [pam_conv_l, pam_conv_y], + include_directories: [libpam_inc], + c_args: [ + '-Wno-unused-function', + '-Wno-sign-compare', + ], + link_args: exe_link_args, +) diff --git a/conf/pam_conv1/pam_conv_l.l b/conf/pam_conv1/pam_conv_l.l new file mode 100644 index 0000000..06fc320 --- /dev/null +++ b/conf/pam_conv1/pam_conv_l.l @@ -0,0 +1,45 @@ + +%{ +/* + * $Id$ + * + * Copyright (c) Andrew G. Morgan 1997 + * + * This file is covered by the Linux-PAM License (which should be + * distributed with this file.) + */ + +#include + +#include + +#include "pam_conv_y.h" + + extern unsigned long long current_line; +%} + +%option noyywrap +%% + +"#"[^\n]* ; /* skip comments (sorry) */ + +"\\\n" { + ++current_line; +} + +([^\n\t ]|[\\][^\n])+ { + return TOK; +} + +[ \t]+ ; /* Ignore */ + +<> { + return EOFILE; +} + +[\n] { + ++current_line; + return NL; +} + +%% diff --git a/conf/pam_conv1/pam_conv_y.y b/conf/pam_conv1/pam_conv_y.y new file mode 100644 index 0000000..b36351c --- /dev/null +++ b/conf/pam_conv1/pam_conv_y.y @@ -0,0 +1,217 @@ +%{ + +/* + * $Id$ + * + * Copyright (c) Andrew G. Morgan 1997 + * + * This file is covered by the Linux-PAM License (which should be + * distributed with this file.) + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + + extern int yylex(void); + + unsigned long long current_line=0; + extern char *yytext; + +/* XXX - later we'll change this to be the specific conf file(s) */ +#define newpamf stderr + +#define PAM_D "./pam.d" +#define PAM_D_MODE 0755 +#define PAM_D_MAGIC_HEADER \ + "#%%PAM-1.0\n" \ + "#[For version 1.0 syntax, the above header is optional]\n" + +#define PAM_D_FILE_FMT PAM_D "/%s" + + const char *old_to_new_ctrl_flag(const char *old); + void yyerror(const char *format, ...); +%} + +%union { + int def; + char *string; +} + +%token NL EOFILE TOK + +%type tok path tokenls + +%start complete + +%% + +complete +: +| complete NL +| complete line +| complete EOFILE { + return 0; +} +; + +line +: tok tok tok path tokenls NL { + char *filename; + FILE *conf; + int i; + + /* make sure we have lower case */ + for (i=0; $1[i]; ++i) { + $1[i] = tolower((unsigned char)$1[i]); + } + + /* $1 = service-name */ + yyerror("Appending to " PAM_D "/%s", $1); + + if (asprintf(&filename, PAM_D_FILE_FMT, $1) < 0) { + yyerror("unable to create filename - aborting"); + exit(1); + } + conf = fopen(filename, "r"); + if (conf == NULL) { + /* new file */ + conf = fopen(filename, "w"); + if (conf != NULL) { + fprintf(conf, PAM_D_MAGIC_HEADER); + fprintf(conf, + "#\n" + "# The PAM configuration file for the `%s' service\n" + "#\n", $1); + } + } else { + fclose(conf); + conf = fopen(filename, "a"); + } + if (conf == NULL) { + yyerror("trouble opening %s - aborting", filename); + exit(1); + } + free(filename); + free($1); + + /* $2 = module-type */ + fprintf(conf, "%-10s", $2); + free($2); + + /* $3 = required etc. */ + { + const char *trans; + + trans = old_to_new_ctrl_flag($3); + free($3); + fprintf(conf, " %-10s", trans); + } + + /* $4 = module-path */ + fprintf(conf, " %s", $4); + free($4); + + /* $5 = arguments */ + if ($5 != NULL) { + fprintf(conf, " \\\n\t\t%s", $5); + free($5); + } + + /* end line */ + fprintf(conf, "\n"); + + fclose(conf); +} +| error NL { + yyerror("malformed line"); +} +; + +tokenls +: { + $$=NULL; +} +| tokenls tok { + if ($1) { + if (asprintf(&$$, "%s %s", $1, $2) < 0) { + yyerror("failed to assemble tokenls"); + exit(1); + } + free($1); + free($2); + } else { + $$ = $2; + } +} +; + +path +: TOK { + /* XXX - this could be used to check if file present */ + $$ = strdup(yytext); + if ($$ == NULL) { + yyerror("failed to duplicate path"); + exit(1); + } +} + +tok +: TOK { + $$ = strdup(yytext); + if ($$ == NULL) { + yyerror("failed to duplicate token"); + exit(1); + } +} + +%% + +const char *old_to_new_ctrl_flag(const char *old) +{ + static const char *const clist[] = { + "requisite", + "required", + "sufficient", + "optional", + NULL, + }; + int i; + + for (i=0; clist[i]; ++i) { + if (strcasecmp(clist[i], old) == 0) { + break; + } + } + + return clist[i]; +} + +PAM_FORMAT((printf, 1, 2)) +void yyerror(const char *format, ...) +{ + va_list args; + + fprintf(stderr, "line %llu: ", current_line); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); +} + +int main(void) +{ + if (mkdir(PAM_D, PAM_D_MODE) != 0) { + yyerror(PAM_D " already exists.. aborting"); + return 1; + } + yyparse(); + return 0; +} diff --git a/doc/adg/Linux-PAM_ADG.xml b/doc/adg/Linux-PAM_ADG.xml new file mode 100644 index 0000000..0f1957e --- /dev/null +++ b/doc/adg/Linux-PAM_ADG.xml @@ -0,0 +1,749 @@ + + + The Linux-PAM Application Developers' Guide + + Andrew G.Morganmorgan@kernel.org + ThorstenKukukkukuk@thkukuk.de + + Version 1.1.2, 31. August 2010 + + + This manual documents what an application developer needs to know + about the Linux-PAM library. It + describes how an application might use the + Linux-PAM library to authenticate + users. In addition it contains a description of the functions + to be found in libpam_misc library, that can + be used in general applications. Finally, it contains some comments + on PAM related security issues for the application developer. + + + + + + Introduction +
+ Description + + Linux-PAM + (Pluggable Authentication Modules for Linux) is a library that enables + the local system administrator to choose how individual applications + authenticate users. For an overview of the + Linux-PAM library see the + Linux-PAM System Administrators' Guide. + + + It is the purpose of the Linux-PAM + project to liberate the development of privilege granting software + from the development of secure and appropriate authentication schemes. + This is accomplished by providing a documented library of functions + that an application may use for all forms of user authentication + management. This library dynamically loads locally configured + authentication modules that actually perform the authentication tasks. + + + From the perspective of an application developer the information + contained in the local configuration of the PAM library should not be + important. Indeed it is intended that an application treat the + functions documented here as a 'black box' that will deal with all + aspects of user authentication. 'All aspects' includes user + verification, account management, session initialization/termination + and also the resetting of passwords + (authentication tokens). + +
+ +
+ Synopsis + + For general applications that wish to use the services provided by + Linux-PAM the following is a summary + of the relevant linking information: + +#include <security/pam_appl.h> + +cc -o application .... -lpam + + + + In addition to libpam, there is a library of + miscellaneous functions that make the job of writing + PAM-aware applications easier (this library is not + covered in the DCE-RFC for PAM and is specific to the Linux-PAM + distribution): + +#include <security/pam_appl.h> +#include <security/pam_misc.h> + +cc -o application .... -lpam -lpam_misc + + +
+
+ + + Overview + + Most service-giving applications are restricted. In other words, + their service is not available to all and every prospective client. + Instead, the applying client must jump through a number of hoops to + convince the serving application that they are authorized to obtain + service. + + + The process of authenticating a client is what + PAM is designed to manage. In addition to authentication, PAM provides + account management, credential management, session management and + authentication-token (password changing) management services. It is + important to realize when writing a PAM based application that these + services are provided in a manner that is + transparent to the application. That is + to say, when the application is written, no assumptions can be made + about how the client will be authenticated. + + + The process of authentication is performed by the PAM library via a + call to pam_authenticate(). The return value + of this function will indicate whether a named client (the + user) has been authenticated. If the PAM library + needs to prompt the user for any information, such as their + name or a password + then it will do so. If the PAM library is configured to authenticate + the user using some silent protocol, it will do this too. (This + latter case might be via some hardware interface for example.) + + + It is important to note that the application must leave all decisions + about when to prompt the user at the discretion of the PAM library. + + + The PAM library, however, must work equally well for different styles + of application. Some applications, like the familiar + login and passwd are terminal + based applications, exchanges of information with the client in + these cases is as plain text messages. Graphically based applications, + however, have a more sophisticated interface. They generally interact + with the user via specially constructed dialogue boxes. Additionally, + network based services require that text messages exchanged with the + client are specially formatted for automated processing: one such + example is ftpd which prefixes each exchanged + message with a numeric identifier. + + + The presentation of simple requests to a client is thus something very + dependent on the protocol that the serving application will use. In + spite of the fact that PAM demands that it drives the whole + authentication process, it is not possible to leave such protocol + subtleties up to the PAM library. To overcome this potential problem, + the application provides the PAM library with a + conversation function. This function is called + from within the PAM library and enables the PAM + to directly interact with the client. The sorts of things that this + conversation function must be able to do are prompt the user with + text and/or obtain textual input from the user for processing by the + PAM library. The details of this function are provided in a later + section. + + + For example, the conversation function may be called by the PAM + library with a request to prompt the user for a password. Its job is + to reformat the prompt request into a form that the client will + understand. In the case of ftpd, this might involve + prefixing the string with the number 331 and sending + the request over the network to a connected client. The conversation + function will then obtain any reply and, after extracting the typed + password, will return this string of text to the PAM library. Similar + concerns need to be addressed in the case of an X-based graphical + server. + + + There are a number of issues that need to be addressed when one is + porting an existing application to become PAM compliant. A section + below has been devoted to this: Porting legacy applications. + + + Besides authentication, PAM provides other forms of management. + Session management is provided with calls to + pam_open_session() and + pam_close_session(). What these functions + actually do is up to the local administrator. But typically, they + could be used to log entry and exit from the system or for mounting + and unmounting the user's home directory. If an application provides + continuous service for a period of time, it should probably call + these functions, first open after the user is authenticated and then + close when the service is terminated. + + + Account management is another area that an application developer + should include with a call to pam_acct_mgmt(). + This call will perform checks on the good health of the user's account + (has it expired etc.). One of the things this function may check is + whether the user's authentication token has expired - in such a case the + application may choose to attempt to update it with a call to + pam_chauthtok(), although some applications + are not suited to this task (ftp for example) + and in this case the application should deny access to the user. + + + PAM is also capable of setting and deleting the user's credentials with + the call pam_setcred(). This function should + always be called after the user is authenticated and before service + is offered to the user. By convention, this should be the last call + to the PAM library before the PAM session is opened. What exactly a + credential is, is not well defined. However, some examples are given + in the glossary below. + + + + + + The public interface to <emphasis remap="B">Linux-PAM</emphasis> + + + Firstly, the relevant include file for the + Linux-PAM library is + <security/pam_appl.h>. + It contains the definitions for a number of functions. After + listing these functions, we collect some guiding remarks for + programmers. + +
+ What can be expected by the application + + + + + + + + + + + + + + + +
+
+ What is expected of an application + +
+
+ Programming notes + + Note, all of the authentication service function calls accept the + token PAM_SILENT, which instructs + the modules to not send messages to the application. This token + can be logically OR'd with any one of the permitted tokens specific + to the individual function calls. + PAM_SILENT does not override the + prompting of the user for passwords etc., it only stops informative + messages from being generated. + +
+
+ + + + Security issues of <emphasis remap="B">Linux-PAM</emphasis> + + + PAM, from the perspective of an application, is a convenient API for + authenticating users. PAM modules generally have no increased + privilege over that possessed by the application that is making use of + it. For this reason, the application must take ultimate responsibility + for protecting the environment in which PAM operates. + + + A poorly (or maliciously) written application can defeat any + Linux-PAM module's authentication + mechanisms by simply ignoring it's return values. It is the + applications task and responsibility to grant privileges and access + to services. The Linux-PAM library + simply assumes the responsibility of authenticating + the user; ascertaining that the user is who they + say they are. Care should be taken to anticipate all of the documented + behavior of the Linux-PAM library + functions. A failure to do this will most certainly lead to a future + security breach. + + +
+ Care about standard library calls + + In general, writers of authorization-granting applications should + assume that each module is likely to call any or + all 'libc' functions. For 'libc' functions + that return pointers to static/dynamically allocated structures + (ie. the library allocates the memory and the user is not expected + to 'free()' it) any module call to this + function is likely to corrupt a pointer previously + obtained by the application. The application programmer should + either re-call such a 'libc' function after a call to the + Linux-PAM library, or copy the + structure contents to some safe area of memory before passing + control to the Linux-PAM library. + + + Two important function classes that fall into this category are + + getpwnam3 + and + syslog3 + . + +
+ +
+ Choice of a service name + + When picking the service-name that + corresponds to the first entry in the + Linux-PAM configuration file, + the application programmer should avoid + the temptation of choosing something related to + argv[0]. It is a trivial matter for any user + to invoke any application on a system under a different name and + this should not be permitted to cause a security breach. + + + In general, this is always the right advice if the program is + setuid, or otherwise more privileged than the user that invokes + it. In some cases, avoiding this advice is convenient, but as an + author of such an application, you should consider well the ways + in which your program will be installed and used. (Its often the + case that programs are not intended to be setuid, but end up + being installed that way for convenience. If your program falls + into this category, don't fall into the trap of making this mistake.) + + + To invoke some target application by + another name, the user may symbolically link the target application + with the desired name. To be precise all the user need do is, + ln -s /target/application ./preferred_name + and then run ./preferred_name. + + + By studying the Linux-PAM + configuration file(s), an attacker can choose the + preferred_name to be that of a service enjoying + minimal protection; for example a game which uses + Linux-PAM to restrict access to + certain hours of the day. If the service-name were to be linked + to the filename under which the service was invoked, it + is clear that the user is effectively in the position of + dictating which authentication scheme the service uses. Needless + to say, this is not a secure situation. + + + The conclusion is that the application developer should carefully + define the service-name of an application. The safest thing is to + make it a single hard-wired name. + +
+ +
+ The conversation function + + Care should be taken to ensure that the conv() + function is robust. Such a function is provided in the library + libpam_misc (see + below). + +
+ +
+ The identity of the user + + The Linux-PAM modules will need + to determine the identity of the user who requests a service, + and the identity of the user who grants the service. These two + users will seldom be the same. Indeed there is generally a third + user identity to be considered, the new (assumed) identity of + the user once the service is granted. + + + The need for keeping tabs on these identities is clearly an + issue of security. One convention that is actively used by + some modules is that the identity of the user requesting a + service should be the current UID + (user ID) of the running process; the identity of the + privilege granting user is the EUID + (effective user ID) of the running process; the identity of + the user, under whose name the service will be executed, is + given by the contents of the PAM_USER + + pam_get_item3 + . Note, modules can change the values of + PAM_USER and PAM_RUSER + during any of the pam_*() library calls. + For this reason, the application should take care to use the + pam_get_item() every time it wishes to + establish who the authenticated user is (or will currently be). + + + For network-serving databases and other applications that provide + their own security model (independent of the OS kernel) the above + scheme is insufficient to identify the requesting user. + + + A more portable solution to storing the identity of the requesting + user is to use the PAM_RUSER + pam_get_item3 + . The application should supply this value before + attempting to authenticate the user with + pam_authenticate(). How well this name can be + trusted will ultimately be at the discretion of the local + administrator (who configures PAM for your application) and a + selected module may attempt to override the value where it can + obtain more reliable data. If an application is unable to determine + the identity of the requesting entity/user, it should not call + + pam_set_item3 + to set PAM_RUSER. + + + In addition to the PAM_RUSER item, the + application should supply the PAM_RHOST + (requesting host) item. As a general rule, + the following convention for its value can be assumed: + NULL = unknown; localhost = invoked directly from the local system; + other.place.xyz = some component of the + user's connection originates from this remote/requesting host. At + present, PAM has no established convention for indicating whether + the application supports a trusted path to communication from + this host. + +
+ +
+ Sufficient resources + + Care should be taken to ensure that the proper execution of an + application is not compromised by a lack of system resources. If an + application is unable to open sufficient files to perform its service, + it should fail gracefully, or request additional resources. + Specifically, the quantities manipulated by the + setrlimit2 + family of commands should be taken into consideration. + + + This is also true of conversation prompts. The application should not + accept prompts of arbitrary length with out checking for resource + allocation failure and dealing with such extreme conditions gracefully + and in a manner that preserves the PAM API. Such tolerance may be + especially important when attempting to track a malicious adversary. + +
+
+ + + A library of miscellaneous helper functions + + To aid the work of the application developer a library of + miscellaneous functions is provided. It is called + libpam_misc, and contains a text based + conversation function, and routines for enhancing the standard + PAM-environment variable support. + + + The functions, structures and macros, made available by this + library can be defined by including + <security/pam_misc.h>. It should be + noted that this library is specific to + Linux-PAM and is not referred to in + the defining DCE-RFC (see See also) + below. + +
+ Functions supplied + + + + +
+
+ + + Porting legacy applications + + The point of PAM is that the application is not supposed to + have any idea how the attached authentication modules will choose + to authenticate the user. So all they can do is provide a conversation + function that will talk directly to the user(client) on the modules' + behalf. + + + Consider the case that you plug a retinal scanner into the login + program. In this situation the user would be prompted: "please look + into the scanner". No username or password would be needed - all this + information could be deduced from the scan and a database lookup. The + point is that the retinal scanner is an ideal task for a "module". + + + While it is true that a pop-daemon program is designed with the POP + protocol in mind and no-one ever considered attaching a retinal + scanner to it, it is also the case that the "clean" PAM'ification of + such a daemon would allow for the possibility of a scanner module + being be attached to it. The point being that the "standard" + pop-authentication protocol(s) [which will be needed to satisfy + inflexible/legacy clients] would be supported by inserting an + appropriate pam_qpopper module(s). However, having rewritten + popd once in this way any new protocols can be + implemented in-situ. + + + One simple test of a ported application would be to insert the + pam_permit module and see if the application + demands you type a password... In such a case, xlock + would fail to lock the terminal - or would at best be a screen-saver, + ftp would give password free access to all etc.. Neither of + these is a very secure thing to do, but they do illustrate how + much flexibility PAM puts in the hands of the local admin. + + + The key issue, in doing things correctly, is identifying what is part + of the authentication procedure (how many passwords etc..) the + exchange protocol (prefixes to prompts etc., numbers like 331 in the + case of ftpd) and what is part of the service that the application + delivers. PAM really needs to have total control in the + authentication "procedure", the conversation function should only + deal with reformatting user prompts and extracting responses from raw + input. + + + + + Glossary of PAM related terms + + The following are a list of terms used within this document. + + + + Authentication token + + + Generally, this is a password. However, users can authenticate + themselves in a variety of ways. Updating the user's + authentication token thus corresponds to + refreshing the object they use to + authenticate themselves with the system. The word password is + avoided to keep open the possibility that the authentication + involves a retinal scan or other non-textual mode of + challenge/response. + + + + + Credentials + + + Having successfully authenticated the user, PAM is able to + establish certain characteristics/attributes of the user. + These are termed credentials. Examples + of which are group memberships to perform privileged tasks + with, and tickets in the form of + environment variables etc. . Some user-credentials, such as + the user's UID and GID (plus default group memberships) are + not deemed to be PAM-credentials. It is the responsibility + of the application to grant these directly. + + + + + + + + An example application + + To get a flavor of the way a Linux-PAM + application is written we include the following example. It prompts + the user for their password and indicates whether their account + is valid on the standard output, its return code also indicates + the success (0 for success; + 1 for failure). + + +/* + This program was contributed by Shane Watts + [modifications by AGM and kukuk] + + You need to add the following (or equivalent) to the + /etc/pam.d/check_user file: + # check authorization + auth required pam_unix.so + account required pam_unix.so + */ + +#include <security/pam_appl.h> +#include <security/pam_misc.h> +#include <stdio.h> + +static struct pam_conv conv = { + misc_conv, + NULL +}; + +int main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + int retval; + const char *user="nobody"; + + if(argc == 2) { + user = argv[1]; + } + + if(argc > 2) { + fprintf(stderr, "Usage: check_user [username]\n"); + exit(1); + } + + retval = pam_start("check_user", user, &conv, &pamh); + + if (retval == PAM_SUCCESS) + retval = pam_authenticate(pamh, 0); /* is user really user? */ + + if (retval == PAM_SUCCESS) + retval = pam_acct_mgmt(pamh, 0); /* permitted access? */ + + /* This is where we have been authorized or not. */ + + if (retval == PAM_SUCCESS) { + fprintf(stdout, "Authenticated\n"); + } else { + fprintf(stdout, "Not Authenticated\n"); + } + + if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */ + pamh = NULL; + fprintf(stderr, "check_user: failed to release authenticator\n"); + exit(1); + } + + return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */ +} + + + + + + Files + + + /usr/include/security/pam_appl.h + + + Header file with interfaces for + Linux-PAM applications. + + + + + /usr/include/security/pam_misc.h + + + Header file for useful library functions for making + applications easier to write. + + + + + + + + See also + + + + The Linux-PAM System Administrators' Guide. + + + + + The Linux-PAM Module Writers' Guide. + + + + + The V. Samar and R. Schemers (SunSoft), ``UNIFIED LOGIN WITH + PLUGGABLE AUTHENTICATION MODULES'', Open Software Foundation + Request For Comments 86.0, October 1995. + + + + + + + Author/acknowledgments + + This document was written by Andrew G. Morgan (morgan@kernel.org) + with many contributions from + Chris Adams, Peter Allgeyer, Tim Baverstock, Tim Berger, Craig S. Bell, + Derrick J. Brashear, Ben Buxton, Seth Chaiklin, Oliver Crow, Chris Dent, + Marc Ewing, Cristian Gafton, Emmanuel Galanos, Brad M. Garcia, + Eric Hester, Roger Hu, Eric Jacksch, Michael K. Johnson, David Kinchlea, + Olaf Kirch, Marcin Korzonek, Thorsten Kukuk, Stephen Langasek, + Nicolai Langfeldt, Elliot Lee, Luke Kenneth Casson Leighton, + Al Longyear, Ingo Luetkebohle, Marek Michalkiewicz, Robert Milkowski, + Aleph One, Martin Pool, Sean Reifschneider, Jan Rekorajski, Erik Troan, + Theodore Ts'o, Jeff Uphoff, Myles Uyema, Savochkin Andrey Vladimirovich, + Ronald Wahl, David Wood, John Wilmes, Joseph S. D. Yao + and Alex O. Yuriev. + + + Thanks are also due to Sun Microsystems, especially to Vipin Samar and + Charlie Lai for their advice. At an early stage in the development of + Linux-PAM, Sun graciously made the + documentation for their implementation of PAM available. This act + greatly accelerated the development of + Linux-PAM. + + + + + Copyright information for this document + +Copyright (c) 2006 Thorsten Kukuk <kukuk@thkukuk.de> +Copyright (c) 1996-2002 Andrew G. Morgan <morgan@kernel.org> + + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + +1. Redistributions of source code must retain the above copyright + notice, and the entire permission notice in its entirety, + including the disclaimer of warranties. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + + Alternatively, this product may be distributed under the terms of + the GNU General Public License (GPL), in which case the provisions + of the GNU GPL are required instead of the above restrictions. + (This clause is necessary due to a potential bad interaction between + the GNU GPL and the restrictions contained in a BSD-style copyright.) + + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + + +
diff --git a/doc/adg/html/meson.build b/doc/adg/html/meson.build new file mode 120000 index 0000000..d6d90ea --- /dev/null +++ b/doc/adg/html/meson.build @@ -0,0 +1 @@ +../../guide-html-meson.build \ No newline at end of file diff --git a/doc/adg/meson.build b/doc/adg/meson.build new file mode 120000 index 0000000..2e19d83 --- /dev/null +++ b/doc/adg/meson.build @@ -0,0 +1 @@ +../guide-meson.build \ No newline at end of file diff --git a/doc/adg/pam_acct_mgmt.xml b/doc/adg/pam_acct_mgmt.xml new file mode 100644 index 0000000..afcf2f2 --- /dev/null +++ b/doc/adg/pam_acct_mgmt.xml @@ -0,0 +1,12 @@ +
+ Account validation management + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_authenticate.xml b/doc/adg/pam_authenticate.xml new file mode 100644 index 0000000..aa36c68 --- /dev/null +++ b/doc/adg/pam_authenticate.xml @@ -0,0 +1,12 @@ +
+ Authenticating the user + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_chauthtok.xml b/doc/adg/pam_chauthtok.xml new file mode 100644 index 0000000..e6815dd --- /dev/null +++ b/doc/adg/pam_chauthtok.xml @@ -0,0 +1,12 @@ +
+ Updating authentication tokens + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_close_session.xml b/doc/adg/pam_close_session.xml new file mode 100644 index 0000000..ed83d7a --- /dev/null +++ b/doc/adg/pam_close_session.xml @@ -0,0 +1,12 @@ +
+ terminating PAM session management + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_conv.xml b/doc/adg/pam_conv.xml new file mode 100644 index 0000000..b2ba876 --- /dev/null +++ b/doc/adg/pam_conv.xml @@ -0,0 +1,29 @@ +
+ The conversation function + + + + +struct pam_message { + int msg_style; + const char *msg; +}; + +struct pam_response { + char *resp; + int resp_retcode; +}; + +struct pam_conv { + int (*conv)(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr); + void *appdata_ptr; +}; + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_end.xml b/doc/adg/pam_end.xml new file mode 100644 index 0000000..5e71925 --- /dev/null +++ b/doc/adg/pam_end.xml @@ -0,0 +1,12 @@ +
+ Termination of PAM transaction + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_fail_delay.xml b/doc/adg/pam_fail_delay.xml new file mode 100644 index 0000000..d602a1f --- /dev/null +++ b/doc/adg/pam_fail_delay.xml @@ -0,0 +1,12 @@ +
+ Request a delay on failure + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_get_item.xml b/doc/adg/pam_get_item.xml new file mode 100644 index 0000000..d12cb17 --- /dev/null +++ b/doc/adg/pam_get_item.xml @@ -0,0 +1,12 @@ +
+ Getting PAM items + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_getenv.xml b/doc/adg/pam_getenv.xml new file mode 100644 index 0000000..f7b483e --- /dev/null +++ b/doc/adg/pam_getenv.xml @@ -0,0 +1,12 @@ +
+ Get a PAM environment variable + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_getenvlist.xml b/doc/adg/pam_getenvlist.xml new file mode 100644 index 0000000..4433c04 --- /dev/null +++ b/doc/adg/pam_getenvlist.xml @@ -0,0 +1,12 @@ +
+ Getting the PAM environment + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_misc_conv.xml b/doc/adg/pam_misc_conv.xml new file mode 100644 index 0000000..4f54e11 --- /dev/null +++ b/doc/adg/pam_misc_conv.xml @@ -0,0 +1,9 @@ +
+ Text based conversation function + + + +
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_misc_drop_env.xml b/doc/adg/pam_misc_drop_env.xml new file mode 100644 index 0000000..cacb770 --- /dev/null +++ b/doc/adg/pam_misc_drop_env.xml @@ -0,0 +1,9 @@ +
+ Liberating a locally saved environment + + + +
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_misc_paste_env.xml b/doc/adg/pam_misc_paste_env.xml new file mode 100644 index 0000000..8ab2440 --- /dev/null +++ b/doc/adg/pam_misc_paste_env.xml @@ -0,0 +1,9 @@ +
+ Transcribing an environment to that of PAM + + + +
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_misc_setenv.xml b/doc/adg/pam_misc_setenv.xml new file mode 100644 index 0000000..7e8c489 --- /dev/null +++ b/doc/adg/pam_misc_setenv.xml @@ -0,0 +1,9 @@ +
+ BSD like PAM environment variable setting + + + +
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_open_session.xml b/doc/adg/pam_open_session.xml new file mode 100644 index 0000000..10afa75 --- /dev/null +++ b/doc/adg/pam_open_session.xml @@ -0,0 +1,12 @@ +
+ Start PAM session management + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_putenv.xml b/doc/adg/pam_putenv.xml new file mode 100644 index 0000000..6378a15 --- /dev/null +++ b/doc/adg/pam_putenv.xml @@ -0,0 +1,12 @@ +
+ Set or change PAM environment variable + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_set_item.xml b/doc/adg/pam_set_item.xml new file mode 100644 index 0000000..efc4292 --- /dev/null +++ b/doc/adg/pam_set_item.xml @@ -0,0 +1,12 @@ +
+ Setting PAM items + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_setcred.xml b/doc/adg/pam_setcred.xml new file mode 100644 index 0000000..488028c --- /dev/null +++ b/doc/adg/pam_setcred.xml @@ -0,0 +1,12 @@ +
+ Setting user credentials + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_start.xml b/doc/adg/pam_start.xml new file mode 100644 index 0000000..c7ee449 --- /dev/null +++ b/doc/adg/pam_start.xml @@ -0,0 +1,12 @@ +
+ Initialization of PAM transaction + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/adg/pam_strerror.xml b/doc/adg/pam_strerror.xml new file mode 100644 index 0000000..e4e1c56 --- /dev/null +++ b/doc/adg/pam_strerror.xml @@ -0,0 +1,12 @@ +
+ Strings describing PAM error codes + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/custom-man.xsl.in b/doc/custom-man.xsl.in new file mode 100644 index 0000000..258627b --- /dev/null +++ b/doc/custom-man.xsl.in @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/doc/guide-html-meson.build b/doc/guide-html-meson.build new file mode 100644 index 0000000..e5c9081 --- /dev/null +++ b/doc/guide-html-meson.build @@ -0,0 +1,32 @@ +# -*- mode: meson -*- + +html = custom_target( + input: xml, + output: name + '.html', + command: [ + prog_xsltproc, + '--nonet', + '--xinclude', + '--stringparam', 'base.dir', meson.current_build_dir(), + '--stringparam', 'root.filename', name, + '--stringparam', 'use.id.as.filename', '1', + '--stringparam', 'chunk.first.sections', '1', + '--stringparam', 'section.autolabel', '1', + '--stringparam', 'section.label.includes.component.label', '1', + '--stringparam', 'toc.max.depth', toc_max_depth, + '--stringparam', 'chunker.output.encoding', 'UTF-8', + html_stylesheet, + '@INPUT@', + ], + install: true, + install_dir: htmldir, + install_tag: 'doc', +) + +meson.add_install_script( + install_html, + meson.current_build_dir(), + htmldir, + html, + install_tag: 'doc', +) diff --git a/doc/guide-meson.build b/doc/guide-meson.build new file mode 100644 index 0000000..e29d16e --- /dev/null +++ b/doc/guide-meson.build @@ -0,0 +1,90 @@ +# -*- mode: meson -*- + +guide = fs.name(meson.current_source_dir()).to_upper() +name = 'Linux-PAM_' + guide + +xml = files(name + '.xml') + +if guide == 'SAG' + toc_max_depth = '2' +else + toc_max_depth = '3' +endif + + +run_command( + [prog_xmllint, + '--noent', + '--nonet', + '--noout', + '--xinclude', + '--relaxng', docbook_rng, + xml], + check: true, +) + + +html = custom_target( + input: xml, + output: name + '.html', + command: [ + prog_xsltproc, + '-o', '@OUTPUT@', + '--nonet', + '--xinclude', + '--stringparam', 'generate.toc', 'book toc', + '--stringparam', 'section.autolabel', '1', + '--stringparam', 'section.label.includes.component.label', '1', + '--stringparam', 'toc.max.depth', toc_max_depth, + txt_stylesheet, + '@INPUT@', + ], +) + +custom_target( + input: html, + output: name + '.txt', + command: [ + redir_exe, + '@INPUT@', + '@OUTPUT@', + browser, + ], + install: true, + install_dir: docdir, + install_tag: 'doc', +) + + +fop = custom_target( + input: xml, + output: name + '.fop', + command: [ + prog_xsltproc, + '-o', '@OUTPUT@', + '--nonet', + '--xinclude', + '--stringparam', 'generate.toc', 'book toc', + '--stringparam', 'section.autolabel', '1', + '--stringparam', 'section.label.includes.component.label', '1', + '--stringparam', 'toc.max.depth', toc_max_depth, + pdf_stylesheet, + '@INPUT@', + ], +) + +custom_target( + input: fop, + output: name + '.pdf', + command: [ + prog_fop, + '@INPUT@', + '@OUTPUT@', + ], + install: true, + install_dir: pdfdir, + install_tag: 'doc', +) + + +subdir('html') diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..9afc8b7 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,21 @@ + + + The Linux-PAM Administration and Developer Guides + + +
+

The Linux-PAM Guides

+
+
+

+ Here is the documentation for Linux-PAM. As you will see it is + currently not complete. +

+

+
+ + \ No newline at end of file diff --git a/doc/install-html.sh b/doc/install-html.sh new file mode 100755 index 0000000..884fc27 --- /dev/null +++ b/doc/install-html.sh @@ -0,0 +1,7 @@ +#!/bin/sh -eu + +cd "$1"; shift +MESON_INSTALL_DESTDIR=${MESON_INSTALL_DESTDIR_PREFIX%$MESON_INSTALL_PREFIX} +dest="$MESON_INSTALL_DESTDIR$1"; shift + +install -p -m644 -t "$dest" -- *.html diff --git a/doc/man/meson.build b/doc/man/meson.build new file mode 100644 index 0000000..b90b157 --- /dev/null +++ b/doc/man/meson.build @@ -0,0 +1,67 @@ +foreach man: [['misc_conv.3', []], + ['pam.3', []], + ['pam_acct_mgmt.3', []], + ['pam_authenticate.3', []], + ['pam_chauthtok.3', []], + ['pam_close_session.3', []], + ['pam_conv.3', []], + ['pam_end.3', []], + ['pam_error.3', ['pam_verror.3']], + ['pam_fail_delay.3', []], + ['pam_get_authtok.3', ['pam_get_authtok_noverify.3', 'pam_get_authtok_verify.3']], + ['pam_get_data.3', []], + ['pam_get_item.3', []], + ['pam_get_user.3', []], + ['pam_getenv.3', []], + ['pam_getenvlist.3', []], + ['pam_info.3', ['pam_vinfo.3']], + ['pam_misc_drop_env.3', []], + ['pam_misc_paste_env.3', []], + ['pam_misc_setenv.3', []], + ['pam_open_session.3', []], + ['pam_prompt.3', ['pam_vprompt.3']], + ['pam_putenv.3', []], + ['pam_set_data.3', []], + ['pam_set_item.3', []], + ['pam_setcred.3', []], + ['pam_sm_acct_mgmt.3', []], + ['pam_sm_authenticate.3', []], + ['pam_sm_chauthtok.3', []], + ['pam_sm_close_session.3', []], + ['pam_sm_open_session.3', []], + ['pam_sm_setcred.3', []], + ['pam_start.3', []], + ['pam_strerror.3', []], + ['pam_syslog.3', ['pam_vsyslog.3']], + ['pam_xauth_data.3', []], + ['pam.conf.5', ['pam.d.5']], + ['pam.8', ['PAM.8']], + ] + xml = man[0] + '.xml' + + run_command([prog_xmllint, + '--nonet', + '--noout', + '--xinclude', + '--relaxng', docbook_rng, + xml], + check: true) + + custom_target(man[0], + input: xml, + output: man, + depends: custom_man_xsl, + command: [prog_xsltproc, + '-o', '@OUTPUT0@', + '--nonet', + '--xinclude', + '--path', meson.current_source_dir(), + stringparam_vendordir, + stringparam_profileconditions, + custom_man_xsl, + '@INPUT@'], + install: true, + install_dir: mandir / 'man' + man[0].substring(-1), + install_tag: 'man', + ) +endforeach diff --git a/doc/man/misc_conv.3.xml b/doc/man/misc_conv.3.xml new file mode 100644 index 0000000..92d4acd --- /dev/null +++ b/doc/man/misc_conv.3.xml @@ -0,0 +1,185 @@ + + + + misc_conv + 3 + Linux-PAM + Linux-PAM Manual + + + + misc_conv + text based conversation function + + + + + + + #include <security/pam_misc.h> + + int misc_conv + int num_msg + const struct pam_message **msgm + struct pam_response **response + void *appdata_ptr + + + + + + DESCRIPTION + + The misc_conv function is part of + libpam_misc and not of the standard + libpam library. This function will prompt + the user with the appropriate comments and obtain the appropriate + inputs as directed by authentication modules. + + + In addition to simply slotting into the appropriate + pam_conv3 + , this function provides some time-out facilities. + The function exports five variables that can be used by an + application programmer to limit the amount of time this conversation + function will spend waiting for the user to type something. The + five variables are as follows: + + + + time_t pam_misc_conv_warn_time; + + + This variable contains the time (as + returned by + time2 + ) that the user should be first warned that + the clock is ticking. By default it has the value + 0, which indicates that no such + warning will be given. The application may set its value to + sometime in the future, but this should be done prior to + passing control to the Linux-PAM library. + + + + + + const char *pam_misc_conv_warn_line; + + + Used in conjunction with + pam_misc_conv_warn_time, this variable is + a pointer to the string that will be displayed when it becomes + time to warn the user that the timeout is approaching. Its + default value is a translated version of + ...Time is running out..., but this can be + changed by the application prior to passing control to + Linux-PAM. + + + + + + time_t pam_misc_conv_die_time; + + + This variable contains the time (as + returned by + time2 + ) that the will time out. By default it has + the value 0, which indicates that + the conversation function will not timeout. The application + may set its value to sometime in the future, but this should + be done prior to passing control to the + Linux-PAM library. + + + + + + const char *pam_misc_conv_die_line; + + + Used in conjunction with + pam_misc_conv_die_time, this variable is + a pointer to the string that will be displayed when the + conversation times out. Its default value is a translated + version of + ...Sorry, your time is up!, but this can be + changed by the application prior to passing control to + Linux-PAM. + + + + + + int pam_misc_conv_died; + + + Following a return from the Linux-PAM + library, the value of this variable indicates whether the + conversation has timed out. A value of + 1 indicates the time-out occurred. + + + + + + The following two function pointers are available for supporting + binary prompts in the conversation function. They are optimized + for the current incarnation of the libpamc + library and are subject to change. + + + + + int (*pam_binary_handler_fn)(void *appdata, pamc_bp_t *prompt_p); + + + + This function pointer is initialized to + NULL but can be filled with a + function that provides machine-machine (hidden) message + exchange. It is intended for use with hidden authentication + protocols such as RSA or Diffie-Hellman key exchanges. + (This is still under development.) + + + + + + int (*pam_binary_handler_free)(void *appdata, pamc_bp_t *delete_me); + + + + This function pointer is initialized to + PAM_BP_RENEW(delete_me, 0, 0), but can be + redefined as desired by the application. + + + + + + + + SEE ALSO + + + pam_conv3 + , + + pam8 + + + + + + STANDARDS + + The misc_conv function is part of the + libpam_misc Library and not defined in any + standard. + + + + \ No newline at end of file diff --git a/doc/man/pam.3.xml b/doc/man/pam.3.xml new file mode 100644 index 0000000..a7d13b4 --- /dev/null +++ b/doc/man/pam.3.xml @@ -0,0 +1,437 @@ + + + + pam + 3 + Linux-PAM + Linux-PAM Manual + + + + pam + Pluggable Authentication Modules Library + + + + + #include <security/pam_appl.h> + #include <security/pam_modules.h> + #include <security/pam_ext.h> + + + + + DESCRIPTION + + PAM is a system of libraries + that handle the authentication tasks of applications (services) + on the system. The library provides a stable general interface + (Application Programming Interface - API) that privilege granting + programs (such as + + login1 + and + su1 + ) + defer to to perform standard authentication tasks. + + + + Initialization and Cleanup + + The + + pam_start3 + function creates the PAM context and initiates the + PAM transaction. It is the first of the PAM functions that needs to + be called by an application. The transaction state is contained + entirely within the structure identified by this handle, so it is + possible to have multiple transactions in parallel. But it is not + possible to use the same handle for different transactions, a new + one is needed for every new context. + + + The + + pam_end3 + function terminates the PAM transaction and is the last + function an application should call in the PAM context. Upon return + the handle pamh is no longer valid and all memory associated with it + will be invalid. It can be called at any time to terminate a PAM + transaction. + + + + + Authentication + + The + + pam_authenticate3 + + function is used to + authenticate the user. The user is required to provide an + authentication token depending upon the authentication service, + usually this is a password, but could also be a finger print. + + + The + + pam_setcred3 + + function manages the user's credentials. + + + + + Account Management + + The + + pam_acct_mgmt3 + function is used to determine if the user's account is + valid. It checks for authentication token and account expiration and + verifies access restrictions. It is typically called after the user + has been authenticated. + + + + + Password Management + + The + + pam_chauthtok3 + function is used to change the authentication token + for a given user on request or because the token has expired. + + + + + Session Management + + The + + pam_open_session3 + function sets up a user session for a previously + successful authenticated user. The session should later be terminated + with a call to + + pam_close_session3 + . + + + + + Conversation + + The PAM library uses an application-defined callback to allow + a direct communication between a loaded module and the application. + This callback is specified by the + struct pam_conv passed to + + pam_start3 + at the start of the transaction. See + + pam_conv3 + + for details. + + + + + Data Objects + + The + + pam_set_item3 + + and + + pam_get_item3 + + functions allow applications and PAM service modules to set and + retrieve PAM information. + + + The + + pam_get_user3 + + function is the preferred method to obtain the username. + + + The + + pam_set_data3 + + and + + pam_get_data3 + + function allows PAM service modules to set and retrieve free-form + data from one invocation to another. + + + + + Environment and Error Management + + The + + pam_putenv3 + , + + pam_getenv3 + and + + pam_getenvlist3 + + functions are for maintaining a set of private environment variables. + + + + The + + pam_strerror3 + function returns a pointer to a string describing the + given PAM error code. + + + + + + RETURN VALUES + + The following return codes are known by PAM: + + + + PAM_ABORT + + Critical error, immediate abort. + + + + PAM_ACCT_EXPIRED + + User account has expired. + + + + PAM_AUTHINFO_UNAVAIL + + + Authentication service cannot retrieve authentication info. + + + + + PAM_AUTHTOK_DISABLE_AGING + + Authentication token aging disabled. + + + + PAM_AUTHTOK_ERR + + Authentication token manipulation error. + + + + PAM_AUTHTOK_EXPIRED + + Authentication token expired. + + + + PAM_AUTHTOK_LOCK_BUSY + + Authentication token lock busy. + + + + PAM_AUTHTOK_RECOVERY_ERR + + Authentication information cannot be recovered. + + + + PAM_AUTH_ERR + + Authentication failure. + + + + PAM_BUF_ERR + + Memory buffer error. + + + + PAM_CONV_ERR + + Conversation failure. + + + + PAM_CRED_ERR + + Failure setting user credentials. + + + + PAM_CRED_EXPIRED + + User credentials expired. + + + + PAM_CRED_INSUFFICIENT + + Insufficient credentials to access authentication data. + + + + PAM_CRED_UNAVAIL + + Authentication service cannot retrieve user credentials. + + + + PAM_IGNORE + + The return value should be ignored by PAM dispatch. + + + + PAM_MAXTRIES + + Have exhausted maximum number of retries for service. + + + + PAM_MODULE_UNKNOWN + + Module is unknown. + + + + PAM_NEW_AUTHTOK_REQD + + + Authentication token is no longer valid; new one required. + + + + + PAM_NO_MODULE_DATA + + No module specific data is present. + + + + PAM_OPEN_ERR + + Failed to load module. + + + + PAM_PERM_DENIED + + Permission denied. + + + + PAM_SERVICE_ERR + + Error in service module. + + + + PAM_SESSION_ERR + + Cannot make/remove an entry for the specified session. + + + + PAM_SUCCESS + + Success. + + + + PAM_SYMBOL_ERR + + Symbol not found. + + + + PAM_SYSTEM_ERR + + System error. + + + + PAM_TRY_AGAIN + + Failed preliminary check by password service. + + + + PAM_USER_UNKNOWN + + User not known to the underlying authentication module. + + + + + + SEE ALSO + + + pam_acct_mgmt3 + , + pam_authenticate3 + , + pam_chauthtok3 + , + pam_close_session3 + , + pam_conv3 + , + pam_end3 + , + pam_get_data3 + , + pam_getenv3 + , + pam_getenvlist3 + , + pam_get_item3 + , + pam_get_user3 + , + pam_open_session3 + , + pam_putenv3 + , + pam_set_data3 + , + pam_set_item3 + , + pam_setcred3 + , + pam_start3 + , + pam_strerror3 + + + + NOTES + + The libpam interfaces are only thread-safe if each + thread within the multithreaded application uses its own PAM handle. + + + \ No newline at end of file diff --git a/doc/man/pam.8.xml b/doc/man/pam.8.xml new file mode 100644 index 0000000..7f3b051 --- /dev/null +++ b/doc/man/pam.8.xml @@ -0,0 +1,212 @@ + + + + pam + 8 + Linux-PAM + Linux-PAM Manual + + + + PAM + pam + Pluggable Authentication Modules for Linux + + + + DESCRIPTION + + This manual is intended to offer a quick introduction to + Linux-PAM. For more information + the reader is directed to the + Linux-PAM system administrators' guide. + + + + Linux-PAM is a system of libraries + that handle the authentication tasks of applications (services) on + the system. The library provides a stable general interface + (Application Programming Interface - API) that privilege granting + programs (such as + login1 + and + su1 + ) defer to to perform standard authentication tasks. + + + + The principal feature of the PAM approach is that the nature of the + authentication is dynamically configurable. In other words, the + system administrator is free to choose how individual + service-providing applications will authenticate users. This dynamic + configuration is set by the contents of the single + Linux-PAM configuration file + /etc/pam.conf. Alternatively and preferably, + the configuration can be set by individual configuration files + located in a pam.d directory. The presence of this + directory will cause Linux-PAM to + ignore /etc/pam.conf. + + + + Vendor-supplied PAM configuration files might be installed in + the system directory /usr/lib/pam.d/ or + a configurable vendor specific directory instead + of the machine configuration directory /etc/pam.d/. + If no machine configuration file is found, the vendor-supplied file + is used. All files in /etc/pam.d/ override + files with the same name in other directories. + + +From the point of view of the system administrator, for whom this +manual is provided, it is not of primary importance to understand the +internal behavior of the +Linux-PAM +library. The important point to recognize is that the configuration +file(s) +define +the connection between applications +(services) +and the pluggable authentication modules +(PAMs) +that perform the actual authentication tasks. + + +Linux-PAM +separates the tasks of +authentication +into four independent management groups: +account management; +authentication management; +password management; +and +session management. +(We highlight the abbreviations used for these groups in the +configuration file.) + + +Simply put, these groups take care of different aspects of a typical +user's request for a restricted service: + + +account - +provide account verification types of service: has the user's password +expired?; is this user permitted access to the requested service? + + +authentication - +authenticate a user and set up user credentials. Typically this is via +some challenge-response request that the user must satisfy: if you are +who you claim to be please enter your password. Not all authentications +are of this type, there exist hardware based authentication schemes +(such as the use of smart-cards and biometric devices), with suitable +modules, these may be substituted seamlessly for more standard +approaches to authentication - such is the flexibility of +Linux-PAM. + + +password - +this group's responsibility is the task of updating authentication +mechanisms. Typically, such services are strongly coupled to those of +the +auth +group. Some authentication mechanisms lend themselves well to being +updated with such a function. Standard UN*X password-based access is +the obvious example: please enter a replacement password. + + +session - +this group of tasks cover things that should be done prior to a +service being given and after it is withdrawn. Such tasks include the +maintenance of audit trails and the mounting of the user's home +directory. The +session +management group is important as it provides both an opening and +closing hook for modules to affect the services available to a user. + + + + + FILES + + + /etc/pam.conf + + the configuration file + + + + /etc/pam.d + + + the Linux-PAM configuration + directory. Generally, if this directory is present, the + /etc/pam.conf file is ignored. + + + + + /usr/lib/pam.d + + + the Linux-PAM vendor configuration + directory. Files in /etc/pam.d override + files with the same name in this directory. + + + + + %vendordir%/pam.d + + + additional Linux-PAM vendor + configuration directory. Files in /etc/pam.d + and /usr/lib/pam.d override files with the + same name in this directory. + + + + + + + + ERRORS + + Typically errors generated by the + Linux-PAM system of libraries, will + be written to + syslog3 + . + + + + + CONFORMING TO + + DCE-RFC 86.0, October 1995. + Contains additional features, but remains backwardly compatible + with this RFC. + + + + + SEE ALSO + + + pam3 + , + + pam_authenticate3 + , + + pam_sm_setcred3 + , + + pam_strerror3 + , + + PAM8 + + + + diff --git a/doc/man/pam.conf-desc.xml b/doc/man/pam.conf-desc.xml new file mode 100644 index 0000000..60c2d71 --- /dev/null +++ b/doc/man/pam.conf-desc.xml @@ -0,0 +1,19 @@ +
+ + When a PAM aware privilege granting application + is started, it activates its attachment to the PAM-API. This + activation performs a number of tasks, the most important being the + reading of the configuration file(s): /etc/pam.conf. + Alternatively and preferably, the configuration can be set by individual + configuration files located in a pam.d directory. + The presence of this directory will cause + Linux-PAM to + ignore /etc/pam.conf. + + + These files list the PAMs that will do the + authentication tasks required by this service, and the appropriate + behavior of the PAM-API in the event that individual + PAMs fail. + +
diff --git a/doc/man/pam.conf-dir.xml b/doc/man/pam.conf-dir.xml new file mode 100644 index 0000000..57b2991 --- /dev/null +++ b/doc/man/pam.conf-dir.xml @@ -0,0 +1,37 @@ +
+ + More flexible than the single configuration file is it to + configure libpam via the contents of + pam.d directories. In this case the + directories are filled with files each of which has a filename + equal to a service-name (in lower-case): it is the personal + configuration file for the named service. + + + + Vendor-supplied PAM configuration files might be installed in + the system directory /usr/lib/pam.d/ or + a configurable vendor specific directory instead + of the machine configuration directory /etc/pam.d/. + If no machine configuration file is found, the vendor-supplied file + is used. All files in /etc/pam.d/ override + files with the same name in other directories. + + + + The syntax of each file in pam.d is similar to that of the + /etc/pam.conf file and is made up of lines + of the following form: + + + +type control module-path module-arguments + + + + The only difference being that the service-name is not present. The + service-name is of course the name of the given configuration file. + For example, /etc/pam.d/login contains the + configuration for the login service. + +
diff --git a/doc/man/pam.conf-syntax.xml b/doc/man/pam.conf-syntax.xml new file mode 100644 index 0000000..c7d9008 --- /dev/null +++ b/doc/man/pam.conf-syntax.xml @@ -0,0 +1,423 @@ +
+ + The syntax of the /etc/pam.conf + configuration file is as follows. The file is made up of a list + of rules, each rule is typically placed on a single line, + but may be extended with an escaped end of line: `\<LF>'. + Comments are preceded with `#' marks and extend to the next end of + line. + + + + The format of each rule is a space separated collection of tokens, + the first three being case-insensitive: + + + + service type control module-path module-arguments + + + + The syntax of files contained in the /etc/pam.d/ + directory, are identical except for the absence of any + service field. In this case, the + service is the name of the file in the + /etc/pam.d/ directory. This filename must be + in lower case. + + + + An important feature of PAM, is that a + number of rules may be stacked to combine + the services of a number of PAMs for a given authentication task. + + + + The service is typically the familiar name of + the corresponding application: login and + su are good examples. The + service-name, other, + is reserved for giving default rules. + Only lines that mention the current service (or in the absence + of such, the other entries) will be associated + with the given service-application. + + + + The type is the management group that the rule + corresponds to. It is used to specify which of the management groups + the subsequent module is to be associated with. Valid entries are: + + + + account + + + this module type performs non-authentication based account + management. It is typically used to restrict/permit access + to a service based on the time of day, currently available + system resources (maximum number of users) or perhaps the + location of the applicant user -- 'root' login only on the + console. + + + + + auth + + + this module type provides two aspects of authenticating + the user. Firstly, it establishes that the user is who they + claim to be, by instructing the application to prompt the user + for a password or other means of identification. Secondly, the + module can grant group membership or other privileges through + its credential granting properties. + + + + + password + + + this module type is required for updating the authentication + token associated with the user. Typically, there is one module + for each 'challenge/response' based authentication (auth) type. + + + + + session + + + this module type is associated with doing things that need to + be done for the user before/after they can be given service. + Such things include the logging of information concerning the + opening/closing of some data exchange with a user, mounting + directories, etc. + + + + + + If the type value from the list above is prepended + with a - character the PAM library will not log to + the system log if it is not possible to load the module because it is + missing in the system. This can be useful especially for modules which + are not always installed on the system and are not required for correct + authentication and authorization of the login session. + + + + The third field, control, indicates the + behavior of the PAM-API should the module fail to succeed in its + authentication task. There are two types of syntax for this control + field: the simple one has a single simple keyword; the more + complicated one involves a square-bracketed selection of + value=action pairs. + + + + For the simple (historical) syntax valid control + values are: + + + + required + + + failure of such a PAM will ultimately lead to the PAM-API + returning failure but only after the remaining + stacked modules (for this + service and type) + have been invoked. + + + + + requisite + + + like required, however, in the case that + such a module returns a failure, control is directly returned + to the application or to the superior PAM stack. + The return value is that associated with + the first required or requisite module to fail. Note, this flag + can be used to protect against the possibility of a user getting + the opportunity to enter a password over an unsafe medium. It is + conceivable that such behavior might inform an attacker of valid + accounts on a system. This possibility should be weighed against + the not insignificant concerns of exposing a sensitive password + in a hostile environment. + + + + + sufficient + + + if such a module succeeds and no prior required + module has failed the PAM framework returns success to + the application or to the superior PAM stack immediately without + calling any further modules in the stack. A failure of a + sufficient module is ignored and processing + of the PAM module stack continues unaffected. + + + + + optional + + + the success or failure of this module is only important if + it is the only module in the stack associated with this + service+type. + + + + + include + + + include all lines of given type from the configuration + file specified as an argument to this control. + + + + + substack + + + include all lines of given type from the configuration + file specified as an argument to this control. This differs from + include in that evaluation of the + done and die actions + in a substack does not cause skipping the rest of the complete + module stack, but only of the substack. Jumps in a substack + also can not make evaluation jump out of it, and the whole substack + is counted as one module when the jump is done in a parent stack. + The reset action will reset the state of a + module stack to the state it was in as of beginning of the substack + evaluation. + + + + + + + For the more complicated syntax valid control + values have the following form: + + + [value1=action1 value2=action2 ...] + + + + Where valueN corresponds to the return code + from the function invoked in the module for which the line is + defined. It is selected from one of these: + success, open_err, + symbol_err, service_err, + system_err, buf_err, + perm_denied, auth_err, + cred_insufficient, + authinfo_unavail, + user_unknown, maxtries, + new_authtok_reqd, + acct_expired, session_err, + cred_unavail, cred_expired, + cred_err, no_module_data, + conv_err, authtok_err, + authtok_recover_err, + authtok_lock_busy, + authtok_disable_aging, + try_again, ignore, + abort, authtok_expired, + module_unknown, bad_item, + conv_again, incomplete, + and default. + + + The last of these, default, implies 'all + valueN's not mentioned explicitly. Note, the + full list of PAM errors is available in + /usr/include/security/_pam_types.h. The + actionN can take one of the following forms: + + + + ignore + + + when used with a stack of modules, the module's return + status will not contribute to the return code the application + obtains. + + + + + bad + + + this action indicates that the return code should be thought + of as indicative of the module failing. If this module is the + first in the stack to fail, its status value will be used for + that of the whole stack. This is the default action for + all return codes. + + + + + die + + + equivalent to bad with the side effect of + terminating the module stack and PAM immediately returning to + the application. + + + + + ok + + + this tells PAM that the administrator thinks this return code + should contribute directly to the return code of the full + stack of modules. In other words, if the former state of the + stack would lead to a return of PAM_SUCCESS, + the module's return code will override this value. Note, if + the former state of the stack holds some value that is + indicative of a modules failure, this 'ok' value will not be + used to override that value. + + + + + done + + + equivalent to ok with the side effect of + terminating the module stack and PAM immediately returning to the + application unless there was a non-ignored module failure before. + + + + + N (an unsigned integer) + + + jump over the next N modules in the stack. + Note that N equal to 0 is not allowed, + it would be treated as ignore in such case. + The side effect depends on the PAM function call: + for pam_authenticate, + pam_acct_mgmt, + pam_chauthtok, and + pam_open_session + it is ignore; + for pam_setcred and + pam_close_session it is + one of ignore, ok, + or bad depending on the module's return value. + + + + + reset + + + clear all memory of the state of the module stack and + start again with the next stacked module. + + + + + + + If a return code's action is not specifically defined via a + valueN token, and the + default value is not specified, that return + code's action defaults to bad. + + + + Each of the four keywords: required; requisite; sufficient; and + optional, have an equivalent expression in terms of the [...] + syntax. They are as follows: + + + + required + + + [success=ok new_authtok_reqd=ok ignore=ignore default=bad] + + + + + requisite + + + [success=ok new_authtok_reqd=ok ignore=ignore default=die] + + + + + sufficient + + + [success=done new_authtok_reqd=done default=ignore] + + + + + optional + + + [success=ok new_authtok_reqd=ok default=ignore] + + + + + + + module-path is either the full filename + of the PAM to be used by the application (it begins with a '/'), + or a relative pathname from the default module location: + /lib/security/ or + /lib64/security/, depending on the architecture. + + + + module-arguments are a space separated list + of tokens that can be used to modify the specific behavior of the + given PAM. Such arguments will be documented for each individual + module. Note, if you wish to include spaces in an argument, you + should surround that argument with square brackets. + + + squid auth required pam_mysql.so user=passwd_query passwd=mada \ + db=eminence [query=select user_name from internet_service \ + where user_name='%u' and password=PASSWORD('%p') and \ + service='web_proxy'] + + + When using this convention, you can include `[' characters + inside the string, and if you wish to include a `]' character + inside the string that will survive the argument parsing, you + should use `\]'. In other words: + + + [..[..\]..] --> ..[..].. + + + + Any line in (one of) the configuration file(s), that is not formatted + correctly, will generally tend (erring on the side of caution) to make + the authentication process fail. A corresponding error is written to + the system log files with a call to + + syslog3 + . + + +
\ No newline at end of file diff --git a/doc/man/pam.conf.5.xml b/doc/man/pam.conf.5.xml new file mode 100644 index 0000000..997776b --- /dev/null +++ b/doc/man/pam.conf.5.xml @@ -0,0 +1,85 @@ + + + + pam.conf + 5 + Linux-PAM + Linux-PAM Manual + + + + pam.conf + pam.d + PAM configuration files + + + + + + DESCRIPTION + + + + + + + + + FILES + + + /etc/pam.conf + + the configuration file + + + + /etc/pam.d + + + the Linux-PAM configuration + directory. Generally, if this directory is present, the + /etc/pam.conf file is ignored. + + + + + /usr/lib/pam.d + + + the Linux-PAM vendor configuration + directory. Files in /etc/pam.d override + files with the same name in this directory. + + + + + %vendordir%/pam.d + + + additional Linux-PAM vendor + configuration directory. Files in /etc/pam.d + and /usr/lib/pam.d override files with the + same name in this directory. + + + + + + + + SEE ALSO + + + pam3 + , + + PAM8 + , + + pam_start3 + + + + + diff --git a/doc/man/pam_acct_mgmt.3.xml b/doc/man/pam_acct_mgmt.3.xml new file mode 100644 index 0000000..de6a94a --- /dev/null +++ b/doc/man/pam_acct_mgmt.3.xml @@ -0,0 +1,143 @@ + + + pam_acct_mgmt + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_acct_mgmt + PAM account validation management + + + + + + + #include <security/pam_appl.h> + + int pam_acct_mgmt + pam_handle_t *pamh + int flags + + + + + + + DESCRIPTION + + The pam_acct_mgmt function is used to determine + if the user's account is valid. It checks for authentication token + and account expiration and verifies access restrictions. It is + typically called after the user has been authenticated. + + + The pamh argument is an authentication + handle obtained by a prior call to pam_start(). + The flags argument is the binary or of zero or more of the + following values: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + PAM_DISALLOW_NULL_AUTHTOK + + + The PAM module service should return PAM_NEW_AUTHTOK_REQD + if the user has a null authentication token. + + + + + + + + RETURN VALUES + + + PAM_ACCT_EXPIRED + + + User account has expired. + + + + + PAM_AUTH_ERR + + + Authentication failure. + + + + + PAM_NEW_AUTHTOK_REQD + + + The user account is valid but their authentication token + is expired. The correct response to + this return-value is to require that the user satisfies + the pam_chauthtok() function before + obtaining service. It may not be possible for some + applications to do this. In such cases, the user should be + denied access until such time as they can update their password. + + + + + PAM_PERM_DENIED + + + Permission denied. + + + + + PAM_SUCCESS + + + The authentication token was successfully updated. + + + + + PAM_USER_UNKNOWN + + + User unknown to password service. + + + + + + + + SEE ALSO + + + pam_start3 + , + + pam_authenticate3 + , + + pam_chauthtok3 + , + + pam_strerror3 + , + + pam8 + + + + \ No newline at end of file diff --git a/doc/man/pam_authenticate.3.xml b/doc/man/pam_authenticate.3.xml new file mode 100644 index 0000000..794a5c7 --- /dev/null +++ b/doc/man/pam_authenticate.3.xml @@ -0,0 +1,167 @@ + + + pam_authenticate + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_authenticate + account authentication + + + + + + + #include <security/pam_appl.h> + + int pam_authenticate + pam_handle_t *pamh + int flags + + + + + + + DESCRIPTION + + The pam_authenticate function is used to + authenticate the user. The user is required to provide an + authentication token depending upon the authentication service, + usually this is a password, but could also be a finger print. + + + The PAM service module may request that the user enter their + username via the conversation mechanism (see + + pam_start3 + and + + pam_conv3 + ). The name of the authenticated user + will be present in the PAM item PAM_USER. This item may be + recovered with a call to + + pam_get_item3 + . + + + The pamh argument is an authentication + handle obtained by a prior call to pam_start(). + The flags argument is the binary or of zero or more of the + following values: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + PAM_DISALLOW_NULL_AUTHTOK + + + The PAM module service should return PAM_AUTH_ERR + if the user does not have a registered authentication token. + + + + + + + + RETURN VALUES + + + PAM_ABORT + + + The application should exit immediately after calling + + pam_end3 + first. + + + + + PAM_AUTH_ERR + + + The user was not authenticated. + + + + + PAM_CRED_INSUFFICIENT + + + For some reason the application does not have sufficient + credentials to authenticate the user. + + + + + PAM_AUTHINFO_UNAVAIL + + + The modules were not able to access the authentication + information. This might be due to a network or hardware + failure etc. + + + + + PAM_MAXTRIES + + + One or more of the authentication modules has reached its + limit of tries authenticating the user. Do not try again. + + + + + PAM_SUCCESS + + + The user was successfully authenticated. + + + + + PAM_USER_UNKNOWN + + + User unknown to authentication service. + + + + + + + + SEE ALSO + + + pam_start3 + , + + pam_setcred3 + , + + pam_chauthtok3 + , + + pam_strerror3 + , + + pam8 + + + + \ No newline at end of file diff --git a/doc/man/pam_chauthtok.3.xml b/doc/man/pam_chauthtok.3.xml new file mode 100644 index 0000000..e184f45 --- /dev/null +++ b/doc/man/pam_chauthtok.3.xml @@ -0,0 +1,162 @@ + + + pam_chauthtok + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_chauthtok + updating authentication tokens + + + + + + + #include <security/pam_appl.h> + + int pam_chauthtok + pam_handle_t *pamh + int flags + + + + + + + DESCRIPTION + + The pam_chauthtok function is used to change the + authentication token for a given user (as indicated by the state + associated with the handle pamh). + + + The pamh argument is an authentication + handle obtained by a prior call to pam_start(). + The flags argument is the binary or of zero or more of the + following values: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + PAM_CHANGE_EXPIRED_AUTHTOK + + + This argument indicates to the modules that the user's + authentication token (password) should only be changed + if it has expired. + If this argument is not passed, the application requires + that all authentication tokens are to be changed. + + + + + + + + RETURN VALUES + + + PAM_AUTHTOK_ERR + + + A module was unable to obtain the new authentication token. + + + + + PAM_AUTHTOK_RECOVERY_ERR + + + A module was unable to obtain the old authentication token. + + + + + PAM_AUTHTOK_LOCK_BUSY + + + One or more of the modules was unable to change the + authentication token since it is currently locked. + + + + + PAM_AUTHTOK_DISABLE_AGING + + + Authentication token aging has been disabled for at least + one of the modules. + + + + + PAM_PERM_DENIED + + + Permission denied. + + + + + PAM_SUCCESS + + + The authentication token was successfully updated. + + + + + PAM_TRY_AGAIN + + + Not all of the modules were in a position to update the + authentication token(s). In such a case none of the user's + authentication tokens are updated. + + + + + PAM_USER_UNKNOWN + + + User unknown to password service. + + + + + + + + SEE ALSO + + + pam_start3 + , + + pam_authenticate3 + , + + pam_setcred3 + , + + pam_get_item3 + , + + pam_strerror3 + , + + pam8 + + + + \ No newline at end of file diff --git a/doc/man/pam_close_session.3.xml b/doc/man/pam_close_session.3.xml new file mode 100644 index 0000000..e1c74eb --- /dev/null +++ b/doc/man/pam_close_session.3.xml @@ -0,0 +1,112 @@ + + + + pam_close_session + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_close_session + terminate PAM session management + + + + + + + #include <security/pam_appl.h> + + int pam_close_session + pam_handle_t *pamh + int flags + + + + + + + DESCRIPTION + + The pam_close_session function is used + to indicate that an authenticated session has ended. + The session should have been created with a call to + + pam_open_session3 + . + + + It should be noted that the effective uid, + + geteuid2 + . of the application should be of sufficient + privilege to perform such tasks as unmounting the + user's home directory for example. + + + The flags argument is the binary or of zero or more of the + following values: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + + + + RETURN VALUES + + + PAM_ABORT + + + General failure. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_SESSION_ERR + + + Session failure. + + + + + PAM_SUCCESS + + + Session was successful terminated. + + + + + + + + SEE ALSO + + + pam_open_session3 + , + + pam_strerror3 + + + + \ No newline at end of file diff --git a/doc/man/pam_conv.3.xml b/doc/man/pam_conv.3.xml new file mode 100644 index 0000000..31834f3 --- /dev/null +++ b/doc/man/pam_conv.3.xml @@ -0,0 +1,226 @@ + + + pam_conv + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_conv + PAM conversation function + + + + + + + #include <security/pam_appl.h> + + +struct pam_message { + int msg_style; + const char *msg; +}; + +struct pam_response { + char *resp; + int resp_retcode; +}; + +struct pam_conv { + int (*conv)(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr); + void *appdata_ptr; +}; + + + + + DESCRIPTION + + The PAM library uses an application-defined callback to allow + a direct communication between a loaded module and the application. + This callback is specified by the + struct pam_conv passed to + + pam_start3 + + at the start of the transaction. + + + When a module calls the referenced conv() function, the argument + appdata_ptr is set to the second element of + this structure. + + + The other arguments of a call to conv() concern the information + exchanged by module and application. That is to say, + num_msg holds the length of the array of + pointers, msg. After a successful return, the + pointer resp points to an array of pam_response + structures, holding the application supplied text. The + resp_retcode member of this struct is unused and + should be set to zero. It is the caller's responsibility to release + both, this array and the responses themselves, using + + free3 + . Note, *resp is a + struct pam_response array and not an array of + pointers. + + + The number of responses is always equal to the + num_msg conversation function argument. + This does require that the response array is + + free3 + 'd after + every call to the conversation function. The index of the + responses corresponds directly to the prompt index in the + pam_message array. + + + On failure, the conversation function should release any resources + it has allocated, and return one of the predefined PAM error codes. + + + Each message can have one of four types, specified by the + msg_style member of + struct pam_message: + + + + PAM_PROMPT_ECHO_OFF + + + Obtain a string without echoing any text. + + + + + PAM_PROMPT_ECHO_ON + + + Obtain a string whilst echoing text. + + + + + PAM_ERROR_MSG + + + Display an error message. + + + + + PAM_TEXT_INFO + + + Display some text. + + + + + + The point of having an array of messages is that it becomes possible + to pass a number of things to the application in a single call from + the module. It can also be convenient for the application that related + things come at once: a windows based application can then present a + single form with many messages/prompts on at once. + + + In passing, it is worth noting that there is a discrepancy between + the way Linux-PAM handles the const struct pam_message **msg + conversation function argument and the way that Solaris' PAM + (and derivatives, known to include HP/UX, are there others?) does. + Linux-PAM interprets the msg argument as entirely equivalent to the + following prototype + const struct pam_message *msg[] (which, in spirit, is consistent with + the commonly used prototypes for argv argument to the familiar main() + function: char **argv; and char *argv[]). Said another way Linux-PAM + interprets the msg argument as a pointer to an array of num_msg read + only 'struct pam_message' pointers. Solaris' PAM implementation + interprets this argument as a pointer to a pointer to an array of + num_msg pam_message structures. Fortunately, perhaps, for most + module/application developers when num_msg has a value of one these + two definitions are entirely equivalent. Unfortunately, casually + raising this number to two has led to unanticipated compatibility + problems. + + + For what its worth the two known module writer work-arounds for trying + to maintain source level compatibility with both PAM implementations + are: + + + + + never call the conversation function with num_msg greater than one. + + + + + set up msg as doubly referenced so both types of conversation + function can find the messages. That is, make + + + msg[n] = & (( *msg )[n]) + + + + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_CONV_ERR + + + Conversation failure. The application should not set + *resp. + + + + + PAM_SUCCESS + + + Success. + + + + + + + + SEE ALSO + + + pam_start3 + , + + pam_set_item3 + , + + pam_get_item3 + , + + pam_strerror3 + , + + pam8 + + + + \ No newline at end of file diff --git a/doc/man/pam_end.3.xml b/doc/man/pam_end.3.xml new file mode 100644 index 0000000..b2584e7 --- /dev/null +++ b/doc/man/pam_end.3.xml @@ -0,0 +1,119 @@ + + + + pam_end + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_end + termination of PAM transaction + + + + + + + #include <security/pam_appl.h> + + int pam_end + pam_handle_t *pamh + int pam_status + + + + + + + DESCRIPTION + + The pam_end function terminates the PAM + transaction and is the last function an application should call + in the PAM context. Upon return the handle pamh + is no longer valid and all memory associated with it will be + invalid. + + + The pam_status argument should be set to + the value returned to the application by the last PAM + library call. + + + The value taken by pam_status is used as + an argument to the module specific callback function, + cleanup() + (See + pam_set_data3 + and + + pam_get_data3 + ). In this way the module can be given notification + of the pass/fail nature of the tear-down process, and perform any + last minute tasks that are appropriate to the module before it is + unlinked. This argument can be logically OR'd with + PAM_DATA_SILENT to indicate that + the module should not treat the call too seriously. It is generally + used to indicate that the current closing of the library is in a + + fork2 + ed + process, and that the parent will take care of cleaning up things + that exist outside of the current process space (files etc.). + + + + This function free's all memory for items + associated with the + + pam_set_item3 + and + + pam_get_item3 + functions. Pointers associated with such objects + are not valid anymore after pam_end was called. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + Transaction was successful terminated. + + + + + PAM_SYSTEM_ERR + + + System error, for example a NULL pointer was submitted + as PAM handle or the function was called by a module. + + + + + + + + SEE ALSO + + + pam_get_data3 + , + + pam_set_data3 + , + + pam_start3 + , + + pam_strerror3 + + + + \ No newline at end of file diff --git a/doc/man/pam_error.3.xml b/doc/man/pam_error.3.xml new file mode 100644 index 0000000..0f294c2 --- /dev/null +++ b/doc/man/pam_error.3.xml @@ -0,0 +1,118 @@ + + + + pam_error + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_error + pam_verror + display error messages to the user + + + + + + + #include <security/pam_ext.h> + + int pam_error + pam_handle_t *pamh + const char *fmt + ... + + + int pam_verror + pam_handle_t *pamh + const char *fmt + va_list args + + + + + + DESCRIPTION + + The pam_error function prints error messages + through the conversation function to the user. + + + The pam_verror function performs the same + task as pam_error() with the difference + that it takes a set of arguments which have been obtained using + the + stdarg3 + variable argument list macros. + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_CONV_ERR + + + Conversation failure. + + + + + PAM_SUCCESS + + + Error message was displayed. + + + + + PAM_SYSTEM_ERR + + + System error. + + + + + + + + SEE ALSO + + + pam_info3 + , + + pam_vinfo3 + , + + pam_prompt3 + , + + pam_vprompt3 + , + + pam8 + + + + + + STANDARDS + + The pam_error and pam_verror + functions are Linux-PAM extensions. + + + + \ No newline at end of file diff --git a/doc/man/pam_fail_delay.3.xml b/doc/man/pam_fail_delay.3.xml new file mode 100644 index 0000000..c400736 --- /dev/null +++ b/doc/man/pam_fail_delay.3.xml @@ -0,0 +1,206 @@ + + + + pam_fail_delay + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_fail_delay + request a delay on failure + + + + + + + #include <security/pam_appl.h> + + int pam_fail_delay + pam_handle_t *pamh + unsigned int usec + + + + + + DESCRIPTION + + The pam_fail_delay function provides a + mechanism by which an application or module can suggest a minimum + delay of usec micro-seconds. The + function keeps a record of the longest time requested with this + function. Should + + pam_authenticate3 + fail, the failing return to the application is + delayed by an amount of time randomly distributed (by up to 50%) + about this longest value. + + + Independent of success, the delay time is reset to its zero + default value when the PAM service module returns control to + the application. The delay occurs after all + authentication modules have been called, but before + control is returned to the service application. + + + When using this function the programmer should check if it is + available with: + + +#ifdef HAVE_PAM_FAIL_DELAY + .... +#endif /* HAVE_PAM_FAIL_DELAY */ + + + + For applications written with a single thread that are event + driven in nature, generating this delay may be undesirable. + Instead, the application may want to register the delay in some + other way. For example, in a single threaded server that serves + multiple authentication requests from a single event loop, the + application might want to simply mark a given connection as + blocked until an application timer expires. For this reason + the delay function can be changed with the + PAM_FAIL_DELAY item. It can be queried and + set with + + pam_get_item3 + + and + + pam_set_item3 + respectively. The value used to set it should be + a function pointer of the following prototype: + +void (*delay_fn)(int retval, unsigned usec_delay, void *appdata_ptr); + + The arguments being the retval return code + of the module stack, the usec_delay + micro-second delay that libpam is requesting and the + appdata_ptr that the application has associated + with the current pamh. This last value was set + by the application when it called + + pam_start3 + or explicitly with + + pam_set_item3 + . + + + Note that the PAM_FAIL_DELAY item is set to NULL by default. This + indicates that PAM should perform a random delay as described + above when authentication fails and a delay has been suggested. + If an application does not want the PAM library to perform any + delay on authentication failure, then the application must define + a custom delay function that executes no statements and set + the PAM_FAIL_DELAY item to point to this function. + + + + + RATIONALE + + It is often possible to attack an authentication scheme by exploiting + the time it takes the scheme to deny access to an applicant user. In + cases of short timeouts, it may prove possible + to attempt a brute force dictionary attack -- + with an automated process, the attacker tries all possible passwords + to gain access to the system. In other cases, where individual + failures can take measurable amounts of time (indicating the nature + of the failure), an attacker can obtain useful information about the + authentication process. These latter attacks make use of procedural + delays that constitute a covert channel + of useful information. + + + To minimize the effectiveness of such attacks, it is desirable to + introduce a random delay in a failed authentication process. + Preferable this value should be set by the application or a special + PAM module. Standard PAM modules should not modify the delay + unconditional. + + + + + EXAMPLE + + For example, a login application may require a failure delay of + roughly 3 seconds. It will contain the following code: + + + pam_fail_delay (pamh, 3000000 /* micro-seconds */ ); + pam_authenticate (pamh, 0); + + + + if the modules do not request a delay, the failure delay will be + between 1.5 and 4.5 seconds. + + + + However, the modules, invoked in the authentication process, may + also request delays: + + + +module #1: pam_fail_delay (pamh, 2000000); +module #2: pam_fail_delay (pamh, 4000000); + + + + in this case, it is the largest requested value that is used to + compute the actual failed delay: here between 2 and 6 seconds. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + Delay was successful adjusted. + + + + + PAM_SYSTEM_ERR + + + A NULL pointer was submitted as PAM handle. + + + + + + + + SEE ALSO + + + pam_start3 + , + + pam_get_item3 + , + + pam_strerror3 + + + + + + STANDARDS + + The pam_fail_delay function is an + Linux-PAM extension. + + + + \ No newline at end of file diff --git a/doc/man/pam_get_authtok.3.xml b/doc/man/pam_get_authtok.3.xml new file mode 100644 index 0000000..60e0a45 --- /dev/null +++ b/doc/man/pam_get_authtok.3.xml @@ -0,0 +1,246 @@ + + + + pam_get_authtok + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_get_authtok + pam_get_authtok_verify + pam_get_authtok_noverify + get authentication token + + + + + + + #include <security/pam_ext.h> + + int pam_get_authtok + pam_handle_t *pamh + int item + const char **authtok + const char *prompt + + + int pam_get_authtok_noverify + pam_handle_t *pamh + const char **authtok + const char *prompt + + + int pam_get_authtok_verify + pam_handle_t *pamh + const char **authtok + const char *prompt + + + + + + DESCRIPTION + + The pam_get_authtok function returns the + cached authentication token, or prompts the user if no token is + currently cached. It is intended for internal use by Linux-PAM and + PAM service modules. Upon successful return, + authtok contains a pointer to the value of the + authentication token. Note, this is a pointer to the + actual data and should + not be free()'ed or + over-written! + + + The prompt argument specifies a prompt to use + if no token is cached. If a NULL pointer + is given, pam_get_authtok uses pre-defined prompts. + + + The following values are supported for item: + + + + PAM_AUTHTOK + + + Returns the current authentication token. Called from + pam_sm_chauthtok3 + pam_get_authtok will + ask the user to confirm the new token by retyping it. If + a prompt was specified, "Retype" will be used as prefix. + + + + + PAM_OLDAUTHTOK + + + Returns the previous authentication token when changing + authentication tokens. + + + + + + The pam_get_authtok_noverify function can + only be used for changing the password + (from + pam_sm_chauthtok3 + ). It returns the cached + authentication token, or prompts the user if no token is + currently cached. The difference to pam_get_authtok + is, that this function does not ask a second time for the password + to verify it. Upon successful return, authtok + contains a pointer to the value of the authentication token. Note, + this is a pointer to the + actual data and should + not be free()'ed or + over-written! + + + The pam_get_authtok_verify function can + only be used to verify a password for mistypes gotten by + + pam_get_authtok_noverify3 + . This function asks a second time for the password + and verify it with the password provided by authtok + argument. In case of an error, the value of authtok + is undefined. Else this argument will point to the + actual data and should + not be free()'ed or + over-written! + + + + + OPTIONS + + pam_get_authtok honours the following module + options: + + + + + try_first_pass + + + + Before prompting the user for their password, the module first + tries the previous stacked module's password in case that + satisfies this module as well. + + + + + + use_first_pass + + + + The argument forces the module + to use a previous stacked modules password and will never prompt + the user - if no password is available or the password is not + appropriate, the user will be denied access. + + + + + + use_authtok + + + + When password changing enforce the module to set the new + token to the one provided by a previously stacked + module. If no token is available + token changing will fail. + + + + + + authtok_type=XXX + + + + The default action is for the module to use the + following prompts when requesting passwords: + "New UNIX password: " and "Retype UNIX password: ". + The example word UNIX can + be replaced with this option, by default it is empty. + + + + + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + Authentication token could not be retrieved. + + + + + PAM_AUTHTOK_ERR + + + New authentication could not be retrieved. + + + + + PAM_SUCCESS + + + Authentication token was successfully retrieved. + + + + + PAM_SYSTEM_ERR + + + A NULL pointer was specified as the PAM handle, or + no space for an authentication token was provided. + + + + + PAM_TRY_AGAIN + + + New authentication tokens mismatch. + + + + + + + + SEE ALSO + + + pam8 + + + + + + STANDARDS + + The pam_get_authtok function is a Linux-PAM + extensions. + + + + diff --git a/doc/man/pam_get_data.3.xml b/doc/man/pam_get_data.3.xml new file mode 100644 index 0000000..9546cc1 --- /dev/null +++ b/doc/man/pam_get_data.3.xml @@ -0,0 +1,104 @@ + + + + pam_get_data + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_get_data + + get module internal data + + + + + + + + + + #include <security/pam_modules.h> + + int pam_get_data + const pam_handle_t *pamh + const char *module_data_name + const void **data + + + + + + + + DESCRIPTION + + This function together with the + + pam_set_data3 + function + is useful to manage module-specific data meaningful only to + the calling PAM module. + + + The pam_get_data function looks up the + object associated with the (hopefully) unique string + module_data_name in the PAM context + specified by the pamh argument. + A successful call to + pam_get_data will result in + data pointing to the object. Note, + this data is not a copy and should be + treated as constant by the module. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + Data was successful retrieved. + + + + + PAM_SYSTEM_ERR + + + A NULL pointer was submitted as PAM handle or the + function was called by an application. + + + + + PAM_NO_MODULE_DATA + + + No module specific data is present. + + + + + + + + SEE ALSO + + + pam_end3 + , + + pam_set_data3 + , + + pam_strerror3 + + + + + diff --git a/doc/man/pam_get_item.3.xml b/doc/man/pam_get_item.3.xml new file mode 100644 index 0000000..c30a279 --- /dev/null +++ b/doc/man/pam_get_item.3.xml @@ -0,0 +1,132 @@ + + + + pam_get_item + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_get_item + + getting PAM information + + + + + + + + + + #include <security/pam_modules.h> + + int pam_get_item + const pam_handle_t *pamh + int item_type + const void **item + + + + + + + + DESCRIPTION + + The pam_get_item function allows applications + and PAM service modules to access and retrieve PAM information + of item_type. Upon successful return, + item contains a pointer to the value of the + corresponding item. Note, this is a pointer to the + actual data and should + not be free()'ed or + over-written! The following values are supported for + item_type: + + + + + + The following additional items are specific to Linux-PAM and should not be used in + portable applications: + + + + + + If a service module wishes to obtain the name of the user, + it should not use this function, but instead perform a call to + + pam_get_user3 + . + + + Only a service module is privileged to read the + authentication tokens, PAM_AUTHTOK and PAM_OLDAUTHTOK. + + + + + + RETURN VALUES + + + PAM_BAD_ITEM + + + The application attempted to set an undefined or inaccessible + item. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_PERM_DENIED + + + The value of item was NULL. + + + + + PAM_SUCCESS + + + Data was successful updated. + + + + + PAM_SYSTEM_ERR + + + The pam_handle_t passed as first + argument was invalid. + + + + + + + + SEE ALSO + + + pam_set_item3 + , + + pam_strerror3 + + + + + \ No newline at end of file diff --git a/doc/man/pam_get_user.3.xml b/doc/man/pam_get_user.3.xml new file mode 100644 index 0000000..121b3aa --- /dev/null +++ b/doc/man/pam_get_user.3.xml @@ -0,0 +1,161 @@ + + + + pam_get_user + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_get_user + + get user name + + + + + + + + + + #include <security/pam_modules.h> + + int pam_get_user + const pam_handle_t *pamh + const char **user + const char *prompt + + + + + + + + DESCRIPTION + + The pam_get_user function returns the + name of the user specified by + + pam_start3 + . If no user was specified it returns what + pam_get_item (pamh, PAM_USER, ... ); would + have returned. If this is NULL it obtains the username via the + + pam_conv3 + mechanism, it prompts the user with the first + non-NULL string in the following list: + + + + + + The prompt argument passed to the function. + + + + + What is returned by pam_get_item (pamh, PAM_USER_PROMPT, ... ); + + + + + The default prompt: "login: " + + + + + By whatever means the username is obtained, a pointer to it is + returned as the contents of *user. Note, + this memory should not be + free()'d or modified + by the module. + + + This function sets the PAM_USER item + associated with the + + pam_set_item3 + and + + pam_get_item3 + functions. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + User name was successful retrieved. + + + + + PAM_SYSTEM_ERR + + + A NULL pointer was submitted. + + + + + PAM_CONV_ERR + + + The conversation method supplied by the + application failed to obtain the username. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_ABORT + + + Error resuming an old conversation. + + + + + PAM_CONV_AGAIN + + + The conversation method supplied by the application + is waiting for an event. + + + + + + + + SEE ALSO + + + pam_end3 + , + + pam_get_item3 + , + + pam_set_item3 + , + + pam_strerror3 + + + + + \ No newline at end of file diff --git a/doc/man/pam_getenv.3.xml b/doc/man/pam_getenv.3.xml new file mode 100644 index 0000000..df25863 --- /dev/null +++ b/doc/man/pam_getenv.3.xml @@ -0,0 +1,65 @@ + + + pam_getenv + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_getenv + get a PAM environment variable + + + + + + + #include <security/pam_appl.h> + + const char *pam_getenv + pam_handle_t *pamh + const char *name + + + + + + + DESCRIPTION + + The pam_getenv function searches the + PAM environment list as associated with the handle + pamh for an item that matches the string + pointed to by name and returns a pointer + to the value of the environment variable. The application is + not allowed to free the data. + + + + + RETURN VALUES + + The pam_getenv function returns NULL + on failure. + + + + + SEE ALSO + + + pam_start3 + , + + pam_getenvlist3 + , + + pam_putenv3 + , + + pam8 + + + + \ No newline at end of file diff --git a/doc/man/pam_getenvlist.3.xml b/doc/man/pam_getenvlist.3.xml new file mode 100644 index 0000000..54b1f41 --- /dev/null +++ b/doc/man/pam_getenvlist.3.xml @@ -0,0 +1,83 @@ + + + pam_getenvlist + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_getenvlist + getting the PAM environment + + + + + + + #include <security/pam_appl.h> + + char **pam_getenvlist + pam_handle_t *pamh + + + + + + + DESCRIPTION + + The pam_getenvlist function returns a complete + copy of the PAM environment as associated with the handle + pamh. The PAM environment variables + represent the contents of the regular environment variables of the + authenticated user when service is granted. + + + The format of the memory is a malloc()'d array of char pointers, + the last element of which is set to NULL. Each of the non-NULL + entries in this array point to a NUL terminated and malloc()'d + char string of the form: "name=value". + + + It should be noted that this memory will never be free()'d by + libpam. Once obtained by a call to + pam_getenvlist, it is the responsibility of + the calling application to free() this memory. + + + It is by design, and not a coincidence, that the format and contents + of the returned array matches that required for the third argument of + the + + execle3 + function call. + + + + + RETURN VALUES + + The pam_getenvlist function returns NULL + on failure. + + + + + SEE ALSO + + + pam_start3 + , + + pam_getenv3 + , + + pam_putenv3 + , + + pam8 + + + + \ No newline at end of file diff --git a/doc/man/pam_info.3.xml b/doc/man/pam_info.3.xml new file mode 100644 index 0000000..5155d41 --- /dev/null +++ b/doc/man/pam_info.3.xml @@ -0,0 +1,106 @@ + + + + pam_info + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_info + pam_vinfo + display messages to the user + + + + + + + #include <security/pam_ext.h> + + int pam_info + pam_handle_t *pamh + const char *fmt + ... + + + int pam_vinfo + pam_handle_t *pamh + const char *fmt + va_list args + + + + + + DESCRIPTION + + The pam_info function prints messages + through the conversation function to the user. + + + The pam_vinfo function performs the same + task as pam_info() with the difference + that it takes a set of arguments which have been obtained using + the + stdarg3 + variable argument list macros. + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_CONV_ERR + + + Conversation failure. + + + + + PAM_SUCCESS + + + Transaction was successful created. + + + + + PAM_SYSTEM_ERR + + + System error. + + + + + + + + SEE ALSO + + + pam8 + + + + + + STANDARDS + + The pam_info and pam_vinfo + functions are Linux-PAM extensions. + + + + \ No newline at end of file diff --git a/doc/man/pam_item_types_ext.inc.xml b/doc/man/pam_item_types_ext.inc.xml new file mode 100644 index 0000000..a5fee9c --- /dev/null +++ b/doc/man/pam_item_types_ext.inc.xml @@ -0,0 +1,60 @@ + + + + PAM_FAIL_DELAY + + + A function pointer to redirect centrally managed + failure delays. See + + pam_fail_delay3 + . + + + + + + PAM_XDISPLAY + + + The name of the X display. For graphical, X-based applications the + value for this item should be the $DISPLAY + variable. This value may be used independently of + PAM_TTY for passing the + name of the display. + + + + + + PAM_XAUTHDATA + + + A pointer to a structure containing the X authentication data + required to make a connection to the display specified by + PAM_XDISPLAY, if such information is + necessary. See + + pam_xauth_data3 + . + + + + + + PAM_AUTHTOK_TYPE + + + The default action is for the module to use the + following prompts when requesting passwords: + "New UNIX password: " and "Retype UNIX password: ". + The example word UNIX can + be replaced with this item, by default it is empty. + This item is used by + pam_get_authtok3 + . + + + + + \ No newline at end of file diff --git a/doc/man/pam_item_types_std.inc.xml b/doc/man/pam_item_types_std.inc.xml new file mode 100644 index 0000000..b91c71b --- /dev/null +++ b/doc/man/pam_item_types_std.inc.xml @@ -0,0 +1,139 @@ + + + + PAM_SERVICE + + + The service name (which identifies that PAM stack that + the PAM functions will use to authenticate the program). + + + + + + PAM_USER + + + The username of the entity under whose identity service + will be given. That is, following authentication, + PAM_USER identifies the local entity + that gets to use the service. Note, this value can be mapped + from something (eg., "anonymous") to something else (eg. + "guest119") by any module in the PAM stack. As such an + application should consult the value of + PAM_USER after each call to a PAM function. + + + + + + PAM_USER_PROMPT + + + The string used when prompting for a user's name. The default + value for this string is a localized version of "login: ". + + + + + + PAM_TTY + + + The terminal name prefixed by /dev/ for + device files. + In the past, graphical X-based applications used to store the + $DISPLAY variable here, but with the + introduction of PAM_XDISPLAY this usage + is deprecated. + + + + + + PAM_RUSER + + + The requesting user name: local name for a locally + requesting user or a remote user name for a remote + requesting user. + + + Generally an application or module will attempt to supply + the value that is most strongly authenticated (a local account + before a remote one. The level of trust in this value is + embodied in the actual authentication stack associated with + the application, so it is ultimately at the discretion of the + system administrator. + + + PAM_RUSER@PAM_RHOST should always identify + the requesting user. In some cases, + PAM_RUSER may be NULL. In such situations, + it is unclear who the requesting entity is. + + + + + + PAM_RHOST + + + The requesting hostname (the hostname of the machine from + which the PAM_RUSER entity is requesting + service). That is PAM_RUSER@PAM_RHOST + does identify the requesting user. In some applications, + PAM_RHOST may be NULL. In such situations, + it is unclear where the authentication request is originating + from. + + + + + + PAM_AUTHTOK + + + The authentication token (often a password). This token + should be ignored by all module functions besides + + pam_sm_authenticate3 + and + + pam_sm_chauthtok3 + . + In the former function it is used to pass the most recent + authentication token from one stacked module to another. In + the latter function the token is used for another purpose. + It contains the currently active authentication token. + + + + + + PAM_OLDAUTHTOK + + + The old authentication token. This token should be ignored + by all module functions except + + pam_sm_chauthtok3 + . + + + + + + + PAM_CONV + + + The pam_conv structure. See + + pam_conv3 + . + + + + + diff --git a/doc/man/pam_misc_drop_env.3.xml b/doc/man/pam_misc_drop_env.3.xml new file mode 100644 index 0000000..a7f6cc8 --- /dev/null +++ b/doc/man/pam_misc_drop_env.3.xml @@ -0,0 +1,60 @@ + + + + pam_misc_drop_env + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_misc_drop_env + liberating a locally saved environment + + + + + + + #include <security/pam_misc.h> + + int pam_misc_drop_env + char **env + + + + + + DESCRIPTION + + This function is defined to complement the + pam_getenvlist3 + function. It liberates the memory associated + with env, overwriting + with 0 all memory before + free()ing it. + + + + + SEE ALSO + + + pam_getenvlist3 + , + + pam8 + + + + + + STANDARDS + + The pam_misc_drop_env function is part of the + libpam_misc Library and not defined in any + standard. + + + + \ No newline at end of file diff --git a/doc/man/pam_misc_paste_env.3.xml b/doc/man/pam_misc_paste_env.3.xml new file mode 100644 index 0000000..06194a9 --- /dev/null +++ b/doc/man/pam_misc_paste_env.3.xml @@ -0,0 +1,58 @@ + + + + pam_misc_paste_env + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_misc_paste_env + transcribing an environment to that of PAM + + + + + + + #include <security/pam_misc.h> + + int pam_misc_paste_env + pam_handle_t *pamh + const char * const *user + + + + + + DESCRIPTION + + This function takes the supplied list of environment pointers and + uploads its contents to the PAM environment. + Success is indicated by PAM_SUCCESS. + + + + + SEE ALSO + + + pam_putenv3 + , + + pam8 + + + + + + STANDARDS + + The pam_misc_paste_env function is part of the + libpam_misc Library and not defined in any + standard. + + + + \ No newline at end of file diff --git a/doc/man/pam_misc_setenv.3.xml b/doc/man/pam_misc_setenv.3.xml new file mode 100644 index 0000000..4414d54 --- /dev/null +++ b/doc/man/pam_misc_setenv.3.xml @@ -0,0 +1,65 @@ + + + + pam_misc_setenv + 3 + Linux-PAM + Linux-PAM Manual + + + pam_misc_setenv + BSD like PAM environment variable setting + + + + + + + #include <security/pam_misc.h> + + int pam_misc_setenv + pam_handle_t *pamh + const char *name + const char *value + int readonly + + + + + + DESCRIPTION + + This function performs a task equivalent to + pam_putenv3 + , its syntax is, however, more like the BSD style + function; setenv(). The name + and value are concatenated with an '=' to + form a name=value and passed to pam_putenv(). + If, however, the PAM variable is already set, the replacement will + only be applied if the last argument, readonly, + is zero. + + + + + SEE ALSO + + + pam_putenv3 + , + + pam8 + + + + + + STANDARDS + + The pam_misc_setenv function is part of the + libpam_misc Library and not defined in any + standard. + + + + \ No newline at end of file diff --git a/doc/man/pam_open_session.3.xml b/doc/man/pam_open_session.3.xml new file mode 100644 index 0000000..a05e0ab --- /dev/null +++ b/doc/man/pam_open_session.3.xml @@ -0,0 +1,112 @@ + + + + pam_open_session + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_open_session + start PAM session management + + + + + + + #include <security/pam_appl.h> + + int pam_open_session + pam_handle_t *pamh + int flags + + + + + + + DESCRIPTION + + The pam_open_session function sets up a + user session for a previously successful authenticated user. + The session should later be terminated with a call to + + pam_close_session3 + . + + + It should be noted that the effective uid, + + geteuid2 + , of the application should be of sufficient + privilege to perform such tasks as creating or mounting the + user's home directory for example. + + + The flags argument is the binary or of zero or more of the + following values: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + + + + RETURN VALUES + + + PAM_ABORT + + + General failure. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_SESSION_ERR + + + Session failure. + + + + + PAM_SUCCESS + + + Session was successful created. + + + + + + + + SEE ALSO + + + pam_close_session3 + , + + pam_strerror3 + + + + \ No newline at end of file diff --git a/doc/man/pam_prompt.3.xml b/doc/man/pam_prompt.3.xml new file mode 100644 index 0000000..c65a0c9 --- /dev/null +++ b/doc/man/pam_prompt.3.xml @@ -0,0 +1,111 @@ + + + + pam_prompt + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_prompt + pam_vprompt + interface to conversation function + + + + + + + #include <security/pam_ext.h> + + int pam_prompt + pam_handle_t *pamh + int style + char **response + const char *fmt + ... + + + int pam_vprompt + pam_handle_t *pamh + int style + char **response + const char *fmt + va_list args + + + + + + DESCRIPTION + + The pam_prompt function constructs a message + from the specified format string and arguments and passes it to the + conversation function as set by the service. Upon successful return, + response is set to point to a string + returned from the conversation function. This string is allocated + on heap and should be freed. + + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_CONV_ERR + + + Conversation failure. + + + + + PAM_SUCCESS + + + Conversation succeeded, response is set. + + + + + PAM_SYSTEM_ERR + + + System error. + + + + + + + + + SEE ALSO + + + pam8 + , + + pam_conv3 + + + + + + STANDARDS + + The pam_prompt and pam_vprompt + functions are Linux-PAM extensions. + + + + \ No newline at end of file diff --git a/doc/man/pam_putenv.3.xml b/doc/man/pam_putenv.3.xml new file mode 100644 index 0000000..7267046 --- /dev/null +++ b/doc/man/pam_putenv.3.xml @@ -0,0 +1,150 @@ + + + pam_putenv + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_putenv + set or change PAM environment variable + + + + + + + #include <security/pam_appl.h> + + int pam_putenv + pam_handle_t *pamh + const char *name_value + + + + + + + DESCRIPTION + + The pam_putenv function is used to + add or change the value of PAM environment variables as + associated with the pamh handle. + + + The pamh argument is an authentication + handle obtained by a prior call to pam_start(). + The name_value argument is a single NUL + terminated string of one of the following forms: + + + + NAME=value of variable + + + In this case the environment variable of the given NAME + is set to the indicated value: + value of variable. If this variable + is already known, it is overwritten. Otherwise it is added + to the PAM environment. + + + + + NAME= + + + This function sets the variable to an empty value. It is + listed separately to indicate that this is the correct way + to achieve such a setting. + + + + + NAME + + + Without an '=' the pam_putenv() function + will delete the + corresponding variable from the PAM environment. + + + + + + pam_putenv() operates on a copy of + name_value, which means in contrast to + + putenv3 + , the application is responsible for freeing the data. + + + + + RETURN VALUES + + + PAM_PERM_DENIED + + + Argument name_value given is a NULL pointer. + + + + + PAM_BAD_ITEM + + + Variable requested (for deletion) is not currently set. + + + + + PAM_ABORT + + + The pamh handle is corrupt. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_SUCCESS + + + The environment variable was successfully updated. + + + + + + + + SEE ALSO + + + pam_start3 + , + + pam_getenv3 + , + + pam_getenvlist3 + , + + pam_strerror3 + , + + pam8 + + + + \ No newline at end of file diff --git a/doc/man/pam_set_data.3.xml b/doc/man/pam_set_data.3.xml new file mode 100644 index 0000000..f8c8988 --- /dev/null +++ b/doc/man/pam_set_data.3.xml @@ -0,0 +1,175 @@ + + + + pam_set_data + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_set_data + + set module internal data + + + + + + + + + + #include <security/pam_modules.h> + + int pam_set_data + pam_handle_t *pamh + const char *module_data_name + void *data + void (*cleanup)(pam_handle_t *pamh, void *data, int error_status) + + + + + + + + DESCRIPTION + + The pam_set_data function associates a pointer + to an object with the (hopefully) unique string + module_data_name in the PAM context specified + by the pamh argument. + + + + PAM modules may be dynamically loadable objects. In general such files + should not contain static variables. This function + and its counterpart + + pam_get_data3 + , + provide a mechanism for a module to associate some data with + the handle pamh. Typically a module will call the + pam_set_data function to register some data + under a (hopefully) unique module_data_name. + The data is available for use by other modules too but + not by an application. Since this functions + stores only a pointer to the data, the module + should not modify or free the content of it. + + + + The function cleanup() is associated with the + data and, if non-NULL, it is called when this + data is over-written or following a call to + + pam_end3 + . + + + + The error_status argument is used to indicate + to the module the sort of action it is to take in cleaning this data + item. As an example, Kerberos creates a ticket file during the + authentication phase, this file might be associated with a data item. + When + + pam_end3 + + is called by the module, the error_status + carries the return value of the + + pam_authenticate3 + + or other libpam function as appropriate. Based + on this value the Kerberos module may choose to delete the ticket file + (authentication failure) or leave it in place. + + + + The error_status may have been logically + OR'd with either of the following two values: + + + + + PAM_DATA_REPLACE + + + When a data item is being replaced (through a second call to + pam_set_data) this mask is used. + Otherwise, the call is assumed to be from + + pam_end3 + . + + + + + + PAM_DATA_SILENT + + + Which indicates that the process would prefer to perform the + cleanup() quietly. That is, discourages + logging/messages to the user. It is generally used to indicate that + the current closing of the library is in a + + fork2 + ed + process, and that the parent will take care of cleaning up things + that exist outside of the current process space (files etc.). + + + + + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_SUCCESS + + + Data was successful stored. + + + + + PAM_SYSTEM_ERR + + + A NULL pointer was submitted as PAM handle or the + function was called by an application. + + + + + + + + SEE ALSO + + + pam_end3 + , + + pam_get_data3 + , + + pam_strerror3 + + + + + \ No newline at end of file diff --git a/doc/man/pam_set_item.3.xml b/doc/man/pam_set_item.3.xml new file mode 100644 index 0000000..1dbaeeb --- /dev/null +++ b/doc/man/pam_set_item.3.xml @@ -0,0 +1,125 @@ + + + + pam_set_item + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_set_item + + set and update PAM information + + + + + + + + + + #include <security/pam_modules.h> + + int pam_set_item + pam_handle_t *pamh + int item_type + const void *item + + + + + + + + DESCRIPTION + + The pam_set_item function allows applications + and PAM service modules to access and to update PAM information + of item_type. For this a copy + of the object pointed to by the item argument + is created. The following item_types are + supported: + + + + + + The following additional items are specific to Linux-PAM and should not be used in + portable applications: + + + + + + For all item_types, other than PAM_CONV and + PAM_FAIL_DELAY, item is a pointer to a <NUL> + terminated character string. In the case of PAM_CONV, + item points to an initialized + pam_conv structure. In the case of + PAM_FAIL_DELAY, item is a function pointer: + void (*delay_fn)(int retval, unsigned usec_delay, void *appdata_ptr) + + + + Both, PAM_AUTHTOK and PAM_OLDAUTHTOK, will be reset before + returning to the application. Which means an application is not + able to access the authentication tokens. + + + + + + RETURN VALUES + + + PAM_BAD_ITEM + + + The application attempted to set an undefined or inaccessible + item. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_SUCCESS + + + Data was successful updated. + + + + + PAM_SYSTEM_ERR + + + The pam_handle_t passed as first + argument was invalid. + + + + + + + + SEE ALSO + + + pam_get_item3 + , + + pam_strerror3 + + + + + \ No newline at end of file diff --git a/doc/man/pam_setcred.3.xml b/doc/man/pam_setcred.3.xml new file mode 100644 index 0000000..09fe30d --- /dev/null +++ b/doc/man/pam_setcred.3.xml @@ -0,0 +1,177 @@ + + + + pam_setcred + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_setcred + + establish / delete user credentials + + + + + + + #include <security/pam_appl.h> + + int pam_setcred + pam_handle_t *pamh + int flags + + + + + + + DESCRIPTION + + The pam_setcred function is used to establish, + maintain and delete the credentials of a user. It should be called + to set the credentials after a user has been authenticated and before + a session is opened for the user (with + + pam_open_session3 + ). The credentials should be deleted after the session + has been closed (with + + pam_close_session3 + ). + + + + A credential is something that the user possesses. It is some + property, such as a Kerberos ticket, or a + supplementary group membership that make up the uniqueness of a + given user. On a Linux system the user's UID + and GID's are credentials too. However, it + has been decided that these properties (along with the default + supplementary groups of which the user is a member) are credentials + that should be set directly by the application and not by PAM. + Such credentials should be established, by the application, prior + to a call to this function. For example, + + initgroups2 + (or equivalent) should have been performed. + + + + Valid flags, any one of which, may be + logically OR'd with , are: + + + + + PAM_ESTABLISH_CRED + + Initialize the credentials for the user. + + + + PAM_DELETE_CRED + + Delete the user's credentials. + + + + PAM_REINITIALIZE_CRED + + Fully reinitialize the user's credentials. + + + + PAM_REFRESH_CRED + + Extend the lifetime of the existing credentials. + + + + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_CRED_ERR + + + Failed to set user credentials. + + + + + PAM_CRED_EXPIRED + + + User credentials are expired. + + + + + PAM_CRED_UNAVAIL + + + Failed to retrieve user credentials. + + + + + PAM_SUCCESS + + + Data was successful stored. + + + + + PAM_SYSTEM_ERR + + + A NULL pointer was submitted as PAM handle, the + function was called by a module or another system + error occurred. + + + + + PAM_USER_UNKNOWN + + + User is not known to an authentication module. + + + + + + + + + SEE ALSO + + + pam_authenticate3 + , + + pam_open_session3 + , + + pam_close_session3 + , + + pam_strerror3 + + + + \ No newline at end of file diff --git a/doc/man/pam_sm_acct_mgmt.3.xml b/doc/man/pam_sm_acct_mgmt.3.xml new file mode 100644 index 0000000..822a338 --- /dev/null +++ b/doc/man/pam_sm_acct_mgmt.3.xml @@ -0,0 +1,152 @@ + + + pam_sm_acct_mgmt + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_sm_acct_mgmt + PAM service function for account management + + + + + + + #include <security/pam_modules.h> + + int pam_sm_acct_mgmt + pam_handle_t *pamh + int flags + int argc + const char **argv + + + + + + + DESCRIPTION + + The pam_sm_acct_mgmt function is the service + module's implementation of the + + pam_acct_mgmt3 + interface. + + + This function performs the task of establishing whether the user is + permitted to gain access at this time. It should be understood that + the user has previously been validated by an authentication + module. This function checks for other things. Such things might be: + the time of day or the date, the terminal line, remote hostname, etc. + This function may also determine things like the expiration on + passwords, and respond that the user change it before continuing. + + + Valid flags, which may be logically OR'd with + PAM_SILENT, are: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + PAM_DISALLOW_NULL_AUTHTOK + + + Return PAM_AUTH_ERR if the + database of authentication tokens for this authentication + mechanism has a NULL entry for the user. + + + + + + + + RETURN VALUES + + + PAM_ACCT_EXPIRED + + + User account has expired. + + + + + PAM_AUTH_ERR + + + Authentication failure. + + + + + PAM_NEW_AUTHTOK_REQD + + + The user's authentication token has expired. Before calling + this function again the application will arrange for a new + one to be given. This will likely result in a call to + pam_sm_chauthtok(). + + + + + + PAM_PERM_DENIED + + + Permission denied. + + + + + PAM_SUCCESS + + + The authentication token was successfully updated. + + + + + PAM_USER_UNKNOWN + + + User unknown to password service. + + + + + + + + SEE ALSO + + + pam3 + , + + pam_acct_mgmt3 + , + + pam_sm_chauthtok3 + , + + pam_strerror3 + , + + PAM8 + + + + \ No newline at end of file diff --git a/doc/man/pam_sm_authenticate.3.xml b/doc/man/pam_sm_authenticate.3.xml new file mode 100644 index 0000000..ec3de2f --- /dev/null +++ b/doc/man/pam_sm_authenticate.3.xml @@ -0,0 +1,149 @@ + + + pam_sm_authenticate + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_sm_authenticate + PAM service function for user authentication + + + + + + + #include <security/pam_modules.h> + + int pam_sm_authenticate + pam_handle_t *pamh + int flags + int argc + const char **argv + + + + + + + DESCRIPTION + + The pam_sm_authenticate function is the service + module's implementation of the + + pam_authenticate3 + interface. + + + This function performs the task of authenticating the user. + + + Valid flags, which may be logically OR'd with + PAM_SILENT, are: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + PAM_DISALLOW_NULL_AUTHTOK + + + Return PAM_AUTH_ERR if the + database of authentication tokens for this authentication + mechanism has a NULL entry for the user. + Without this flag, such a NULL token + will lead to a success without the user being prompted. + + + + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + Authentication failure. + + + + + PAM_CRED_INSUFFICIENT + + + For some reason the application does not have sufficient + credentials to authenticate the user. + + + + + PAM_AUTHINFO_UNAVAIL + + + The modules were not able to access the authentication + information. This might be due to a network or hardware + failure etc. + + + + + PAM_SUCCESS + + + The authentication token was successfully updated. + + + + + PAM_USER_UNKNOWN + + + The supplied username is not known to the authentication + service. + + + + + PAM_MAXTRIES + + + One or more of the authentication modules has reached its + limit of tries authenticating the user. Do not try again. + + + + + + + + SEE ALSO + + + pam3 + , + + pam_authenticate3 + , + + pam_sm_setcred3 + , + + pam_strerror3 + , + + PAM8 + + + + \ No newline at end of file diff --git a/doc/man/pam_sm_chauthtok.3.xml b/doc/man/pam_sm_chauthtok.3.xml new file mode 100644 index 0000000..692bc62 --- /dev/null +++ b/doc/man/pam_sm_chauthtok.3.xml @@ -0,0 +1,202 @@ + + + pam_sm_chauthtok + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_sm_chauthtok + PAM service function for authentication token management + + + + + + + #include <security/pam_modules.h> + + int pam_sm_chauthtok + pam_handle_t *pamh + int flags + int argc + const char **argv + + + + + + + DESCRIPTION + + The pam_sm_chauthtok function is the service + module's implementation of the + + pam_chauthtok3 + interface. + + + This function is used to (re-)set the authentication token of the user. + + + Valid flags, which may be logically OR'd with + PAM_SILENT, are: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + PAM_CHANGE_EXPIRED_AUTHTOK + + + This argument indicates to the module that the user's + authentication token (password) should only be changed if + it has expired. This flag is optional and + must be combined with one of the + following two flags. Note, however, the following two options + are mutually exclusive. + + + + + PAM_PRELIM_CHECK + + + This indicates that the modules are being probed as to + their ready status for altering the user's authentication + token. If the module requires access to another system over + some network it should attempt to verify it can connect to + this system on receiving this flag. If a module cannot establish + it is ready to update the user's authentication token it should + return PAM_TRY_AGAIN, this + information will be passed back to the application. + + + If the control value sufficient is used in + the password stack, the PAM_PRELIM_CHECK section + of the modules following that control value is not always executed. + + + + + PAM_UPDATE_AUTHTOK + + + This informs the module that this is the call it should change + the authorization tokens. If the flag is logically OR'd with + PAM_CHANGE_EXPIRED_AUTHTOK, the + token is only changed if it has actually expired. + + + + + + The PAM library calls this function twice in succession. The first + time with PAM_PRELIM_CHECK and then, + if the module does not return + PAM_TRY_AGAIN, subsequently with + PAM_UPDATE_AUTHTOK. It is only on + the second call that the authorization token is (possibly) changed. + + + + + RETURN VALUES + + + PAM_AUTHTOK_ERR + + + The module was unable to obtain the new authentication token. + + + + + PAM_AUTHTOK_RECOVERY_ERR + + + The module was unable to obtain the old authentication token. + + + + + PAM_AUTHTOK_LOCK_BUSY + + + Cannot change the authentication token since it is currently + locked. + + + + + PAM_AUTHTOK_DISABLE_AGING + + + Authentication token aging has been disabled. + + + + + PAM_PERM_DENIED + + + Permission denied. + + + + + PAM_TRY_AGAIN + + + Preliminary check was unsuccessful. Signals an immediate + return to the application is desired. + + + + + PAM_SUCCESS + + + The authentication token was successfully updated. + + + + + PAM_USER_UNKNOWN + + + User unknown to password service. + + + + + + + + SEE ALSO + + + pam3 + , + + pam_chauthtok3 + , + + pam_sm_chauthtok3 + , + + pam_strerror3 + , + + PAM8 + + + + \ No newline at end of file diff --git a/doc/man/pam_sm_close_session.3.xml b/doc/man/pam_sm_close_session.3.xml new file mode 100644 index 0000000..e76693f --- /dev/null +++ b/doc/man/pam_sm_close_session.3.xml @@ -0,0 +1,97 @@ + + + pam_sm_close_session + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_sm_close_session + PAM service function to terminate session management + + + + + + + #include <security/pam_modules.h> + + int pam_sm_close_session + pam_handle_t *pamh + int flags + int argc + const char **argv + + + + + + + DESCRIPTION + + The pam_sm_close_session function is the service + module's implementation of the + + pam_close_session3 + interface. + + + This function is called to terminate a session. The only valid + value for flags is zero or: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + + + + RETURN VALUES + + + PAM_SESSION_ERR + + + Cannot make/remove an entry for the specified session. + + + + + PAM_SUCCESS + + + The session was successfully terminated. + + + + + + + + SEE ALSO + + + pam3 + , + + pam_close_session3 + , + + pam_sm_close_session3 + , + + pam_strerror3 + , + + PAM8 + + + + \ No newline at end of file diff --git a/doc/man/pam_sm_open_session.3.xml b/doc/man/pam_sm_open_session.3.xml new file mode 100644 index 0000000..392225a --- /dev/null +++ b/doc/man/pam_sm_open_session.3.xml @@ -0,0 +1,97 @@ + + + pam_sm_open_session + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_sm_open_session + PAM service function to start session management + + + + + + + #include <security/pam_modules.h> + + int pam_sm_open_session + pam_handle_t *pamh + int flags + int argc + const char **argv + + + + + + + DESCRIPTION + + The pam_sm_open_session function is the service + module's implementation of the + + pam_open_session3 + interface. + + + This function is called to commence a session. The only valid + value for flags is zero or: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + + + + RETURN VALUES + + + PAM_SESSION_ERR + + + Cannot make/remove an entry for the specified session. + + + + + PAM_SUCCESS + + + The session was successfully started. + + + + + + + + SEE ALSO + + + pam3 + , + + pam_open_session3 + , + + pam_sm_close_session3 + , + + pam_strerror3 + , + + PAM8 + + + + \ No newline at end of file diff --git a/doc/man/pam_sm_setcred.3.xml b/doc/man/pam_sm_setcred.3.xml new file mode 100644 index 0000000..93a69e3 --- /dev/null +++ b/doc/man/pam_sm_setcred.3.xml @@ -0,0 +1,182 @@ + + + pam_sm_setcred + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_sm_setcred + PAM service function to alter credentials + + + + + + + #include <security/pam_modules.h> + + int pam_sm_setcred + pam_handle_t *pamh + int flags + int argc + const char **argv + + + + + + + DESCRIPTION + + The pam_sm_setcred function is the service + module's implementation of the + + pam_setcred3 + interface. + + + This function performs the task of altering the credentials of the + user with respect to the corresponding authorization + scheme. Generally, an authentication module may have access to more + information about a user than their authentication token. This + function is used to make such information available to the + application. It should only be called after the + user has been authenticated but before a session has been established. + + + Valid flags, which may be logically OR'd with + PAM_SILENT, are: + + + + PAM_SILENT + + + Do not emit any messages. + + + + + PAM_ESTABLISH_CRED + + Initialize the credentials for the user. + + + + PAM_DELETE_CRED + + + Delete the credentials associated with the authentication service. + + + + + PAM_REINITIALIZE_CRED + + + Reinitialize the user credentials. + + + + + PAM_REFRESH_CRED + + + Extend the lifetime of the user credentials. + + + + + + The way the auth stack is + navigated in order to evaluate the pam_setcred() + function call, independent of the pam_sm_setcred() + return codes, is exactly the same way that it was navigated when + evaluating the pam_authenticate() library + call. Typically, if a stack entry was ignored in evaluating + pam_authenticate(), it will be ignored when + libpam evaluates the pam_setcred() function + call. Otherwise, the return codes from each module specific + pam_sm_setcred() call are treated as + required. + + + + + RETURN VALUES + + + PAM_CRED_UNAVAIL + + + This module cannot retrieve the user's credentials. + + + + + PAM_CRED_EXPIRED + + + The user's credentials have expired. + + + + + PAM_CRED_ERR + + + This module was unable to set the credentials of the user. + + + + + PAM_SUCCESS + + + The user credential was successfully set. + + + + + PAM_USER_UNKNOWN + + + The user is not known to this authentication module. + + + + + + These, non-PAM_SUCCESS, return values will + typically lead to the credential stack failing. + The first such error will dominate in the return value of + pam_setcred(). + + + + + SEE ALSO + + + pam3 + , + + pam_authenticate3 + , + + pam_setcred3 + , + + pam_sm_authenticate3 + , + + pam_strerror3 + , + + PAM8 + + + + \ No newline at end of file diff --git a/doc/man/pam_start.3.xml b/doc/man/pam_start.3.xml new file mode 100644 index 0000000..470c6ce --- /dev/null +++ b/doc/man/pam_start.3.xml @@ -0,0 +1,164 @@ + + + + pam_start + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_start + pam_start_confdir + initialization of PAM transaction + + + + + + + #include <security/pam_appl.h> + + int pam_start + const char *service_name + const char *user + const struct pam_conv *pam_conversation + pam_handle_t **pamh + + + int pam_start_confdir + const char *service_name + const char *user + const struct pam_conv *pam_conversation + const char *confdir + pam_handle_t **pamh + + + + + + + DESCRIPTION + + The pam_start function creates the PAM context + and initiates the PAM transaction. It is the first of the PAM + functions that needs to be called by an application. The transaction + state is contained entirely within the structure identified by this + handle, so it is possible to have multiple transactions in parallel. + But it is not possible to use the same handle for different + transactions, a new one is needed for every new context. + + + + The service_name argument specifies the name + of the service to apply and will be stored as PAM_SERVICE item in + the new context. The policy for the service will be read from the + file /etc/pam.d/service_name or, if that file + does not exist, from /etc/pam.conf. + + + + The user argument can specify the name + of the target user and will be stored as PAM_USER item. If + the argument is NULL, the module has to ask for this item if + necessary. + + + + The pam_conversation argument points to + a struct pam_conv describing the + conversation function to use. An application must provide this + for direct communication between a loaded module and the + application. + + + + Following a successful return (PAM_SUCCESS) the contents of + pamh is a handle that contains the PAM + context for successive calls to the PAM functions. In an error + case is the content of pamh undefined. + + + + The pam_handle_t is a blind structure and + the application should not attempt to probe it directly for + information. Instead the PAM library provides the functions + + pam_set_item3 + and + + pam_get_item3 + . + The PAM handle cannot be used for multiple authentications at the + same time as long as pam_end was not called on + it before. + + + + The pam_start_confdir function behaves + like the pam_start function but it also + allows setting confdir argument with + a path to a directory to override the default + (/etc/pam.d) path for service policy + files. If the confdir is NULL, the function + works exactly the same as pam_start. + + + + + RETURN VALUES + + + PAM_ABORT + + + General failure. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_SUCCESS + + + Transaction was successfully started. + + + + + PAM_SYSTEM_ERR + + + System error, for example a NULL pointer was submitted + instead of a pointer to data. + + + + + + + + SEE ALSO + + + pam_get_data3 + , + + pam_set_data3 + , + + pam_end3 + , + + pam_strerror3 + + + + \ No newline at end of file diff --git a/doc/man/pam_strerror.3.xml b/doc/man/pam_strerror.3.xml new file mode 100644 index 0000000..b76cbc4 --- /dev/null +++ b/doc/man/pam_strerror.3.xml @@ -0,0 +1,55 @@ + + + + pam_strerror + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_strerror + return string describing PAM error code + + + + + + + #include <security/pam_appl.h> + + const char *pam_strerror + pam_handle_t *pamh + int errnum + + + + + + + DESCRIPTION + + The pam_strerror function returns a pointer to + a string describing the error code passed in the argument + errnum, possibly using the LC_MESSAGES part of + the current locale to select the appropriate language. This string + must not be modified by the application. No library function will + modify this string. + + + + RETURN VALUES + + This function returns always a pointer to a string. + + + + + SEE ALSO + + + pam8 + + + + \ No newline at end of file diff --git a/doc/man/pam_syslog.3.xml b/doc/man/pam_syslog.3.xml new file mode 100644 index 0000000..f5be287 --- /dev/null +++ b/doc/man/pam_syslog.3.xml @@ -0,0 +1,79 @@ + + + + pam_syslog + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_syslog + pam_vsyslog + send messages to the system logger + + + + + + + #include <syslog.h> + #include <security/pam_ext.h> + + void pam_syslog + const pam_handle_t *pamh + int priority + const char *fmt + ... + + + void pam_vsyslog + const pam_handle_t *pamh + int priority + const char *fmt + va_list args + + + + + + DESCRIPTION + + The pam_syslog function logs messages using + + syslog3 + and is intended for internal use by Linux-PAM and + PAM service modules. The priority argument is + formed by ORing the facility and the level values as documented + in the + syslog3 + manual page. + + + The pam_vsyslog function performs the same + task as pam_syslog() with the difference + that it takes a set of arguments which have been obtained using + the + stdarg3 + variable argument list macros. + + + + + SEE ALSO + + + pam8 + + + + + + STANDARDS + + The pam_syslog and pam_vsyslog + functions are Linux-PAM extensions. + + + + \ No newline at end of file diff --git a/doc/man/pam_xauth_data.3.xml b/doc/man/pam_xauth_data.3.xml new file mode 100644 index 0000000..447a9c2 --- /dev/null +++ b/doc/man/pam_xauth_data.3.xml @@ -0,0 +1,91 @@ + + + + pam_xauth_data + 3 + Linux-PAM + Linux-PAM Manual + + + + pam_xauth_data + structure containing X authentication data + + + + + + + #include <security/pam_appl.h> + + +struct pam_xauth_data { + int namelen; + char *name; + int datalen; + char *data; +}; + + + + + DESCRIPTION + + The pam_xauth_data structure contains X + authentication data used to make a connection to an X display. + Using this mechanism, an application can communicate X + authentication data to PAM service modules. This allows modules to + make a connection to the user's X display in order to label the + user's session on login, display visual feedback or for other + purposes. + + + The name field contains the name of the + authentication method, such as "MIT-MAGIC-COOKIE-1". The + namelen field contains the length of this string, + not including the trailing NUL character. + + + The data field contains the authentication + method-specific data corresponding to the specified name. The + datalen field contains its length in bytes. + + + The X authentication data can be changed with the + PAM_XAUTH_DATA item. It can be queried and + set with + + pam_get_item3 + + and + + pam_set_item 3 + respectively. The value used to set it should be + a pointer to a pam_xauth_data structure. An internal copy of both + the structure itself and its fields is made by PAM when setting the + item. + + + + + SEE ALSO + + + pam_start3 + , + + pam_get_item3 + , + + + + + STANDARDS + + The pam_xauth_data structure and + PAM_XAUTH_DATA item are + Linux-PAM extensions. + + + + \ No newline at end of file diff --git a/doc/meson.build b/doc/meson.build new file mode 100644 index 0000000..afe1585 --- /dev/null +++ b/doc/meson.build @@ -0,0 +1,26 @@ +custom_man_xsl = custom_target( + 'custom-man.xsl', + input: 'custom-man.xsl.in', + output: ['custom-man.xsl'], + command: [ + redir_exe, + '@INPUT@', + '@OUTPUT@', + 'sed', + 's+MAN_STYLESHEET+' + man_stylesheet + '+g' + ] +) + +install_data( + 'index.html', + install_dir: htmldir, + install_tag: 'doc', +) + +install_html = files('install-html.sh') + +subdir('man') +subdir('specs') +subdir('sag') +subdir('adg') +subdir('mwg') diff --git a/doc/mwg/Linux-PAM_MWG.xml b/doc/mwg/Linux-PAM_MWG.xml new file mode 100644 index 0000000..046c3c4 --- /dev/null +++ b/doc/mwg/Linux-PAM_MWG.xml @@ -0,0 +1,604 @@ + + + The Linux-PAM Module Writers' Guide + + Andrew G.Morganmorgan@kernel.org + ThorstenKukukkukuk@thkukuk.de + + Version 1.1.2, 31. August 2010 + + + This manual documents what a programmer needs to know in order + to write a module that conforms to the + Linux-PAM standard.It also + discusses some security issues from the point of view of the + module programmer. + + + + + + Introduction +
+ Description + + Linux-PAM (Pluggable Authentication + Modules for Linux) is a library that enables the local system + administrator to choose how individual applications authenticate + users. For an overview of the + Linux-PAM library see the + Linux-PAM System Administrators' Guide. + + + A Linux-PAM module is a single + executable binary file that can be loaded by the + Linux-PAM interface library. + This PAM library is configured locally with a system file, + /etc/pam.conf, to authenticate a user + request via the locally available authentication modules. The + modules themselves will usually be located in the directory + /lib/security (or + /lib64/security, depending on the architecture) + and take the form of dynamically loadable object files (see + + dlopen3 + . Alternatively, the modules can be statically + linked into the Linux-PAM library; + this is mostly to allow Linux-PAM to + be used on platforms without dynamic linking available, but this is + a deprecated functionality. It is the + Linux-PAM interface that is called + by an application and it is the responsibility of the library to + locate, load and call the appropriate functions in a + Linux-PAM-module. + + + Except for the immediate purpose of interacting with the user + (entering a password etc..) the module should never call the + application directly. This exception requires a "conversation + mechanism" which is documented below. + +
+ +
+ Synopsis + +#include <security/pam_modules.h> + +gcc -fPIC -c pam_module.c +gcc -shared -o pam_module.so pam_module.o -lpam + +
+
+ + + What can be expected by the module + + Here we list the interface that the conventions that all + Linux-PAM modules must adhere to. + +
+ + Getting and setting <emphasis>PAM_ITEM</emphasis>s and + <emphasis>data</emphasis> + + + First, we cover what the module should expect from the + Linux-PAM library and a + Linux-PAM aware application. + Essentially this is the libpam.* library. + + + + + + + + + + +
+
+ + Other functions provided by <filename>libpam</filename> + + + +
+
+ + + What is expected of a module + + The module must supply a sub-set of the six functions listed + below. Together they define the function of a + Linux-PAM module. Module developers + are strongly urged to read the comments on security that follow + this list. + +
+ Overview + + The six module functions are grouped into four independent + management groups. These groups are as follows: + authentication, account, + session and password. + To be properly defined, a module must define all functions within + at least one of these groups. A single module may contain the + necessary functions for all four groups. + +
+ Functional independence + + The independence of the four groups of service a module can + offer means that the module should allow for the possibility + that any one of these four services may legitimately be called + in any order. Thus, the module writer should consider the + appropriateness of performing a service without the prior + success of some other part of the module. + + + As an informative example, consider the possibility that an + application applies to change a user's authentication token, + without having first requested that + Linux-PAM authenticate the + user. In some cases this may be deemed appropriate: when + root wants to change the authentication + token of some lesser user. In other cases it may not be + appropriate: when joe maliciously wants + to reset alice's password; or when anyone + other than the user themself wishes to reset their + KERBEROS authentication token. A policy + for this action should be defined by any reasonable + authentication scheme, the module writer should consider + this when implementing a given module. + +
+
+ Minimizing administration problems + + To avoid system administration problems and the poor + construction of a /etc/pam.conf file, + the module developer may define all six of the following + functions. For those functions that would not be called, + the module should return PAM_SERVICE_ERR + and write an appropriate message to the system log. When + this action is deemed inappropriate, the function would + simply return PAM_IGNORE. + +
+
+ Arguments supplied to the module + + The flags argument of each of + the following functions can be logically OR'd with + PAM_SILENT, which is used to inform the + module to not pass any text (errors or + warnings) application. + + + The argc and argv + arguments are taken from the line appropriate to this + module---that is, with the service_name + matching that of the application---in the configuration file + (see the Linux-PAM + System Administrators' Guide). Together these two parameters + provide the number of arguments and an array of pointers to + the individual argument tokens. This will be familiar to C + programmers as the ubiquitous method of passing command arguments + to the function main(). Note, however, that + the first argument (argv[0]) is a true + argument and not the name of the module. + +
+
+
+ Authentication management + + +
+
+ Account management + +
+
+ Session management + + +
+
+ Authentication token management + +
+
+ + + Generic optional arguments + + Here we list the generic arguments that all modules can expect to + be passed. They are not mandatory, and their absence should be + accepted without comment by the module. + + + + debug + + + Use the + pam_syslog3 + call to log debugging information to the system + log files. + + + + + use_first_pass + + + The module should not prompt the user for a password. + Instead, it should obtain the previously typed password + (by a call to pam_get_item() for the + PAM_AUTHTOK item), and use that. If + that doesn't work, then the user will not be authenticated. + (This option is intended for auth and + passwd modules only). + + + + + + + + Programming notes + + Here we collect some pointers for the module writer to bear in mind + when writing/developing a Linux-PAM + compatible module. + + +
+ Security issues for module creation +
+ Sufficient resources + + Care should be taken to ensure that the proper execution + of a module is not compromised by a lack of system resources. + If a module is unable to open sufficient files to perform its + task, it should fail gracefully, or request additional resources. + Specifically, the quantities manipulated by the + setrlimit2 + family of commands should be taken into + consideration. + +
+
+ Who´s who? + + Generally, the module may wish to establish the identity of + the user requesting a service. This may not be the same as + the username returned by pam_get_user(). + Indeed, that is only going to be the name of the user under + whose identity the service will be given. This is not + necessarily the user that requests the service. + + + In other words, user X runs a program that is setuid-Y, it + grants the user to have the permissions of Z. A specific example + of this sort of service request is the su + program: user joe executes + su to become the user jane. + In this situation X=joe, Y=root + and Z=jane. Clearly, it is important that + the module does not confuse these different users and grant an + inappropriate level of privilege. + + + The following is the convention to be adhered to when juggling + user-identities. + + + + + X, the identity of the user invoking the service request. + This is the user identifier; returned by the function + + getuid2 + . + + + + + Y, the privileged identity of the application used to + grant the requested service. This is the + effective user identifier; + returned by the function + geteuid2 + . + + + + + Z, the user under whose identity the service will be granted. + This is the username returned by + pam_get_user() and also stored in the + Linux-PAM item, + PAM_USER. + + + + + Linux-PAM has a place for + an additional user identity that a module may care to make + use of. This is the PAM_RUSER item. + Generally, network sensitive modules/applications may wish + to set/read this item to establish the identity of the user + requesting a service from a remote location. + + + + + Note, if a module wishes to modify the identity of either the + uid or euid of the + running process, it should take care to restore the original + values prior to returning control to the + Linux-PAM library. + +
+
+ Using the conversation function + + Prior to calling the conversation function, the module should + reset the contents of the pointer that will return the applications + response. This is a good idea since the application may fail + to fill the pointer and the module should be in a position to + notice! + + + The module should be prepared for a failure from the + conversation. The generic error would be + PAM_CONV_ERR, but anything other than + PAM_SUCCESS should be treated as + indicating failure. + +
+
+ Authentication tokens + + To ensure that the authentication tokens are not left lying + around the items, PAM_AUTHTOK and + PAM_OLDAUTHTOK, are not available to + the application: they are defined in + <security/pam_modules.h>. This + is ostensibly for security reasons, but a maliciously + programmed application will always have access to all memory + of the process, so it is only superficially enforced. As a + general rule the module should overwrite authentication tokens + as soon as they are no longer needed. Especially before + free()'ing them. The + Linux-PAM library is + required to do this when either of these authentication + token items are (re)set. + + + Not to dwell too little on this concern; should the module + store the authentication tokens either as (automatic) function + variables or using pam_[gs]et_data() the + associated memory should be over-written explicitly before it + is released. In the case of the latter storage mechanism, the + associated cleanup() function should + explicitly overwrite the *data before + free()'ing it: for example, + +/* + * An example cleanup() function for releasing memory that was used to + * store a password. + */ + +int cleanup(pam_handle_t *pamh, void *data, int error_status) +{ + char *xx; + + if ((xx = data)) { + while (*xx) + *xx++ = '\0'; + free(data); + } + return PAM_SUCCESS; +} + + +
+
+
+ Use of <citerefentry> + <refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum> + </citerefentry> + + Only rarely should error information be directed to the user. + Usually, this is to be limited to + sorry you cannot login now + type messages. Information concerning errors in the configuration + file, /etc/pam.conf, or due to some system + failure encountered by the module, should be written to + + syslog3 + with facility-type + LOG_AUTHPRIV. + + + With a few exceptions, the level of logging is, at the discretion + of the module developer. Here is the recommended usage of different + logging levels: + + + + + As a general rule, errors encountered by a module should be + logged at the LOG_ERR level. However, + information regarding an unrecognized argument, passed to a + module from an entry in the /etc/pam.conf + file, is required to be logged at the + LOG_ERR level. + + + + + Debugging information, as activated by the + debug argument to the module in + /etc/pam.conf, should be logged + at the LOG_DEBUG level. + + + + + If a module discovers that its personal configuration + file or some system file it uses for information is + corrupted or somehow unusable, it should indicate this + by logging messages at level, LOG_ALERT. + + + + + Shortages of system resources, such as a failure to + manipulate a file or malloc() failures + should be logged at level LOG_CRIT. + + + + + Authentication failures, associated with an incorrectly + typed password should be logged at level, + LOG_NOTICE. + + + +
+
+ Modules that require system libraries + + Writing a module is much like writing an application. You + have to provide the "conventional hooks" for it to work + correctly, like pam_sm_authenticate() + etc., which would correspond to the main() + function in a normal function. + + + Typically, the author may want to link against some standard system + libraries. As when one compiles a normal program, this can be + done for modules too: you simply append the + -lXXX arguments + for the desired libraries when you create the shared module object. + To make sure a module is linked to the + libwhatever.so library + when it is dlopen()ed, try: + +% gcc -shared -o pam_module.so pam_module.o -lwhatever + + +
+
+ + + An example module + + At some point, we may include a fully commented example of a module in + this document. For now, please look at the modules directory of the + Linux-PAM sources. + + + + + See also + + + + The Linux-PAM System Administrators' Guide. + + + + + The Linux-PAM Application Developers' Guide. + + + + + The V. Samar and R. Schemers (SunSoft), ``UNIFIED LOGIN WITH + PLUGGABLE AUTHENTICATION MODULES'', Open Software Foundation + Request For Comments 86.0, October 1995. + + + + + + + Author/acknowledgments + + This document was written by Andrew G. Morgan (morgan@kernel.org) + with many contributions from + Chris Adams, Peter Allgeyer, Tim Baverstock, Tim Berger, Craig S. Bell, + Derrick J. Brashear, Ben Buxton, Seth Chaiklin, Oliver Crow, Chris Dent, + Marc Ewing, Cristian Gafton, Emmanuel Galanos, Brad M. Garcia, + Eric Hester, Roger Hu, Eric Jacksch, Michael K. Johnson, David Kinchlea, + Olaf Kirch, Marcin Korzonek, Thorsten Kukuk, Stephen Langasek, + Nicolai Langfeldt, Elliot Lee, Luke Kenneth Casson Leighton, + Al Longyear, Ingo Luetkebohle, Marek Michalkiewicz, Robert Milkowski, + Aleph One, Martin Pool, Sean Reifschneider, Jan Rekorajski, Erik Troan, + Theodore Ts'o, Jeff Uphoff, Myles Uyema, Savochkin Andrey Vladimirovich, + Ronald Wahl, David Wood, John Wilmes, Joseph S. D. Yao + and Alex O. Yuriev. + + + Thanks are also due to Sun Microsystems, especially to Vipin Samar and + Charlie Lai for their advice. At an early stage in the development of + Linux-PAM, Sun graciously made the + documentation for their implementation of PAM available. This act + greatly accelerated the development of + Linux-PAM. + + + + + Copyright information for this document + +Copyright (c) 2006 Thorsten Kukuk <kukuk@thkukuk.de> +Copyright (c) 1996-2002 Andrew G. Morgan <morgan@kernel.org> + + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + +1. Redistributions of source code must retain the above copyright + notice, and the entire permission notice in its entirety, + including the disclaimer of warranties. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + + Alternatively, this product may be distributed under the terms of + the GNU General Public License (GPL), in which case the provisions + of the GNU GPL are required instead of the above restrictions. + (This clause is necessary due to a potential bad interaction between + the GNU GPL and the restrictions contained in a BSD-style copyright.) + + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + + +
\ No newline at end of file diff --git a/doc/mwg/html/meson.build b/doc/mwg/html/meson.build new file mode 120000 index 0000000..d6d90ea --- /dev/null +++ b/doc/mwg/html/meson.build @@ -0,0 +1 @@ +../../guide-html-meson.build \ No newline at end of file diff --git a/doc/mwg/meson.build b/doc/mwg/meson.build new file mode 120000 index 0000000..2e19d83 --- /dev/null +++ b/doc/mwg/meson.build @@ -0,0 +1 @@ +../guide-meson.build \ No newline at end of file diff --git a/doc/mwg/pam_conv.xml b/doc/mwg/pam_conv.xml new file mode 100644 index 0000000..2b36950 --- /dev/null +++ b/doc/mwg/pam_conv.xml @@ -0,0 +1,29 @@ +
+ The conversation function + + + + +struct pam_message { + int msg_style; + const char *msg; +}; + +struct pam_response { + char *resp; + int resp_retcode; +}; + +struct pam_conv { + int (*conv)(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr); + void *appdata_ptr; +}; + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_fail_delay.xml b/doc/mwg/pam_fail_delay.xml new file mode 100644 index 0000000..d602a1f --- /dev/null +++ b/doc/mwg/pam_fail_delay.xml @@ -0,0 +1,12 @@ +
+ Request a delay on failure + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_get_data.xml b/doc/mwg/pam_get_data.xml new file mode 100644 index 0000000..e1342d1 --- /dev/null +++ b/doc/mwg/pam_get_data.xml @@ -0,0 +1,12 @@ +
+ Get module internal data + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_get_item.xml b/doc/mwg/pam_get_item.xml new file mode 100644 index 0000000..e0635d2 --- /dev/null +++ b/doc/mwg/pam_get_item.xml @@ -0,0 +1,12 @@ +
+ Getting PAM items + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_get_user.xml b/doc/mwg/pam_get_user.xml new file mode 100644 index 0000000..3b79fe0 --- /dev/null +++ b/doc/mwg/pam_get_user.xml @@ -0,0 +1,12 @@ +
+ Get user name + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_getenv.xml b/doc/mwg/pam_getenv.xml new file mode 100644 index 0000000..f7b483e --- /dev/null +++ b/doc/mwg/pam_getenv.xml @@ -0,0 +1,12 @@ +
+ Get a PAM environment variable + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_getenvlist.xml b/doc/mwg/pam_getenvlist.xml new file mode 100644 index 0000000..4433c04 --- /dev/null +++ b/doc/mwg/pam_getenvlist.xml @@ -0,0 +1,12 @@ +
+ Getting the PAM environment + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_putenv.xml b/doc/mwg/pam_putenv.xml new file mode 100644 index 0000000..6378a15 --- /dev/null +++ b/doc/mwg/pam_putenv.xml @@ -0,0 +1,12 @@ +
+ Set or change PAM environment variable + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_set_data.xml b/doc/mwg/pam_set_data.xml new file mode 100644 index 0000000..3fb3b1f --- /dev/null +++ b/doc/mwg/pam_set_data.xml @@ -0,0 +1,12 @@ +
+ Set module internal data + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_set_item.xml b/doc/mwg/pam_set_item.xml new file mode 100644 index 0000000..7a8ee8d --- /dev/null +++ b/doc/mwg/pam_set_item.xml @@ -0,0 +1,12 @@ +
+ Setting PAM items + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_sm_acct_mgmt.xml b/doc/mwg/pam_sm_acct_mgmt.xml new file mode 100644 index 0000000..c17a9bf --- /dev/null +++ b/doc/mwg/pam_sm_acct_mgmt.xml @@ -0,0 +1,12 @@ +
+ Service function for account management + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_sm_authenticate.xml b/doc/mwg/pam_sm_authenticate.xml new file mode 100644 index 0000000..138fc1f --- /dev/null +++ b/doc/mwg/pam_sm_authenticate.xml @@ -0,0 +1,12 @@ +
+ Service function for user authentication + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_sm_chauthtok.xml b/doc/mwg/pam_sm_chauthtok.xml new file mode 100644 index 0000000..546ae66 --- /dev/null +++ b/doc/mwg/pam_sm_chauthtok.xml @@ -0,0 +1,12 @@ +
+ Service function to alter authentication token + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_sm_close_session.xml b/doc/mwg/pam_sm_close_session.xml new file mode 100644 index 0000000..69140b8 --- /dev/null +++ b/doc/mwg/pam_sm_close_session.xml @@ -0,0 +1,12 @@ +
+ Service function to terminate session management + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_sm_open_session.xml b/doc/mwg/pam_sm_open_session.xml new file mode 100644 index 0000000..aba28a3 --- /dev/null +++ b/doc/mwg/pam_sm_open_session.xml @@ -0,0 +1,12 @@ +
+ Service function to start session management + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_sm_setcred.xml b/doc/mwg/pam_sm_setcred.xml new file mode 100644 index 0000000..36e43c0 --- /dev/null +++ b/doc/mwg/pam_sm_setcred.xml @@ -0,0 +1,12 @@ +
+ Service function to alter credentials + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/mwg/pam_strerror.xml b/doc/mwg/pam_strerror.xml new file mode 100644 index 0000000..e4e1c56 --- /dev/null +++ b/doc/mwg/pam_strerror.xml @@ -0,0 +1,12 @@ +
+ Strings describing PAM error codes + + + +
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/Linux-PAM_SAG.xml b/doc/sag/Linux-PAM_SAG.xml new file mode 100644 index 0000000..205ab98 --- /dev/null +++ b/doc/sag/Linux-PAM_SAG.xml @@ -0,0 +1,520 @@ + + + The Linux-PAM System Administrators' Guide + + Andrew G.Morganmorgan@kernel.org + ThorstenKukukkukuk@thkukuk.de + + Version 1.1.2, 31. August 2010 + + + This manual documents what a system-administrator needs to know about + the Linux-PAM library. It covers the + correct syntax of the PAM configuration file and discusses strategies + for maintaining a secure system. + + + + + + Introduction + + Linux-PAM (Pluggable Authentication + Modules for Linux) is a suite of shared libraries that enable the + local system administrator to choose how applications authenticate users. + + + In other words, without (rewriting and) recompiling a PAM-aware + application, it is possible to switch between the authentication + mechanism(s) it uses. Indeed, one may entirely upgrade the local + authentication system without touching the applications themselves. + + + Historically an application that has required a given user to be + authenticated, has had to be compiled to use a specific authentication + mechanism. For example, in the case of traditional UN*X systems, the + identity of the user is verified by the user entering a correct + password. This password, after being prefixed by a two character + ``salt'', is encrypted (with crypt(3)). The user is then authenticated + if this encrypted password is identical to the second field of the + user's entry in the system password database (the + /etc/passwd file). On such systems, most if + not all forms of privileges are granted based on this single + authentication scheme. Privilege comes in the form of a personal + user-identifier (UID) and membership of various groups. Services and + applications are available based on the personal and group identity + of the user. Traditionally, group membership has been assigned based + on entries in the /etc/group file. + + + It is the purpose of the Linux-PAM + project to separate the development of privilege granting software + from the development of secure and appropriate authentication schemes. + This is accomplished by providing a library of functions that an + application may use to request that a user be authenticated. This + PAM library is configured locally with a system file, + /etc/pam.conf (or a series of configuration + files located in /etc/pam.d/) to authenticate a + user request via the locally available authentication modules. The + modules themselves will usually be located in the directory + /lib/security or + /lib64/security and take the form of dynamically + loadable object files (see + dlopen3 + ). + + + + + Some comments on the text + + Before proceeding to read the rest of this document, it should be + noted that the text assumes that certain files are placed in certain + directories. Where they have been specified, the conventions we adopt + here for locating these files are those of the relevant RFC (RFC-86.0, + see bibliography"). If you are + using a distribution of Linux (or some other operating system) that + supports PAM but chooses to distribute these files in a different way + you should be careful when copying examples directly from the text. + + + As an example of the above, where it is explicit, the text assumes + that PAM loadable object files (the + modules) are to be located in + the following directory: /lib/security/ or + /lib64/security depending on the architecture. + This is generally the location that seems to be compatible with the + Filesystem Hierarchy Standard (FHS). On Solaris, which has its own + licensed version of PAM, and some other implementations of UN*X, + these files can be found in /usr/lib/security. + Please be careful to perform the necessary transcription when using + the examples from the text. + + + + + Overview + + For the uninitiated, we begin by considering an example. We take an + application that grants some service to users; + login is one such program. + Login does two things, it first establishes that + the requesting user is whom they claim to be and second provides + them with the requested service: in the case of + login the service is a command shell + (bash, tcsh, zsh, etc.) running with the identity of the user. + + + Traditionally, the former step is achieved by the + login application prompting the user for a + password and then verifying that it agrees with that located on + the system; hence verifying that as far as the system is concerned + the user is who they claim to be. This is the task that is delegated + to Linux-PAM. + + + From the perspective of the application programmer (in this case + the person that wrote the login application), + Linux-PAM takes care of this + authentication task -- verifying the identity of the user. + + + The flexibility of Linux-PAM is + that you, the system administrator, have + the freedom to stipulate which authentication scheme is to be + used. You have the freedom to set the scheme for any/all + PAM-aware applications on your Linux system. That is, you can + authenticate from anything as naive as + simple trust (pam_permit) + to something as paranoid as a combination of a retinal scan, a + voice print and a one-time password! + + + To illustrate the flexibility you face, consider the following + situation: system administrators (parents) wish to improve the + mathematical ability of their users (children). Their favorite + ``Shoot 'em up game'' (PAM-aware of course) can be configured to + authenticate them with a request for the product of a couple of + random numbers less than 12. It is clear that if the game is any + good they will soon learn their + multiplication tables. As they mature, the + authentication can be upgraded to include (long) division! + + + Linux-PAM deals with four + separate types of (management) task. These are: + authentication management; + account management; + session management; and + password management. + The association of the preferred management scheme with the behavior + of an application is made with entries in the relevant + Linux-PAM configuration file. + The management functions are performed by modules + specified in the configuration file. The syntax for this + file is discussed in the section + below. + + + Here is a figure that describes the overall organization of + Linux-PAM: + + +----------------+ + | application: X | + +----------------+ / +----------+ +================+ + | authentication-[---->--\--] Linux- |--<--| PAM config file| + | + [----<--/--] PAM | |================| + |[conversation()][--+ \ | | | X auth .. a.so | + +----------------+ | / +-n--n-----+ | X auth .. b.so | + | | | __| | | _____/ + | service user | A | | |____,-----' + | | | V A + +----------------+ +------|-----|---------+ -----+------+ + +---u-----u----+ | | | + | auth.... |--[ a ]--[ b ]--[ c ] + +--------------+ + | acct.... |--[ b ]--[ d ] + +--------------+ + | password |--[ b ]--[ c ] + +--------------+ + | session |--[ e ]--[ c ] + +--------------+ + + By way of explanation, the left of the figure represents the + application; application X. Such an application interfaces with the + Linux-PAM library and knows none of + the specifics of its configured authentication method. The + Linux-PAM library (in the center) + consults the contents of the PAM configuration file and loads the + modules that are appropriate for application-X. These modules fall + into one of four management groups (lower-center) and are stacked in + the order they appear in the configuration file. These modules, when + called by Linux-PAM, perform the + various authentication tasks for the application. Textual information, + required from/or offered to the user, can be exchanged through the + use of the application-supplied conversation + function. + + + If a program is going to use PAM, then it has to have PAM + functions explicitly coded into the program. If you have + access to the source code you can add the appropriate PAM + functions. If you do not have access to the source code, and + the binary does not have the PAM functions included, then + it is not possible to use PAM. + + + + + The Linux-PAM configuration file + +
+ Configuration file syntax + +
+
+ Directory based configuration + +
+
+ Example configuration file entries + + In this section, we give some examples of entries that can + be present in the Linux-PAM + configuration file. As a first attempt at configuring your + system you could do worse than to implement these. + + + If a system is to be considered secure, it had better have a + reasonably secure 'other entry. + The following is a paranoid setting (which is not a bad place + to start!): + + +# +# default; deny access +# +other auth required pam_deny.so +other account required pam_deny.so +other password required pam_deny.so +other session required pam_deny.so + + + Whilst fundamentally a secure default, this is not very + sympathetic to a misconfigured system. For example, such + a system is vulnerable to locking everyone out should the + rest of the file become badly written. + + + The module pam_deny (documented in a + later section) is not very + sophisticated. For example, it logs no information when it + is invoked so unless the users of a system contact the + administrator when failing to execute a service application, + the administrator may go for a long while in ignorance of the + fact that the system is misconfigured. + + + The addition of the following line before those in the above + example would provide a suitable warning to the administrator. + + +# +# default; wake up! This application is not configured +# +other auth required pam_warn.so +other password required pam_warn.so + + + Having two 'other auth' lines is an + example of stacking. + + + On a system that uses the /etc/pam.d/ + configuration, the corresponding default setup would be + achieved with the following file: + + +# +# default configuration: /etc/pam.d/other +# +auth required pam_warn.so +auth required pam_deny.so +account required pam_deny.so +password required pam_warn.so +password required pam_deny.so +session required pam_deny.so + + + This is the only explicit example we give for an + /etc/pam.d/ file. In general, it + should be clear how to transpose the remaining examples + to this configuration scheme. + + + On a less sensitive computer, one on which the system + administrator wishes to remain ignorant of much of the + power of Linux-PAM, the + following selection of lines (in + /etc/pam.d/other) is likely to + mimic the historically familiar Linux setup. + + +# +# default; standard UN*X access +# +auth required pam_unix.so +account required pam_unix.so +password required pam_unix.so +session required pam_unix.so + + + In general this will provide a starting place for most applications. + +
+
+ + + Security issues +
+ If something goes wrong + + Linux-PAM has the potential + to seriously change the security of your system. You can + choose to have no security or absolute security (no access + permitted). In general, Linux-PAM + errs towards the latter. Any number of configuration errors + can disable access to your system partially, or completely. + + + The most dramatic problem that is likely to be encountered when + configuring Linux-PAM is that of + deleting the configuration file(s): + /etc/pam.d/* and/or + /etc/pam.conf. This will lock you out of + your own system! + + + To recover, your best bet is to restore the system from a + backup or boot the system into a rescue system and correct + things from there. + +
+
+ Avoid having a weak `other' configuration + + It is not a good thing to have a weak default + (other) entry. + This service is the default configuration for all PAM aware + applications and if it is weak, your system is likely to be + vulnerable to attack. + + + Here is a sample "other" configuration file. The + pam_deny module will deny access and the + pam_warn module will send a syslog message + to auth.notice: + + +# +# The PAM configuration file for the `other' service +# +auth required pam_deny.so +auth required pam_warn.so +account required pam_deny.so +account required pam_warn.so +password required pam_deny.so +password required pam_warn.so +session required pam_deny.so +session required pam_warn.so + +
+
+ + + A reference guide for available modules + + Here, we collect together the descriptions of the various modules + coming with Linux-PAM. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + See also + + + + The Linux-PAM Application Writers' Guide. + + + + + The Linux-PAM Module Writers' Guide. + + + + + The V. Samar and R. Schemers (SunSoft), ``UNIFIED LOGIN WITH + PLUGGABLE AUTHENTICATION MODULES'', Open Software Foundation + Request For Comments 86.0, October 1995. + + + + + + + Author/acknowledgments + + This document was written by Andrew G. Morgan (morgan@kernel.org) + with many contributions from + Chris Adams, Peter Allgeyer, Tim Baverstock, Tim Berger, + Craig S. Bell, Derrick J. Brashear, Ben Buxton, Seth Chaiklin, + Oliver Crow, Chris Dent, Marc Ewing, Cristian Gafton, + Emmanuel Galanos, Brad M. Garcia, Eric Hester, Michel D'Hooge, + Roger Hu, Eric Jacksch, Michael K. Johnson, David Kinchlea, + Olaf Kirch, Marcin Korzonek, Thorsten Kukuk, Stephen Langasek, + Nicolai Langfeldt, Elliot Lee, Luke Kenneth Casson Leighton, + Al Longyear, Ingo Luetkebohle, Marek Michalkiewicz, + Robert Milkowski, Aleph One, Martin Pool, Sean Reifschneider, + Jan Rekorajski, Erik Troan, Theodore Ts'o, Jeff Uphoff, Myles Uyema, + Savochkin Andrey Vladimirovich, Ronald Wahl, David Wood, John Wilmes, + Joseph S. D. Yao and Alex O. Yuriev. + + + Thanks are also due to Sun Microsystems, especially to Vipin Samar and + Charlie Lai for their advice. At an early stage in the development of + Linux-PAM, Sun graciously made the + documentation for their implementation of PAM available. This act + greatly accelerated the development of + Linux-PAM. + + + + + Copyright information for this document + +Copyright (c) 2006 Thorsten Kukuk <kukuk@thkukuk.de> +Copyright (c) 1996-2002 Andrew G. Morgan <morgan@kernel.org> + + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + +1. Redistributions of source code must retain the above copyright + notice, and the entire permission notice in its entirety, + including the disclaimer of warranties. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + + Alternatively, this product may be distributed under the terms of + the GNU General Public License (GPL), in which case the provisions + of the GNU GPL are required instead of the above restrictions. + (This clause is necessary due to a potential bad interaction between + the GNU GPL and the restrictions contained in a BSD-style copyright.) + + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + + +
diff --git a/doc/sag/html/meson.build b/doc/sag/html/meson.build new file mode 120000 index 0000000..d6d90ea --- /dev/null +++ b/doc/sag/html/meson.build @@ -0,0 +1 @@ +../../guide-html-meson.build \ No newline at end of file diff --git a/doc/sag/meson.build b/doc/sag/meson.build new file mode 120000 index 0000000..2e19d83 --- /dev/null +++ b/doc/sag/meson.build @@ -0,0 +1 @@ +../guide-meson.build \ No newline at end of file diff --git a/doc/sag/pam_access.xml b/doc/sag/pam_access.xml new file mode 100644 index 0000000..75f14b3 --- /dev/null +++ b/doc/sag/pam_access.xml @@ -0,0 +1,30 @@ +
+ pam_access - logdaemon style login access control + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_canonicalize_user.xml b/doc/sag/pam_canonicalize_user.xml new file mode 100644 index 0000000..8c42f39 --- /dev/null +++ b/doc/sag/pam_canonicalize_user.xml @@ -0,0 +1,24 @@ +
+ pam_canonicalize_user - get user name and canonicalize it + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
diff --git a/doc/sag/pam_debug.xml b/doc/sag/pam_debug.xml new file mode 100644 index 0000000..0c8aa94 --- /dev/null +++ b/doc/sag/pam_debug.xml @@ -0,0 +1,24 @@ +
+ pam_debug - debug the PAM stack + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_deny.xml b/doc/sag/pam_deny.xml new file mode 100644 index 0000000..fdd2aaa --- /dev/null +++ b/doc/sag/pam_deny.xml @@ -0,0 +1,24 @@ +
+ pam_deny - locking-out PAM module + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_echo.xml b/doc/sag/pam_echo.xml new file mode 100644 index 0000000..e4de886 --- /dev/null +++ b/doc/sag/pam_echo.xml @@ -0,0 +1,24 @@ +
+ pam_echo - print text messages + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_env.xml b/doc/sag/pam_env.xml new file mode 100644 index 0000000..68b7c4f --- /dev/null +++ b/doc/sag/pam_env.xml @@ -0,0 +1,30 @@ +
+ pam_env - set/unset environment variables + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_exec.xml b/doc/sag/pam_exec.xml new file mode 100644 index 0000000..859bb3b --- /dev/null +++ b/doc/sag/pam_exec.xml @@ -0,0 +1,24 @@ +
+ pam_exec - call an external command + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_faildelay.xml b/doc/sag/pam_faildelay.xml new file mode 100644 index 0000000..9690208 --- /dev/null +++ b/doc/sag/pam_faildelay.xml @@ -0,0 +1,24 @@ +
+ pam_faildelay - change the delay on failure per-application + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_faillock.xml b/doc/sag/pam_faillock.xml new file mode 100644 index 0000000..32777b1 --- /dev/null +++ b/doc/sag/pam_faillock.xml @@ -0,0 +1,27 @@ +
+ pam_faillock - temporarily locking access based on failed authentication attempts during an interval + + + + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_filter.xml b/doc/sag/pam_filter.xml new file mode 100644 index 0000000..56af28c --- /dev/null +++ b/doc/sag/pam_filter.xml @@ -0,0 +1,24 @@ +
+ pam_filter - filter module + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_ftp.xml b/doc/sag/pam_ftp.xml new file mode 100644 index 0000000..13fe40a --- /dev/null +++ b/doc/sag/pam_ftp.xml @@ -0,0 +1,24 @@ +
+ pam_ftp - module for anonymous access + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_group.xml b/doc/sag/pam_group.xml new file mode 100644 index 0000000..e4efc03 --- /dev/null +++ b/doc/sag/pam_group.xml @@ -0,0 +1,30 @@ +
+ pam_group - module to modify group access + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_issue.xml b/doc/sag/pam_issue.xml new file mode 100644 index 0000000..f56cc46 --- /dev/null +++ b/doc/sag/pam_issue.xml @@ -0,0 +1,24 @@ +
+ pam_issue - add issue file to user prompt + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_keyinit.xml b/doc/sag/pam_keyinit.xml new file mode 100644 index 0000000..d801351 --- /dev/null +++ b/doc/sag/pam_keyinit.xml @@ -0,0 +1,24 @@ +
+ pam_keyinit - display the keyinit file + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_lastlog.xml b/doc/sag/pam_lastlog.xml new file mode 100644 index 0000000..1c9c6b2 --- /dev/null +++ b/doc/sag/pam_lastlog.xml @@ -0,0 +1,24 @@ +
+ pam_lastlog - display date of last login + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_limits.xml b/doc/sag/pam_limits.xml new file mode 100644 index 0000000..f03a1e4 --- /dev/null +++ b/doc/sag/pam_limits.xml @@ -0,0 +1,30 @@ +
+ pam_limits - limit resources + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_listfile.xml b/doc/sag/pam_listfile.xml new file mode 100644 index 0000000..66d7a82 --- /dev/null +++ b/doc/sag/pam_listfile.xml @@ -0,0 +1,24 @@ +
+ pam_listfile - deny or allow services based on an arbitrary file + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_localuser.xml b/doc/sag/pam_localuser.xml new file mode 100644 index 0000000..a3cee75 --- /dev/null +++ b/doc/sag/pam_localuser.xml @@ -0,0 +1,24 @@ +
+ pam_localuser - require users to be listed in /etc/passwd + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_loginuid.xml b/doc/sag/pam_loginuid.xml new file mode 100644 index 0000000..fc4a096 --- /dev/null +++ b/doc/sag/pam_loginuid.xml @@ -0,0 +1,24 @@ +
+ pam_loginuid - record user's login uid to the process attribute + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_mail.xml b/doc/sag/pam_mail.xml new file mode 100644 index 0000000..6b76770 --- /dev/null +++ b/doc/sag/pam_mail.xml @@ -0,0 +1,24 @@ +
+ pam_mail - inform about available mail + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_mkhomedir.xml b/doc/sag/pam_mkhomedir.xml new file mode 100644 index 0000000..141395c --- /dev/null +++ b/doc/sag/pam_mkhomedir.xml @@ -0,0 +1,24 @@ +
+ pam_mkhomedir - create users home directory + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_motd.xml b/doc/sag/pam_motd.xml new file mode 100644 index 0000000..9af77bb --- /dev/null +++ b/doc/sag/pam_motd.xml @@ -0,0 +1,24 @@ +
+ pam_motd - display the motd file + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_namespace.xml b/doc/sag/pam_namespace.xml new file mode 100644 index 0000000..e18bc0f --- /dev/null +++ b/doc/sag/pam_namespace.xml @@ -0,0 +1,30 @@ +
+ pam_namespace - setup a private namespace + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_nologin.xml b/doc/sag/pam_nologin.xml new file mode 100644 index 0000000..f2acf49 --- /dev/null +++ b/doc/sag/pam_nologin.xml @@ -0,0 +1,24 @@ +
+ pam_nologin - prevent non-root users from login + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_permit.xml b/doc/sag/pam_permit.xml new file mode 100644 index 0000000..52548c0 --- /dev/null +++ b/doc/sag/pam_permit.xml @@ -0,0 +1,24 @@ +
+ pam_permit - the promiscuous module + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_pwhistory.xml b/doc/sag/pam_pwhistory.xml new file mode 100644 index 0000000..867a1bc --- /dev/null +++ b/doc/sag/pam_pwhistory.xml @@ -0,0 +1,27 @@ +
+ pam_pwhistory - grant access using .pwhistory file + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_rhosts.xml b/doc/sag/pam_rhosts.xml new file mode 100644 index 0000000..f70b1fb --- /dev/null +++ b/doc/sag/pam_rhosts.xml @@ -0,0 +1,24 @@ +
+ pam_rhosts - grant access using .rhosts file + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_rootok.xml b/doc/sag/pam_rootok.xml new file mode 100644 index 0000000..ab4b443 --- /dev/null +++ b/doc/sag/pam_rootok.xml @@ -0,0 +1,24 @@ +
+ pam_rootok - gain only root access + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_securetty.xml b/doc/sag/pam_securetty.xml new file mode 100644 index 0000000..9bd9fe2 --- /dev/null +++ b/doc/sag/pam_securetty.xml @@ -0,0 +1,24 @@ +
+ pam_securetty - limit root login to special devices + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_selinux.xml b/doc/sag/pam_selinux.xml new file mode 100644 index 0000000..cb64bcf --- /dev/null +++ b/doc/sag/pam_selinux.xml @@ -0,0 +1,24 @@ +
+ pam_selinux - set the default security context + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_sepermit.xml b/doc/sag/pam_sepermit.xml new file mode 100644 index 0000000..2642661 --- /dev/null +++ b/doc/sag/pam_sepermit.xml @@ -0,0 +1,27 @@ +
+ pam_sepermit - allow/reject access based on SELinux mode + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_setquota.xml b/doc/sag/pam_setquota.xml new file mode 100644 index 0000000..01d1873 --- /dev/null +++ b/doc/sag/pam_setquota.xml @@ -0,0 +1,24 @@ +
+ pam_setquota - set or modify disk quotas on session start + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_shells.xml b/doc/sag/pam_shells.xml new file mode 100644 index 0000000..6765a19 --- /dev/null +++ b/doc/sag/pam_shells.xml @@ -0,0 +1,24 @@ +
+ pam_shells - check for valid login shell + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_succeed_if.xml b/doc/sag/pam_succeed_if.xml new file mode 100644 index 0000000..7c9f493 --- /dev/null +++ b/doc/sag/pam_succeed_if.xml @@ -0,0 +1,24 @@ +
+ pam_succeed_if - test account characteristics + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_time.xml b/doc/sag/pam_time.xml new file mode 100644 index 0000000..e15d20a --- /dev/null +++ b/doc/sag/pam_time.xml @@ -0,0 +1,30 @@ +
+ pam_time - time controlled access + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_timestamp.xml b/doc/sag/pam_timestamp.xml new file mode 100644 index 0000000..dfe87e7 --- /dev/null +++ b/doc/sag/pam_timestamp.xml @@ -0,0 +1,30 @@ +
+ pam_timestamp - authenticate using cached successful authentication attempts + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_tty_audit.xml b/doc/sag/pam_tty_audit.xml new file mode 100644 index 0000000..44de810 --- /dev/null +++ b/doc/sag/pam_tty_audit.xml @@ -0,0 +1,27 @@ +
+ pam_tty_audit - enable/disable tty auditing + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_umask.xml b/doc/sag/pam_umask.xml new file mode 100644 index 0000000..2fb200b --- /dev/null +++ b/doc/sag/pam_umask.xml @@ -0,0 +1,24 @@ +
+ pam_umask - set the file mode creation mask + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_unix.xml b/doc/sag/pam_unix.xml new file mode 100644 index 0000000..bb34122 --- /dev/null +++ b/doc/sag/pam_unix.xml @@ -0,0 +1,24 @@ +
+ pam_unix - traditional password authentication + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_userdb.xml b/doc/sag/pam_userdb.xml new file mode 100644 index 0000000..3c1bbc1 --- /dev/null +++ b/doc/sag/pam_userdb.xml @@ -0,0 +1,24 @@ +
+ pam_userdb - authenticate against a db database + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_warn.xml b/doc/sag/pam_warn.xml new file mode 100644 index 0000000..0f1376b --- /dev/null +++ b/doc/sag/pam_warn.xml @@ -0,0 +1,24 @@ +
+ pam_warn - logs all PAM items + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_wheel.xml b/doc/sag/pam_wheel.xml new file mode 100644 index 0000000..76f0204 --- /dev/null +++ b/doc/sag/pam_wheel.xml @@ -0,0 +1,24 @@ +
+ pam_wheel - only permit root access to members of group wheel + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/sag/pam_xauth.xml b/doc/sag/pam_xauth.xml new file mode 100644 index 0000000..4c9ba35 --- /dev/null +++ b/doc/sag/pam_xauth.xml @@ -0,0 +1,24 @@ +
+ pam_xauth - forward xauth keys between users + + + +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
\ No newline at end of file diff --git a/doc/specs/draft-morgan-pam.raw b/doc/specs/draft-morgan-pam.raw new file mode 100644 index 0000000..8fdb050 --- /dev/null +++ b/doc/specs/draft-morgan-pam.raw @@ -0,0 +1,764 @@ +Open-PAM working group ## A.G. Morgan +Internet Draft: ## Dec 8, 2001 +Document: draft-morgan-pam-08.txt ## +Expires: June 8, 2002 ## +Obsoletes: draft-morgan-pam-07.txt## + +## Pluggable Authentication Modules (PAM) ## + +#$ Status of this memo + +This document is a draft specification. Its contents are subject to +change with revision. The latest version of this draft may be obtained +from here: + + http://www.kernel.org/pub/linux/libs/pam/pre/doc/ + +As + + Linux-PAM-'version'-docs.tar.gz + +It is also contained in the Linux-PAM tar ball. + +#$ Abstract + +This document is concerned with the definition of a general +infrastructure for module based authentication. The infrastructure is +named Pluggable Authentication Modules (PAM for short). + +#$ Introduction + +Computers are tools. They provide services to people and other +computers (collectively we shall call these _users_ entities). In +order to provide convenient, reliable and individual service to +different entities, it is common for entities to be labelled. Having +defined a label as referring to a some specific entity, the label is +used for the purpose of protecting and allocating data resources. + +All modern operating systems have a notion of labelled entities and +all modern operating systems face a common problem: how to +authenticate the association of a predefined label with applicant +entities. + +There are as many authentication methods as one might care to count. +None of them are perfect and none of them are invulnerable. In +general, any given authentication method becomes weaker over time. It +is common then for new authentication methods to be developed in +response to newly discovered weaknesses in the old authentication +methods. + +The problem with inventing new authentication methods is the fact that +old applications do not support them. This contributes to an inertia +that discourages the overhaul of weakly protected systems. Another +problem is that individuals (people) are frequently powerless to layer +the protective authentication around their systems. They are forced +to rely on single (lowest common denominator) authentication schemes +even in situations where this is far from appropriate. + +PAM, as discussed in this document, is a generalization of the +approach first introduced in [#$R#{OSF_RFC_PAM}]. In short, it is a +general framework of interfaces that abstract the process of +authentication. With PAM, a service provider can custom protect +individual services to the level that they deem is appropriate. + +PAM has nothing explicit to say about transport layer encryption. +Within the context of this document encryption and/or compression of +data exchanges are application specific (strictly between client and +server) and orthogonal to the process of authentication. + +#$ Definitions + +Here we pose the authentication problem as one of configuring defined +interfaces between two entities. + +#$$#{players} Players in the authentication process + +PAM reserves the following words to specify unique entities in the +authentication process: + + applicant + the entity (user) initiating an application for service + [PAM associates the PAM_RUSER _item_ with this requesting user]. + + arbitrator + the entity (user) under whose identity the service application + is negotiated and with whose authority service is granted. + + user + the entity (user) whose identity is being authenticated + [PAM associates the PAM_USER _item_ with this identity]. + + server + the application that provides service, or acts as an + authenticated gateway to the requested service. This + application is completely responsible for the server end of + the transport layer connecting the server to the client. + PAM makes no assumptions about how data is encapsulated for + exchanges between the server and the client, only that full + octet sequences can be freely exchanged without corruption. + + client + application providing the direct/primary interface to + applicant. This application is completely responsible + for the client end of the transport layer connecting the + server to the client. PAM makes no assumptions about how data + is encapsulated for exchanges between the server and the + client, only that full octet sequences can be freely + exchanged without corruption. + + module + authentication binary that provides server-side support for + some (arbitrary) authentication method. + + agent + authentication binary that provides client-side support for + some (arbitrary) authentication method. + +Here is a diagram to help orient the reader: + +## +-------+ +--------+ ## +## . . . . .| agent | .| module | ## +## . +-------+ .+--------+ ## +## V | . | ## +## . | V | ## +## +---------+ +-------+ . +------+ ## +## | | |libpamc| . |libpam| ## +## | | +-------+ . +------+ ## +## |applicant| | . | ## +## | | +--------+ +----------+ ## +## | |---| client |-----------| server | ## +## +---------+ +--------+ +----------+ ## + +Solid lines connecting the boxes represent two-way interaction. The +dotted-directed lines indicate an optional connection between the +plugin module (agent) and the server (applicant). In the case of the +module, this represents the module invoking the 'conversation' +callback function provided to libpam by the server application when it +initializes the libpam library. In the case of the agent, this may +be some out-of-PAM API interaction (for example directly displaying a +dialog box under X). + +#$$ Defined Data Types + +In this draft, we define two composite data types, the text string and +the binary prompt. They are the data types used to communicate +authentication requests and responses. + +#$$$#{text_string} text string + +The text string is a simple sequence of non-NUL (NUL = 0x00) +octets. Terminated with a single NUL (0x00) octet. The character set +employed in the octet sequence may be negotiated out of band, but +defaults to utf-8. + +## --------------------------- ## +## [ character data | NUL ] ## +## [ octet sequence | 0x00 ] ## +## --------------------------- ## + +Within the rest of this text, PAM text strings are delimited with a +pair of double quotes. Example, "this" = {'t';'h';'i';'s';0x00}. + +#$$$#{binary_prompt} binary prompt + +A binary prompt consists of a stream of octets arranged as follows: + +## ---------------------------------------- ## +## [ u32 | u8 | (length-5 octets) ] ## +## [ length | control | data ] ## +## ---------------------------------------- ## + +That is, a 32-bit unsigned integer in network byte order, a single +unsigned byte of control information and a sequence of octets of +length (length-5). The composition of the _data_ is context dependent +but is generally not a concern for either the server or the client. It +is very much the concern of modules and agents. + +For purposes of interoperability, we define the following control +characters as legal. + +## value symbol description ## +## ------------------------------------------------- ## +## 0x01 PAM_BPC_OK - continuation packet ## +## 0x02 PAM_BPC_SELECT - initialization packet ## +## 0x03 PAM_BPC_DONE - termination packet ## +## 0x04 PAM_BPC_FAIL - unable to execute ## + +The following control characters are only legal for exchanges between +an agent and a client (it is the responsibility of the client to +enforce this rule in the face of a rogue server): + +## 0x41 PAM_BPC_GETENV - obtain client env.var ## +## 0x42 PAM_BPC_PUTENV - set client env.var ## +## 0x43 PAM_BPC_TEXT - display message ## +## 0x44 PAM_BPC_ERROR - display error message ## +## 0x45 PAM_BPC_PROMPT - echo'd text prompt ## +## 0x46 PAM_BPC_PASS - non-echo'd text prompt ## +## 0x46 PAM_BPC_STATUS - ping all active clients## +## 0x47 PAM_BPC_ABORT - please abort session ## + +Note, length is always equal to the total length of the binary +prompt and represented by a network ordered unsigned 32 bit integer. + +#$$$$#{agent_ids} PAM_BPC_SELECT binary prompts + +Binary prompts of control type PAM_BPC_SELECT have a defined +data part. It is composed of three elements: + + {agent_id;'/';data} + +The agent_id is a sequence of characters satisfying the following +regexp: + + /^[a-z0-9\_]+(@[a-z0-9\_.]+)?$/ + +and has a specific form for each independent agent. + +o Agent_ids that do not contain an at-sign (@) are to be considered as + representing some authentication mode that is a "public + standard" see reference [#$R#{PAM_STD_AGENTIDS}]. Registered names + MUST NOT contain an at-sign (@). + +o Anyone can define additional agents by using names in the format + name@domainname, e.g. "ouragent@example.com". The part following + the at-sign MUST be a valid fully qualified internet domain name + [RFC-1034] controlled by the person or organization defining the + name. (Said another way, if you control the email address that + your agent has as an identifier, they you are entitled to use + this identifier.) It is up to each domain how it manages its local + namespace. + +The '/' character is a mandatory delimiter, indicating the end of the +agent_id. The trailing data is of a format specific to the agent with +the given agent_id. + + +#$$ Special cases + +In a previous section (#{players}) we identified the most general +selection of authentication participants. In the case of network +authentication, it is straightforward to ascribe identities to the +defined participants. However, there are also special (less general) +cases that we recognize here. + +The primary authentication step, when a user is directly introduced +into a computer system (log's on to a workstation) is a special case. +In this situation, the client and the server are generally one +application. Before authenticating such a user, the applicant is +formally unknown: PAM_RUSER is NULL. + +Some client-server implementations (telnet for example) provide +effective full tty connections. In these cases, the four simple text +string prompting cases (see below) can be handled as in the primary +login step. In other words, the server absorbs most of the overhead of +propagating authentication messages. In these cases, there needs to be +special client/server support for handling binary prompts. + +In some circumstances, a legacy network transfer protocol can carry +authentication information. In such cases, a desire to support legacy +clients (with no client-side support for PAM) will neccessitate the +'hardcoding' of an agent protocol into the server application. Whilst +against the spirit of PAM, this special casing can be managed by the +server's 'conversation function' (see below). The guiding principle +when implementing such support is for the application developer to +relegate the authentication process to the PAM module -- simply +performing a transcription of data from binary-prompt to legacy +network 'packet' and visa-versa for propagating replies back to the +driving PAM module. A common case of this is with network protocols +that define an initialization packet of "user+password". In such cases +one should attempt to support the "userpass" agent-id and its defined +protocol. + +#$ Defined interfaces for information flow + +Here, we discuss the information exchange interfaces between the +players in the authentication process. It should be understood that +the server side is responsible for driving the authentication of the +applicant. Notably, every request received by the client from the +server must be matched with a single response from the client to the +server. + +#$$#{applicant_client} Applicant <-> client + +Once the client is invoked, requests to the applicant entity are +initiated by the client application. General clients are able to make +the following requests directly to an applicant: + + echo text string + echo error text string + prompt with text string for echo'd text string input + prompt with text string for concealed text string input + +the nature of the interface provided by the client for the benefit of +the applicant entity is client specific and not defined by PAM. + +#$$#{client_agent} Client <-> agent + +In general, authentication schemes require more modes of exchange than +the four defined in the previous section (#{applicant_client}). This +provides a role for client-loadable agents. The client and agent +exchange binary-messages that can have one of the following forms: + + client -> agent + binary prompt agent expecting binary prompt reply to client + + agent -> client + binary prompt reply from agent to clients binary prompt + +Following the acceptance of a binary prompt by the agent, the agent +may attempt to exchange information with the client before returning +its binary prompt reply. Permitted exchanges are binary prompts of the +following types: + + agent -> client + set environment variable (A) + get environment variable (B) + echo text string (C) + echo error text string (D) + prompt for echo'd text string input (E) + prompt for concealed text string input (F) + +In response to these prompts, the client must legitimately respond +with a corresponding binary prompt reply. We list a complete set of +example exchanges, including each type of legitimate response (passes +and a single fail): + +## Type | Agent request | Client response ## +## --------------------------------------------------------------- ## +## (A) | {13;PAM_BPC_PUTENV;"FOO=BAR"} | {5;PAM_BPC_OK;} ## +## | {10;PAM_BPC_PUTENV;"FOO="} | {5;PAM_BPC_OK;} ## +## | {9;PAM_BPC_PUTENV;"FOO"} (*) | {5;PAM_BPC_OK;} ## +## | {9;PAM_BPC_PUTENV;"BAR"} (*) | {5;PAM_BPC_FAIL;} ## +## --------------------------------------------------------------- ## +## (B) | {10;PAM_BPC_GETENV;"TERM"} | {11;PAM_BPC_OK;"vt100"} ## +## | {9;PAM_BPC_GETENV;"FOO"} | {5;PAM_BPC_FAIL;} ## +## --------------------------------------------------------------- ## +## (C) | {12;PAM_BPC_TEXT;"hello!"} | {5;PAM_BPC_OK;} ## +## | {12;PAM_BPC_TEXT;"hello!"} | {5;PAM_BPC_FAIL;} ## +## --------------------------------------------------------------- ## +## (D) | {11;PAM_BPC_ERROR;"ouch!"} | {5;PAM_BPC_OK;} ## +## | {11;PAM_BPC_ERROR;"ouch!"} | {5;PAM_BPC_FAIL;} ## +## --------------------------------------------------------------- ## +## (E) | {13;PAM_BPC_PROMPT;"login: "} | {9;PAM_BPC_OK;"joe"} ## +## | {13;PAM_BPC_PROMPT;"login: "} | {6;PAM_BPC_OK;""} ## +## | {13;PAM_BPC_PROMPT;"login: "} | {5;PAM_BPC_FAIL;} ## +## --------------------------------------------------------------- ## +## (F) | {16;PAM_BPC_PASS;"password: "} | {9;PAM_BPC_OK;"XYZ"} ## +## | {16;PAM_BPC_PASS;"password: "} | {6;PAM_BPC_OK;""} ## +## | {16;PAM_BPC_PASS;"password: "} | {5;PAM_BPC_FAIL;} ## + +(*) Used to attempt the removal of a pre-existing environment +variable. + +#$$ Client <-> server + +Once the client has established a connection with the server (the +nature of the transport protocol is not specified by PAM), the server +is responsible for driving the authentication process. + +General servers can request the following from the client: + + (to be forwarded by the client to the applicant) + echo text string + echo error text string + prompt for echo'd text string response + prompt for concealed text string response + + (to be forwarded by the client to the appropriate agent) + binary prompt for a binary prompt response + +Client side agents are required to process binary prompts. The +agents' binary prompt responses are returned to the server. + +#$$ Server <-> module + +Modules drive the authentication process. The server provides a +conversation function with which it encapsulates module-generated +requests and exchanges them with the client. Every message sent by a +module should be acknowledged. + +General conversation functions can support the following five +conversation requests: + + echo text string + echo error string + prompt for echo'd text string response + prompt for concealed text string response + binary prompt for binary prompt response + +The server is responsible for redirecting these requests to the +client. + +#$ C API for application interfaces (client and server) + +#$$ Applicant <-> client + +No API is defined for this interface. The interface is considered to +be specific to the client application. Example applications include +terminal login, (X)windows login, machine file transfer applications. + +All that is important is that the client application is able to +present the applicant with textual output and to receive textual +input from the applicant. The forms of textual exchange are listed +in an earlier section (#{applicant_client}). Other methods of +data input/output are better suited to being handled via an +authentication agent. + +#$$ Client <-> agent + +The client makes use of a general API for communicating with +agents. The client is not required to communicate directly with +available agents, instead a layer of abstraction (in the form of a +library: libpamc) takes care of loading and maintaining communication +with all requested agents. This layer of abstraction will choose which +agents to interact with based on the content of binary prompts it +receives that have the control type PAM_BPC_SELECT. + +#$$$ Client <-> libpamc + +#$$$$ Compilation information + +The C-header file provided for client-agent abstraction is included +with the following source line: + + \#include + +The library providing the corresponding client-agent abstraction +functions is, libpamc. + + cc .... -lpamc + +#$$$$ Initializing libpamc + +The libpamc library is initialized with a call to the following +function: + + pamc_handle_t pamc_start(void); + +This function is responsible for configuring the library and +registering the location of available agents. The location of the +available agents on the system is implementation specific. + +pamc_start() function returns NULL on failure. Otherwise, the return +value is a pointer to an opaque data type which provides a handle to +the libpamc library. On systems where threading is available, the +libpamc libraray is thread safe provided a single (pamc_handler_t *) +is used by each thread. + +#$$$$ Client (Applicant) selection of agents + +For the purpose of applicant and client review of available agents, +the following function is provided. + + char **pamc_list_agents(pamc_handle_t pch); + +This returns a list of pointers to the agent_id's of the agents which +are available on the system. The list is terminated by a NULL pointer. +It is the clients responsibility to free this memory area by calling +free() on each agent id and the block of agent_id pointers in the +result. + +PAM represents a server-driven authentication model, so by default +any available agent may be invoked in the authentication process. + +#$$$$$ Client demands agent + +If the client requires that a specific authentication agent is +satisfied during the authentication process, then the client should +call the following function, immediately after obtaining a +pamc_handle_t from pamc_start(). + + int pamc_load(pamc_handle_t pch, const char *agent_id); + +agent_id is a PAM text string (see section #{agent_ids}) and is not +suffixed with a '/' delimiter. The return value for this function is: + + PAM_BPC_TRUE - agent located and loaded. + PAM_BPC_FALSE - agent is not available. + +Note, although the agent is loaded, no data is fed to it. The agent's +opportunity to inform the client that it does not trust the server is +when the agent is shutdown. + +#$$$$$ Client marks agent as unusable + +The applicant might prefer that a named agent is marked as not +available. To do this, the client would invoke the following function +immediately after obtaining a pamc_handle_t from pam_start(). + + int pamc_disable(pamc_handle_t pch, const char *agent_id); + +here agent_id is a PAM text string containing an agent_id (section +#{agent_ids}). + +The return value for this function is: + + PAM_BPC_TRUE - agent is disabled. This is the response + independent of whether the agent is locally + available. + + PAM_BPC_FALSE - agent cannot be disabled (this may be because + it has already been invoked). + +#$$$$ Allocating and manipulating binary prompts + +All conversation between an client and an agent takes place with +respect to binary prompts. A binary prompt (see section #{binary_prompt}), is +obtained, resized and deleted via the following C-macro: + + CREATION of a binary prompt with control X1 and data length Y1: + + pamc_bp_t prompt = NULL; + PAM_BP_RENEW(&prompt, X1, Y1); + + REPLACEMENT of a binary prompt with a control X2 and data length Y2: + + PAM_BP_RENEW(&prompt, X2, Y2); + + DELETION of a binary prompt (the referenced prompt is scrubbed): + + PAM_BP_RENEW(&prompt, 0, 0); + +Note, the PAM_BP_RENEW macro always overwrites any prompt that you +call it with, deleting and liberating the old contents in a secure +fashion. Also note that PAM_BP_RENEW, when returning a prompt of data +size Y1>0, will always append a '\0' byte to the end of the prompt (at +data offset Y1). It is thus, by definition, acceptable to treat the +data contents of a binary packet as a text string (see #{text_string}). + + FILLING a binary prompt from a memory pointer U1 from offset O1 of + length L1: + + PAM_BP_FILL(prompt, O1, L1, U1); + + the CONTROL type for the packet can be obtained as follows: + + control = PAM_PB_CONTROL(prompt); + + the LENGTH of a data within the prompt (_excluding_ its header + information) can be obtained as follows: + + length = PAM_BP_LENGTH(prompt); + + the total SIZE of the prompt (_including_ its header information) + can be obtained as follows: + + size = PAM_BP_SIZE(prompt); + + EXTRACTING data from a binary prompt from offset O2 of length L2 to + a memory pointer U2: + + PAM_BP_EXTRACT(prompt, O2, L2, U2); + + If you require direct access to the raw prompt DATA, you should use + the following macro: + + __u8 *raw_data = PAM_BP_DATA(prompt); + +#$$$$ Client<->agent conversations + +All exchanges of binary prompts with agents are handled with the +single function: + + int pamc_converse(pamc_handle_t *pch, pamc_bp_t *prompt_p); + +The return value for pamc_converse(...) is PAM_BPC_TRUE when there is +a response packet and PAM_BPC_FALSE when the client is unable to +handle the request represented by the original prompt. In this latter +case, *prompt_p is set to NULL. + +This function takes a binary prompt and returns a replacement binary +prompt that is either a request from an agent to be acted upon by the +client or the 'result' which should be forwarded to the server. In the +former case, the following macro will return 1 (PAM_BPC_TRUE) and in +all other cases, 0 (PAM_BPC_FALSE): + + PAM_BPC_FOR_CLIENT(/* pamc_bp_t */ prompt) + +Note, all non-NULL binary prompts returned by pamc_converse(...), are +terminated with a '\0', even when the full length of the prompt (as +returned by the agent) does not contain this delimiter. This is a +defined property of the PAM_BP_RENEW macro, and can be relied upon. + +Important security note: in certain implementations, agents are +implemented by executable binaries, which are transparently loaded and +managed by the PAM client library. To ensure there is never a leakage +of elevated privilege to an unprivileged agent, the client application +should go to some effort to lower its level of privilege. It remains +the responsibility of the applicant and the client to ensure that it +is not compromised by a rogue agent. + +#$$$$ Status of agents + + int pamc_status(pamc_handle_t *pch, pamc_bp_t *prompt_p); + +At any time, the client may ping all active agents for their status +(with a PAM_BPC_STATUS binary prompt). If any agent replies with +PAM_BPC_ABORT, the client is responsible for terminating the +connection to the server and then terminating all agents with a call +to pamc_end(). In such cases, the return value of pamc_status() is +PAM_BPC_FALSE. + +If the return status of pamc_status() is PAM_BPC_TRUE and *prompt_p is +non-NULL, then an agent is requesting access to a server module. + +XXX - how this information gets propagated to the server, and + ultimately to the server's module is yet to be determined. + +#$$$$ Termination of agents + +When closing the authentication session and severing the connection +between a client and a selection of agents, the following function is +used: + + int pamc_end(pamc_handle_t *pch); + +Following a call to pamc_end, the pamc_handle_t will be invalid. + +The return value for this function is one of the following: + + PAM_BPC_TRUE - all invoked agents are content with + authentication (the server is _not_ judged + _un_trustworthy by any agent) + + PAM_BPC_FALSE - one or more agents were unsatisfied at + being terminated. In general, the client + should terminate its connection to the + server and indicate to the applicant that + the server is untrusted. + +#$$$ libpamc <-> agents + +The agents are manipulated from within libpamc. Each agent is an +executable in its own right. This permits the agent to have access to +sensitive data not accessible directly from the client. The mode of +communication between libpamc and an agent is through a pair of +pipes. The agent reads binary prompts (section #{binary_prompt}) +through its standard input file descriptor and writes response (to the +server) binary prompts and instruction binary prompts (instructions +for the client) through its standard output file descriptor. + +#$$ Client <-> server + +This interface is concerned with the exchange of text and binary +prompts between the client application and the server application. No +API is provided for this as it is considered specific to the transport +protocol shared by the client and the server. + +#$$ Server <-> modules + +The server makes use of a general API for communicating with +modules. The client is not required to communicate directly with +available modules. By abstracting the authentication interface, it +becomes possible for the local administrator to make a run time +decision about the authentication method adopted by the server. + +#$$$ Functions and definitions available to servers and modules + +[This section will document the following functions + + pam_set_item() + pam_get_item() + pam_fail_delay(pam_handle_t *pamh, unsigned int micro_sec) + pam_get_env(pam_handle_t *pamh, const char *varname) + pam_strerror(pam_handle_t *pamh, int pam_errno) + +Event driven support (XXX work in progress) + + pam_register_event() - app or module associates an event poller/handler + pam_select_event() - query for any outstanding event and act on any +] + +#$$$ Server <-> libpam + +[This section will document the following pam_ calls: + + pam_start + pam_end + pam_authenticate (*) + pam_setcred + pam_acct_mgmt + pam_open_session + pam_close_session + pam_chauthtok (*) + +The asterisked functions may return PAM_INCOMPLETE. In such cases, the +application should be aware that the conversation function was called +and that it returned PAM_CONV_AGAIN to a module. The correct action +for the application to take in response to receiving PAM_INCOMPLETE, +is to acquire the replies so that the next time the conversation +function is called it will be able to provide the desired +responses. And then recall pam_authenticate (pam_chauthtok) with the +same arguments. Libpam will arrange that the module stack is resumed +from the module that returned before. This functionality is required +for programs whose user interface is maintained by an event loop. ] + +#$$$ libpam <-> modules + +[This section will document the following pam_ and pam_sm_ calls: + +functions provided by libpam + + pam_set_data + pam_get_data + +functions provided to libpam by each module + + groups: + AUTHENTICATION + pam_sm_authenticate + pam_sm_setcred + ACCOUNT + pam_sm_acct_mgmt + SESSION + pam_sm_open_session + pam_sm_close_session + AUTHENTICATION TOKEN MANAGEMENT + pam_sm_chauthtok +] + +#$$$ The conversation function + +The server application, as part of its initialization of libpam, +provides a conversation function for use by modules and libpam. The +purpose of the conversation function is to enable direct communication +to the applicant ultimately via the client and selected agents. + +[ this section will contain a definition for the conversation + function, the conversation structure (appdata etc), and legitimate + return codes for the application supplied function. + + PAM_SUCCESS - ok conversation completed + PAM_CONV_ERR - conversation failed + PAM_CONV_AGAIN - application needs control to complete conv + PAM_CONV_RECONSIDER - application believes module should check if + it still needs to converse for this info + ] + +#$ Security considerations + +This document is devoted to standardizing authentication +infrastructure: everything in this document has implications for +security. + +#$ Contact + +The email list for discussing issues related to this document is +. + +#$ References + +[#{OSF_RFC_PAM}] OSF RFC 86.0, "Unified Login with Pluggable Authentication + Modules (PAM)", October 1995 + +[#{PAM_STD_AGENTIDS}] Definitions for standard agents, "REGISTERED + AGENTS AND THEIR AGENT-ID'S", to be found here: + +## http://www.kernel.org/pub/linux/libs/pam/pre/doc/std-agent-ids.txt ## + +#$ Author's Address + +Andrew G. Morgan +Email: morgan@kernel.org + +## $Id$ ## diff --git a/doc/specs/meson.build b/doc/specs/meson.build new file mode 100644 index 0000000..4ddeef8 --- /dev/null +++ b/doc/specs/meson.build @@ -0,0 +1,42 @@ +parse_y = custom_target( + 'parse_y.[ch]', + input: 'parse_y.y', + output: ['parse_y.c', 'parse_y.h'], + command: yacc_cmd, +) + +parse_l = custom_target( + 'parse_l.c', + input: 'parse_l.l', + output: 'parse_l.c', + depends: parse_y, + command: [prog_flex, '-o', '@OUTPUT@', '@INPUT@'], +) + +padout = executable( + 'padout', + sources: [parse_l, parse_y], + include_directories: [libpam_inc], + c_args: [ + '-Wno-unused-function', + '-Wno-sign-compare', + ], + link_args: exe_link_args, +) + +custom_target( + 'draft-morgan-pam-current.txt', + input: 'draft-morgan-pam.raw', + output: 'draft-morgan-pam-current.txt', + command: [redir_exe, '@INPUT@', '@OUTPUT@', padout], + depends: padout, + install: true, + install_dir: docdir, + install_tag: 'doc', +) + +install_data( + 'rfc86.0.txt', + install_dir: docdir, + install_tag: 'doc', +) diff --git a/doc/specs/parse_l.l b/doc/specs/parse_l.l new file mode 100644 index 0000000..0edc08d --- /dev/null +++ b/doc/specs/parse_l.l @@ -0,0 +1,20 @@ +%{ +#include + +#include + +#include "parse_y.h" +%} + +%option noyywrap +%% + +\#[\$]+[a-zA-Z]*(\=[0-9]+)? return NEW_COUNTER; +\#\{[a-zA-Z][a-zA-Z0-9\_]*\} return LABEL; +\# return NO_INDENT; +\#\# return RIGHT; +\\\# return HASH; +[^\n] return CHAR; +[\n] return NEWLINE; + +%% diff --git a/doc/specs/parse_y.y b/doc/specs/parse_y.y new file mode 100644 index 0000000..9dbd102 --- /dev/null +++ b/doc/specs/parse_y.y @@ -0,0 +1,293 @@ + +%{ +#include + +#include +#include +#include + +#define MAXLINE 1000 +#define INDENT_STRING " " +#define PAPER_WIDTH 74 + + int indent=0; + int line=1; + char *last_label=NULL; + + extern int yylex(void); + extern char *yytext; + extern void yyerror(const char *x); + extern char *get_label(const char *label); + extern void set_label(const char *label, const char *target); + char *new_counter(const char *key); +%} + +%union { + int def; + char *string; +} + +%token NEW_COUNTER LABEL HASH CHAR NEWLINE NO_INDENT RIGHT +%type stuff text + +%start doc + +%% + +doc: +| doc NEWLINE { + printf("\n"); + ++line; +} +| doc stuff NEWLINE { + if (strlen($2) > (PAPER_WIDTH-(indent ? strlen(INDENT_STRING):0))) { + yyerror("line too long"); + } + printf("%s%s\n", indent ? INDENT_STRING:"", $2); + free($2); + indent = 1; + ++line; +} +| doc stuff RIGHT stuff NEWLINE { + char fixed[PAPER_WIDTH+1]; + int len; + + len = PAPER_WIDTH-(strlen($2)+strlen($4)); + + if (len >= 0) { + memset(fixed, ' ', len); + fixed[len] = '\0'; + } else { + yyerror("line too wide"); + fixed[0] = '\0'; + } + printf("%s%s%s\n", $2, fixed, $4); + free($2); + free($4); + indent = 1; + ++line; +} +| doc stuff RIGHT stuff RIGHT stuff NEWLINE { + char fixed[PAPER_WIDTH+1]; + int len, l; + + len = PAPER_WIDTH-(strlen($2)+strlen($4)); + + if (len < 0) { + len = 0; + yyerror("line too wide"); + } + + l = len/2; + memset(fixed, ' ', l); + fixed[l] = '\0'; + printf("%s%s%s", $2, fixed, $4); + free($2); + free($4); + + l = (len+1)/2; + memset(fixed, ' ', l); + fixed[l] = '\0'; + printf("%s%s\n", fixed, $6); + free($6); + + indent = 1; + ++line; +} +| doc stuff RIGHT stuff RIGHT stuff NEWLINE { + char fixed[PAPER_WIDTH+1]; + int len, l; + + len = PAPER_WIDTH-(strlen($2)+strlen($4)); + + if (len < 0) { + len = 0; + yyerror("line too wide"); + } + + l = len/2; + memset(fixed, ' ', l); + fixed[l] = '\0'; + printf("%s%s%s", $2, fixed, $4); + free($2); + free($4); + + l = (len+1)/2; + memset(fixed, ' ', l); + fixed[l] = '\0'; + printf("%s%s\n", fixed, $6); + free($6); + + indent = 1; + ++line; +} +; + +stuff: { + $$ = strdup(""); +} +| stuff text { + $$ = malloc(strlen($1)+strlen($2)+1); + sprintf($$,"%s%s", $1, $2); + free($1); + free($2); +} +; + +text: CHAR { + $$ = strdup(yytext); +} +| text CHAR { + $$ = malloc(strlen($1)+2); + sprintf($$,"%s%s", $1, yytext); + free($1); +} +| NO_INDENT { + $$ = strdup(""); + indent = 0; +} +| HASH { + $$ = strdup("#"); +} +| LABEL { + if (($$ = get_label(yytext)) == NULL) { + set_label(yytext, last_label); + $$ = strdup(""); + } +} +| NEW_COUNTER { + $$ = new_counter(yytext); +} +; + +%% + +typedef struct node_s { + struct node_s *left, *right; + const char *key; + char *value; +} *node_t; + +node_t label_root = NULL; +node_t counter_root = NULL; + +static const char *find_key(node_t root, const char *key) +{ + while (root) { + int cmp = strcmp(key, root->key); + + if (cmp > 0) { + root = root->right; + } else if (cmp) { + root = root->left; + } else { + return root->value; + } + } + return NULL; +} + +static node_t set_key(node_t root, const char *key, const char *value) +{ + if (root) { + int cmp = strcmp(key, root->key); + if (cmp > 0) { + root->right = set_key(root->right, key, value); + } else if (cmp) { + root->left = set_key(root->left, key, value); + } else { + free(root->value); + root->value = strdup(value); + } + } else { + root = malloc(sizeof(struct node_s)); + root->right = root->left = NULL; + root->key = strdup(key); + root->value = strdup(value); + } + return root; +} + +void yyerror(const char *x) +{ + fprintf(stderr, "line %d: %s\n", line, x); +} + +char *get_label(const char *label) +{ + const char *found = find_key(label_root, label); + + if (found) { + return strdup(found); + } + return NULL; +} + +void set_label(const char *label, const char *target) +{ + if (target == NULL) { + yyerror("no hanging value for label"); + target = ""; /* avoid trigraph warning */ + } + label_root = set_key(label_root, label, target); +} + +char *new_counter(const char *key) +{ + int i=0, j, ndollars = 0; + const char *old; + char *new; + + if (key[i++] != '#') { + yyerror("bad index"); + return strdup(""); /* avoid trigraph warning */ + } + + while (key[i] == '$') { + ++ndollars; + ++i; + } + + key += i; + old = find_key(counter_root, key); + new = malloc(20*ndollars); + + if (old) { + for (j=0; ndollars > 1 && old[j]; ) { + if (old[j++] == '.' && --ndollars <= 0) { + break; + } + } + if (j) { + strncpy(new, old, j); + } + if (old[j]) { + i = atoi(old+j); + } else { + new[j++] = '.'; + i = 0; + } + } else { + j=0; + while (--ndollars > 0) { + new[j++] = '0'; + new[j++] = '.'; + } + i = 0; + } + new[j] = '\0'; + sprintf(new+j, "%d", ++i); + + counter_root = set_key(counter_root, key, new); + + free(last_label); + last_label = strdup(new); + + return new; +} + +int +main(void) +{ + return yyparse(); +} diff --git a/doc/specs/rfc86.0.txt b/doc/specs/rfc86.0.txt new file mode 100644 index 0000000..3451eaf --- /dev/null +++ b/doc/specs/rfc86.0.txt @@ -0,0 +1,1845 @@ + + + + + + + + + Open Software Foundation V. Samar (SunSoft) + Request For Comments: 86.0 R. Schemers (SunSoft) + October 1995 + + + + UNIFIED LOGIN WITH + PLUGGABLE AUTHENTICATION MODULES (PAM) + + + 1. INTRODUCTION + + Since low-level authentication mechanisms constantly evolve, it is + important to shield the high-level consumers of these mechanisms + (system-entry services and users) from such low-level changes. With + the Pluggable Authentication Module (PAM) framework, we can provide + pluggability for a variety of system-entry services -- not just + system authentication _per se_, but also for account, session and + password management. PAM's ability to _stack_ authentication modules + can be used to integrate `login' with different authentication + mechanisms such as RSA, DCE, and Kerberos, and thus unify login + mechanisms. The PAM framework can also provide easy integration of + smart cards into the system. + + Modular design and pluggability have become important for users who + want ease of use. In the PC hardware arena, no one wants to set the + interrupt vector numbers or resolve the addressing conflict between + various devices. In the software arena, people also want to be able + to replace components easily for easy customization, maintenance, and + upgrades. + + Authentication software deserves special attention because + authentication forms a very critical component of any secure computer + system. The authentication infrastructure and its components may + have to be modified or replaced either because some deficiencies have + been found in the current algorithms, or because sites want to + enforce a different security policy than what was provided by the + system vendor. The replacement and modification should be done in + such a way that the user is not affected by these changes. + + The solution has to address not just how the applications use the new + authentication mechanisms in a generic fashion, but also how the user + will be authenticated to these mechanisms in a generic way. The + former is addressed by GSS-API [Linn 93], while this RFC addresses + the later; these two efforts are complementary to each other. + + Since most system-entry services (for example, `login', `dtlogin', + `rlogin', `ftp', `rsh') may want to be independent of the specific + authentication mechanisms used by the machine, it is important that + there be a framework for _plugging_ in various mechanisms. This + requires that the system applications use a standard API to interact + + + + Samar, Schemers Page 1 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + with the authentication services. If these system-entry services + remain independent of the actual mechanism used on that machine, the + system administrator can install suitable authentication modules + without requiring changes to these applications. + + For any security system to be successful, it has to be easy to use. + In the case of authentication, the single most important ease-of-use + characteristic is that the user should not be required to learn about + various ways of authentication and remember multiple passwords. + Ideally, there should be one all-encompassing authentication system + where there is only one password, but for heterogeneous sites, + multiple authentication mechanisms have to co-exist. The problem of + integrating multiple authentication mechanisms such as Kerberos + [Steiner 88], RSA [Rivest 78], and Diffie-Hellman [Diffie 76, Taylor + 88], is also referred to as _integrated login_, or _unified login_ + problem. Even if the user has to use multiple authentication + mechanisms, the user should not be forced to type multiple passwords. + Furthermore, the user should be able to use the new network identity + without taking any further actions. The key here is in modular + integration of the network authentication technologies with `login' + and other system-entry services. + + In this RFC we discuss the architecture and design of pluggable + authentication modules. This design gives the capability to use + field-replaceable authentication modules along with unified login + capability. It thus provides for both _pluggability_ and _ease-of- + use_. + + The RFC is organized as follows. We first motivate the need for a + generic way to authenticate the user by various system-entry services + within the operating system. We describe the goals and constraints + of the design. This leads to the architecture, description of the + interfaces, and _stacking_ of modules to get unified login + functionality. We then describe our experience with the design, and + end with a description of future work. + + + 2. OVERVIEW OF IDENTIFICATION AND AUTHENTICATION MECHANISMS + + An identification and authentication ("I&A") mechanism is used to + establish a user's identity the system (i.e., to a local machine's + operating system) and to other principals on the network. On a + typical UNIX system, there are various ports of entry into the + system, such as `login', `dtlogin', `rlogin', `ftp', `rsh', `su', and + `telnet'. In all cases, the user has to be identified and + authenticated before granting appropriate access rights to the user. + The user identification and authentication for all these entry points + needs to be coordinated to ensure a secure system. + + In most of the current UNIX systems, the login mechanism is based + upon verification of the password using the modified DES algorithm. + + + + Samar, Schemers Page 2 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + The security of the implementation assumes that the password cannot + be guessed, and that the password does not go over the wire in the + clear. These assumptions, however, are not universally valid. + Various programs are now available freely on the Internet that can + run dictionary attack against the encrypted password. Further, some + of the network services (for example, `rlogin', `ftp', `telnet') send + the password over in clear, and there are "sniffer" programs freely + available to steal these passwords. The classical assumptions may be + acceptable on a trusted network, but in an open environment there is + a need to use more restrictive and stronger authentication + mechanisms. Examples of such mechanisms include Kerberos, RSA, + Diffie-Hellman, one-time password [Skey 94], and challenge-response + based smart card authentication systems. Since this list will + continue to evolve, it is important that the system-entry services do + not have hard-coded dependencies on any of these authentication + mechanisms. + + + 3. DESIGN GOALS + + The goals of the PAM framework are as follows: + + (a) The system administrator should be able to choose the default + authentication mechanism for the machine. This can range from + a simple password-based mechanism to a biometric or a smart + card based system. + + (b) It should be possible to configure the user authentication + mechanism on a per application basis. For example, a site may + require S/Key password authentication for `telnet' access, + while allowing machine `login' sessions with just UNIX password + authentication. + + (c) The framework should support the display requirements of the + applications. For example, for a graphical login session such + as `dtlogin', the user name and the password may have to be + entered in a new window. For networking system-entry + applications such as `ftp' and `telnet', the user name and + password has to be transmitted over the network to the client + machine. + + (d) It should be possible to configure multiple authentication + protocols for each of those applications. For example, one may + want the users to get authenticated by both Kerberos and RSA + authentication systems. + + (e) The system administrator should be able to _stack_ multiple + user authentication mechanisms such that the user is + authenticated with all authentication protocols without + retyping the password. + + + + + Samar, Schemers Page 3 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + (f) The architecture should allow for multiple passwords if + necessary to achieve higher security for users with specific + security requirements. + + (g) The system-entry services should not be required to change when + the underlying mechanism changes. This can be very useful for + third-party developers because they often do not have the + source code for these services. + + (h) The architecture should provide for a _pluggable_ model for + system authentication, as well as for other related tasks such + as password, account, and session management. + + (i) For backward-compatibility reasons, the PAM API should support + the authentication requirements of the current system-entry + services. + + There are certain issues that the PAM framework does not specifically + address: + + (a) We focus only on providing a generic scheme through which users + use passwords to establish their identities to the machine. + Once the identity is established, how the identity is + communicated to other interested parties is outside the scope + of this design. There are efforts underway at IETF [Linn 93] + to develop a Generic Security Services Application Interface + (GSSAPI) that can be used by applications for secure and + authenticated communication without knowing the underlying + mechanism. + + (b) The _single-signon_ problem of securely transferring the + identity of the caller to a remote site is not addressed. For + example, the problem of delegating credentials from the + `rlogin' client to the other machine without typing the + password is not addressed by our work. We also do not address + the problem of sending the passwords over the network in the + clear. + + (c) We do not address the source of information obtained from the + "`getXbyY()'" family of calls (e.g., `getpwnam()'). Different + operating systems address this problem differently. For + example, Solaris uses the name service switch (NSS) to + determine the source of information for the "`getXbyY()'" + calls. It is expected that data which is stored in multiple + sources (such as passwd entries in NIS+ and the DCE registry) + is kept in sync using the appropriate commands (such as + `passwd_export'). + + + + + + + + Samar, Schemers Page 4 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + 4. OVERVIEW OF THE PAM FRAMEWORK + + We propose that the goals listed above can be met through a framework + in which authentication modules can be _plugged_ independently of the + application. We call this the _Pluggable Authentication Modules_ + (PAM) framework. + + The core components of the PAM framework are the authentication + library API (the front end) and the authentication mechanism-specific + modules (the back end), connected through the Service Provider + Interface (SPI). Applications write to the PAM API, while the + authentication-system providers write to the PAM SPI and supply the + back end modules that are independent of the application. + + ftp telnet login (Applications) + | | | + | | | + +--------+--------+ + | + +-----+-----+ + | PAM API | <-- pam.conf file + +-----+-----+ + | + +--------+--------+ + UNIX Kerberos Smart Cards (Mechanisms) + + Figure 1: The Basic PAM Architecture + + Figure 1 illustrates the relationship between the application, the + PAM library, and the authentication modules. Three applications + (`login', `telnet' and `ftp') are shown which use the PAM + authentication interfaces. When an application makes a call to the + PAM API, it loads the appropriate authentication module as determined + by the configuration file, `pam.conf'. The request is forwarded to + the underlying authentication module (for example, UNIX password, + Kerberos, smart cards) to perform the specified operation. The PAM + layer then returns the response from the authentication module to the + application. + + PAM unifies system authentication and access control for the system, + and allows plugging of associated authentication modules through well + defined interfaces. The plugging can be defined through various + means, one of which uses a configuration file, such as the one in + Table 1. For each of the system applications, the file specifies the + authentication module that should be loaded. In the example below, + `login' uses the UNIX password module, while `ftp' and `telnet' use + the S/Key module. + + + + + + + + Samar, Schemers Page 5 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + Table 1: A Simplified View of a Sample PAM Configuration File. + + service module_path + ------- ----------- + login pam_unix.so + ftp pam_skey.so + telnet pam_skey.so + + Authentication configuration is only one aspect of this interface. + Other critical components include account management, session + management, and password management. For example, the `login' + program may want to verify not only the password but also whether the + account has aged or expired. Generic interfaces also need to be + provided so that the password can be changed according to the + requirements of the module. Furthermore, the application may want to + log information about the current session as determined by the + module. + + Not all applications or services may need all of the above + components, and not each authentication module may need to provide + support for all of the interfaces. For example, while `login' may + need access to all four components, `su' may need access to just the + authentication component. Some applications may use some specific + authentication and password management modules but share the account + and session management modules with others. + + This reasoning leads to a partitioning of the entire set of + interfaces into four areas of functionality: (1) authentication, (2) + account, (3) session, and (4) password. The concept of PAM was + extended to these functional areas by implementing each of them as a + separate pluggable module. + + Breaking the functionality into four modules helps the module + providers because they can use the system-provided libraries for the + modules that they are not changing. For example, if a supplier wants + to provide a better version of Kerberos, they can just provide that + new authentication and password module, and reuse the existing ones + for account and session. + + 4.1. Module Description + + More details on specific API's are described in Appendix A. A brief + description of four modules follows: + + (a) Authentication management: This set includes the + `pam_authenticate()' function to authenticate the user, and the + `pam_setcred()' interface to set, refresh or destroy the user + credentials. + + (b) Account management: This set includes the `pam_acct_mgmt()' + function to check whether the authenticated user should be + + + + Samar, Schemers Page 6 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + given access to the account. This function can implement + account expiration and access hour restrictions. + + (c) Session management: This set includes the `pam_open_session()' + and `pam_close_session()' functions for session management and + accounting. For example, the system may want to store the + total time for the session. + + (d) Password management: This set includes a function, + `pam_chauthtok()', to change the password. + + + 5. FRAMEWORK INTERFACES + + The PAM framework further provides a set of administrative interfaces + to support the above modules and to provide for application-module + communication. There is no corresponding service provider interface + (SPI) for such functions. + + 5.1. Administrative Interfaces + + Each set of PAM transactions starts with `pam_start()' and ends with + the `pam_end()' function. The interfaces `pam_get_item()' and + `pam_set_item()' are used to read and write the state information + associated with the PAM transaction. + + If there is any error with any of the PAM interfaces, the error + message can be printed with `pam_strerror()'. + + 5.2. Application-Module Communication + + During application initialization, certain data such as the user name + is saved in the PAM framework layer through `pam_start()' so that it + can be used by the underlying modules. The application can also pass + opaque data to the module which the modules will pass back while + communicating with the user. + + 5.3. User-Module Communication + + The `pam_start()' function also passes conversation function that has + to be used by the underlying modules to read and write module + specific authentication information. For example, these functions + can be used to prompt the user for the password in a way determined + by the application. PAM can thus be used by graphical, non- + graphical, or networked applications. + + + + + + + + + + Samar, Schemers Page 7 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + 5.4. Inter-Module Communication + + Though the modules are independent, they can share certain common + information about the authentication session such as user name, + service name, password, and conversation function through the + `pam_get_item()' and `pam_set_item()' interfaces. These API's can + also be used by the application to change the state information after + having called `pam_start()' once. + + 5.5. Module State Information + + The PAM service modules may want to keep certain module-specific + state information about the session. The interfaces `pam_get_data()' + and `pam_set_data()' can be used by the service modules to access and + update module-specific information as needed from the PAM handle. + The modules can also attach a cleanup function with the data. The + cleanup function is executed when `pam_end()' is called to indicate + the end of the current authentication activity. + + Since the PAM modules are loaded upon demand, there is no direct + module initialization support in the PAM framework. If there are + certain initialization tasks that the PAM service modules have to do, + they should be done upon the first invocation. However, if there are + certain clean-up tasks to be done when the authentication session + ends, the modules should use `pam_set_data()' to specify the clean-up + functions, which would be called when `pam_end()' is called by the + application. + + + 6. MODULE CONFIGURATION MANAGEMENT + + Table 2 shows an example of a configuration file `pam.conf' with + support for authentication, session, account, and password management + modules. `login' has three entries: one each for authentication + processing, session management and account management. Each entry + specifies the module name that should be loaded for the given module + type. In this example, the `ftp' service uses the authentication and + session modules. Note that all services here share the same session + management module, while having different authentication modules. + + + + + + + + + + + + + + + + Samar, Schemers Page 8 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + Table 2: Configuration File (pam.conf) with Different Modules + and Control Flow + + service module_type control_flag module_path options + ------- ----------- ------------ ----------- ------- + login auth required pam_unix_auth.so nowarn + login session required pam_unix_session.so + login account required pam_unix_account.so + ftp auth required pam_skey_auth.so debug + ftp session required pam_unix_session.so + telnet session required pam_unix_session.so + login password required pam_unix_passwd.so + passwd password required pam_unix_passwd.so + OTHER auth required pam_unix_auth.so + OTHER session required pam_unix_session.so + OTHER account required pam_unix_account.so + + The first field, _service_, denotes the service (for example, + `login', `passwd', `rlogin'). The name `OTHER' indicates the module + used by all other applications that have not been specified in this + file. This name can also be used if all services have the same + requirements. In the example, since all the services use the same + session module, we could have replaced those lines with a single + `OTHER' line. + + The second field, _module_type_, indicates the type of the PAM + functional module. It can be one of `auth', `account', `session', or + `password' modules. + + The third field, _control_flag_ determines the behavior of stacking + multiple modules by specifying whether any particular module is + _required_, _sufficient_, or _optional_. The next section describes + stacking in more detail. + + The fourth field, _module_path_, specifies the location of the + module. The PAM framework loads this module upon demand to invoke + the required function. + + The fifth field, _options_, is used by the PAM framework layer to + pass module specific options to the modules. It is up to the module + to parse and interpret the options. This field can be used by the + modules to turn on debugging or to pass any module specific + parameters such as a timeout value. It is also used to support + unified login as described below. The options field can be used by + the system administrator to fine-tune the PAM modules. + + If any of the fields are invalid, or if a module is not found, that + line is ignored and the error is logged as a critical error via + `syslog(3)'. If no entries are found for the given module type, then + the PAM framework returns an error to the application. + + + + + Samar, Schemers Page 9 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + 7. INTEGRATING MULTIPLE AUTHENTICATION SERVICES WITH STACKING + + In the world of heterogeneous systems, the system administrator often + has to deal with the problem of integrating multiple authentication + mechanisms. The user is often required to know about the + authentication command of the new authentication module (for example, + `kinit', `dce_login') after logging into the system. This is not + user-friendly because it forces people to remember to type the new + command and enter the new password. This functionality should be + invisible instead of burdening the user with it. + + There are two problems to be addressed here: + + (a) Supporting multiple authentication mechanisms. + + (b) Providing unified login in the presence of multiple mechanisms. + + In the previous section, we described how one could replace the + default authentication module with any other module of choice. Now + we demonstrate how the same model can be extended to provide support + for multiple modules. + + 7.1. Design for Stacked Modules + + One possibility was to provide hard-coded rules in `login' or other + applications requiring authentication services [Adamson 95]. But + this becomes very specific to the particular combination of + authentication protocols, and also requires the source code of the + application. Digital's Security Integration Architecture [SIA 95] + addresses this problem by specifying the same list of authentication + modules for all applications. Since requirements for various + applications can vary, it is essential that the configuration be on a + per-application basis. + + To support multiple authentication mechanisms, the PAM framework was + extended to support _stacking_. When any API is called, the back + ends for the stacked modules are invoked in the order listed, and the + result returned to the caller. In Figure 2, the authentication + service of `login' is stacked and the user is authenticated by UNIX, + Kerberos, and RSA authentication mechanisms. Note that in this + example, there is no stacking for session or account management + modules. + + + + + + + + + + + + + Samar, Schemers Page 10 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + login + | + +--------+--------+ + | | | + session auth account + | | | + +--+--+ +--+--+ +--+--+ + | PAM | | PAM | | PAM | + +--+--+ +--+--+ +--+--+ + | | | + UNIX UNIX UNIX + session auth account + | + Kerberos + auth + | + RSA + auth + + Figure 2: Stacking With the PAM Architecture + + Stacking is specified through additional entries in the configuration + file shown earlier. As shown in Table 2, for each application (such + as `login') the configuration file can specify multiple mechanisms + that have to be invoked in the specified order. When mechanisms + fail, the _control_flag_ decides which error should be returned to + the application. Since the user should not know which authentication + module failed when a bad password was typed, the PAM framework + continues to call other authentication modules on the stack even on + failure. The semantics of the control flag are as follows: + + (a) `required': With this flag, the module failure results in the + PAM framework returning the error to the caller _after_ + executing all other modules on the stack. For the function to + be able to return success to the application all `required' + modules have to report success. This flag is normally set when + authentication by this module is a _must_. + + (b) `optional': With this flag, the PAM framework ignores the + module failure and continues with the processing of the next + module in sequence. This flag is used when the user is allowed + to login even if that particular module has failed. + + (c) `sufficient': With this flag, if the module succeeds the PAM + framework returns success to the application immediately + without trying any other modules. For failure cases, the + _sufficient_ modules are treated as `optional'. + + Table 3 shows a sample configuration file that stacks the `login' + command. Here the user is authenticated by UNIX, Kerberos, and RSA + authentication services. The `required' key word for _control_flag_ + + + + Samar, Schemers Page 11 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + enforces that the user is allowed to login only if he/she is + authenticated by _both_ UNIX and Kerberos services. RSA + authentication is optional by virtue of the `optional' key word in + the _control_flag_ field. The user can still log in even if RSA + authentication fails. + + Table 3: PAM Configuration File with Support for Stacking + + service module_type control_flag module_path options + ------- ----------- ------------ ----------- ------- + login auth required pam_unix.so debug + login auth required pam_kerb.so use_mapped_pass + login auth optional pam_rsa.so use_first_pass + + Table 4 illustrates the use of the sufficient flag for the `rlogin' + service. The Berkeley `rlogin' protocol specifies that if the remote + host is trusted (as specified in the `/etc/hosts.equiv' file or in + the `.rhosts' file in the home directory of the user), then the + `rlogin' daemon should not require the user to type the password. If + this is not the case, then the user is required to type the password. + Instead of hard coding this policy in the `rlogin' daemon, this can + be expressed with the `pam.conf' file in Table 4. The PAM module + `pam_rhosts_auth.so.1' implements the `.rhosts' policy described + above. If a site administrator wants to enable remote login with + only passwords, then the first line should be deleted. + + Table 4: PAM Configuration File for the rlogin service + + service module_type control_flag module_path options + ------- ----------- ------------ ----------- ------- + rlogin auth sufficient pam_rhosts_auth.so + rlogin auth required pam_unix.so + + 7.2. Password-Mapping + + Multiple authentication mechanisms on a machine can lead to multiple + passwords that users have to remember. One attractive solution from + the ease-of-use viewpoint is to use the same password for all + mechanisms. This, however, can also weaken the security because if + that password were to be compromised in any of the multiple + mechanisms, all mechanisms would be compromised at the same time. + Furthermore, different authentication mechanisms may have their own + distinctive password requirements in regards to its length, allowed + characters, time interval between updates, aging, locking, and so + forth. These requirements make it problematic to use the same + password for multiple authentication mechanisms. + + The solution we propose, while not precluding use of the same + password for every mechanism, allows for a different password for + each mechanism through what we call _password-mapping_. This + basically means using the user's _primary_ password to encrypt the + + + + Samar, Schemers Page 12 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + user's other (_secondary_) passwords, and storing these encrypted + passwords in a place where they are available to the user. Once the + primary password is verified, the authentication modules would obtain + the other passwords for their own mechanisms by decrypting the + mechanism-specific encrypted password with the primary password, and + passing it to the authentication service. The security of this + design for password-mapping assumes that the primary password is the + user's strongest password, in terms of its unguessability (length, + type and mix of characters used, etc.). + + If there is any error in password-mapping, or if the mapping does not + exist, the user will be prompted for the password by each + authentication module. + + To support password-mapping, the PAM framework saves the primary + password and provides it to stacked authentication modules. The + password is cleared out before the `pam_authenticate' function + returns. + + How the password is encrypted depends completely on the module + implementation. The encrypted secondary password (also called a + "mapped password") can be stored in a trusted or untrusted place, + such as a smart card, a local file, or a directory service. If the + encrypted passwords are stored in an untrusted publicly accessible + place, this does provide an intruder with opportunities for potential + dictionary attack. + + Though password-mapping is voluntary, it is recommended that all + module providers add support for the following four mapping options: + + (a) `use_first_pass': Use the same password used by the first + mechanism that asked for a password. The module should not ask + for the password if the user cannot be authenticated by the + first password. This option is normally used when the system + administrator wants to enforce the same password across + multiple modules. + + (b) `try_first_pass': This is the same as `use_first_pass', except + that if the primary password is not valid, it should prompt the + user for the password. + + (c) `use_mapped_pass': Use the password-mapping scheme to get the + actual password for this module. One possible implementation + is to get the mapped-password using the XFN API [XFN 94], and + decrypt it with the primary password to get the module-specific + password. The module should not ask for the password if the + user cannot be authenticated by the first password. The XFN + API allows user-defined attributes (such as _mapped-password_) + to be stored in the _user-context_. Using the XFN API is + particularly attractive because support for the XFN may be + found on many systems in the future. + + + + Samar, Schemers Page 13 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + (d) `try_mapped_pass': This is the same as `use_mapped_pass', + except that if the primary password is not valid, it should + prompt the user for the password. + + When passwords get updated, the PAM framework stores both the old as + well as the new password to be able to inform other dependent + authentication modules about the change. Other modules can use this + information to update the encrypted password without forcing the user + to type the sequence of passwords again. The PAM framework clears + out the passwords before returning to the application. + + Table 3 illustrates how the same password can be used by `login' for + authenticating to the standard UNIX login, Kerberos and RSA services. + Once the user has been authenticated to the primary authentication + service (UNIX `login' in this example) with the primary password, the + option `use_mapped_pass' indicates to the Kerberos module that it + should use the primary password to decrypt the stored Kerberos + password and then use the Kerberos password to get the ticket for the + ticket-granting-service. After that succeeds, the option + `use_first_pass' indicates to the RSA module that instead of + prompting the user for a password, it should use the primary password + typed earlier for authenticating the user. Note that in this + scenario, the user has to enter the password just once. + + Note that if a one-time password scheme (e.g., S/Key) is used, + password mapping cannot apply. + + 7.3. Implications of Stacking on the PAM Design + + Because of the stacking capability of PAM, we have designed the PAM + API's to not return any data to the application, except status. If + this were not the case, it would be difficult for the PAM framework + to decide which module should return data to the application. When + there is any error, the application does not know which of the + modules failed. This behavior enables (even requires) the + application to be completely independent from the modules. + + Another design decision we have made is that PAM gives only the user + name to all the underlying PAM modules, hence it is the + responsibility of the PAM modules to convert the name to their own + internal format. For example, the Kerberos module may have to + convert the UNIX user name to a Kerberos principal name. + + Stacking also forces the modules to be designed such that they can + occur anywhere in the stack without any side-effects. + + Since modules such as the authentication and the password module are + very closely related, it is important they be configured in the same + order and with compatible options. + + + + + + Samar, Schemers Page 14 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + 8. INTEGRATION WITH SMART CARDS + + Many networking authentication protocols require possession of a long + key to establish the user identity. For ease-of-use reasons, that + long key is normally encrypted with the user's password so that the + user is not required to memorize it. However, weak passwords can be + compromised through a dictionary attack and thus undermine the + stronger network authentication mechanism. Furthermore, the + encrypted data is normally stored in a centrally accessible service + whose availability depends upon the reliability of the associated + service. Solutions have been proposed to use a pass-phrase or one- + time-password, but those are much longer than the regular eight + character passwords traditionally used with UNIX `login'. This makes + the solution user-unfriendly because it requires longer strings to be + remembered and typed. + + For most authentication protocol implementations, the trust boundary + is the local machine. This assumption may not be valid in cases + where the user is mobile and has to use publicly available networked + computers. In such cases, it is required that the clear text of the + key or the password never be made available to the machine. + + Smart cards solve the above problems by reducing password exposure by + supporting a _two factor_ authentication mechanism: the first with + the possession of the card, and the second with the knowledge of the + PIN associated with the card. Not only can the smart cards be a + secure repository of multiple passwords, they can also provide the + encryption and authentication functions such that the long (private) + key is never exposed outside the card. + + The PAM framework allows for integrating smart cards to the system by + providing a smart card specific module for authentication. + Furthermore, the unified login problem is simplified because the + multiple passwords for various authentication mechanisms can be + stored on the smart card itself. This can be enabled by adding a + suitable key-word such as `use_smart_card' in the _options_ field. + + + 9. SECURITY ISSUES + + It is important to understand the impact of PAM on the security of + any system so that the site-administrator can make an informed + decision. + + (a) Sharing of passwords with multiple authentication mechanisms. + + If there are multiple authentication modules, one possibility + is to use the same password for all of them. If the password + for any of the multiple authentication system is compromised, + the user's password in all systems would be compromised. If + this is a concern, then multiple passwords might be considered + + + + Samar, Schemers Page 15 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + at the cost of ease-of-use. + + (b) Password-mapping. + + This technique of encrypting all other passwords with the + primary password assumes that it is lot more difficult to crack + the primary password and that reasonable steps have been taken + to ensure limited availability of the encrypted primary + password. If this is not done, an intruder could target the + primary password as the first point of dictionary attack. If + one of the other modules provide stronger security than the + password based security, the site would be negating the strong + security by using password-mapping. If this is a concern, then + multiple passwords might be considered at the cost of ease-of- + use. If smart cards are used, they obviate the need for + password-mapping completely. + + (c) Security of the configuration file. + + Since the policy file dictates how the user is authenticated, + this file should be protected from unauthorized modifications. + + (d) Stacking various PAM modules. + + The system administrator should fully understand the + implications of stacking various modules that will be installed + on the system and their respective orders and interactions. + The composition of various authentication modules should be + carefully examined. The trusted computing base of the machine + now includes the PAM modules. + + + 10. EXPERIENCE WITH PAM + + The PAM framework was first added in Solaris 2.3 release as a private + internal interface. PAM is currently being used by several system + entry applications such as `login', `passwd', `su', `dtlogin', + `rlogind', `rshd', `telnetd', `ftpd', `in.rexecd', `uucpd', `init', + `sac', and `ttymon'. We have found that PAM provides an excellent + framework to encapsulate the authentication-related tasks for the + entire system. The Solaris 2.3 PAM API's were hence enhanced and + simplified to support stacking. + + PAM modules have been developed for UNIX, DCE, Kerberos, S/Key, + remote user authentication, and dialpass authentication. Other PAM + modules are under development, and integration with smart cards is + being planned. + + Some third parties have used the PAM interface to extend the security + mechanisms offered by the Solaris environment. + + + + + Samar, Schemers Page 16 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + The PAM API has been accepted by Common Desktop Environment (CDE) + vendors as the API to be used for integrating the graphical interface + for login, `dtlogin' with multiple authentication mechanisms. + + + 11. FUTURE WORK + + Amongst the various components of PAM, the password component needs + to be carefully examined to see whether the stacking semantics are + particularly applicable, and how PAM should deal with partial + failures when changing passwords. + + The _control_flag_ of the configuration file can be extended to + include other semantics. For example, if the error is "name service + not available", one may want to retry. It is also possible to offer + semantics of "return success if any of the modules return success". + + In an earlier section, we had mentioned integration of smart cards + with PAM. Though we feel that integration should be straight forward + from the PAM architecture point of view, there may be some issues + with implementation because the interfaces to the smart cards have + not yet been standardized. + + One possible extension to PAM is to allow the passing of module- + specific data between applications and PAM modules. For example, the + `login' program likes to build its new environment from a select list + of variables, yet the DCE module needs the `KRB5CCNAME' variable to + be exported to the child process. For now we have modified the + `login' program to explicitly export the `KRB5CCNAME' variable. + + Administrative tools are needed to help system administrators modify + `pam.conf', and perform sanity checks on it (i.e., a `pam_check' + utility). + + + 12. CONCLUSION + + The PAM framework and the module interfaces provide pluggability for + user authentication, as well as for account, session and password + management. The PAM architecture can be used by `login' and by all + other system-entry services, and thus ensure that all entry points + for the system have been secured. This architecture enables + replacement and modification of authentication modules in the field + to secure the system against the newly found weaknesses without + changing any of the system services. + + The PAM framework can be used to integrate `login' and `dtlogin' with + different authentication mechanisms such as RSA and Kerberos. + Multiple authentication systems can be accessed with the same + password. The PAM framework also provides easy integration of smart + cards into the system. + + + + Samar, Schemers Page 17 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + PAM provides complementary functionality to GSS-API, in that it + provides mechanisms through which the user gets authenticated to any + new system-level authentication service on the machine. GSS-API then + uses the credentials for authenticated and secure communications with + other application-level service entities on the network. + + + 13. ACKNOWLEDGEMENTS + + PAM development has spanned several release cycles at SunSoft. + Shau-Ping Lo, Chuck Hickey, and Alex Choy did the first design and + implementation. Bill Shannon and Don Stephenson helped with the PAM + architecture. Rocky Wu prototyped stacking of multiple modules. + Paul Fronberg, Charlie Lai, and Roland Schemers made very significant + enhancements to the PAM interfaces and took the project to completion + within a very short time. Kathy Slattery wrote the PAM + documentation. John Perry integrated PAM within the CDE framework. + + + APPENDIX A. PAM API'S + + This appendix gives an informal description of the various interfaces + of PAM. Since the goal here is just for the reader to get a working + knowledge about the PAM interfaces, not all flags and options have + been fully defined and explained. The API's described here are + subject to change. + + The PAM Service Provider Interface is very similar to the PAM API, + except for one extra parameter to pass module-specific options to the + underlying modules. + + A.1. Framework Layer API's + + int + pam_start( + char *service_name, + char *user, + struct pam_conv *pam_conversation, + pam_handle_t **pamh + ); + + `pam_start()' is called to initiate an authentication transaction. + `pam_start()' takes as arguments the name of the service, the name of + the user to be authenticated, the address of the conversation + structure. `pamh' is later used as a handle for subsequent calls to + the PAM library. + + The PAM modules do not communicate directly with the user; instead + they rely on the application to perform all such interaction. The + application needs to provide the conversation functions, `conv()', + and associated application data pointers through a `pam_conv' + + + + Samar, Schemers Page 18 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + structure when it initiates an authentication transaction. The + module uses the `conv()' function to prompt the user for data, + display error messages, or text information. + + int + pam_end( + pam_handle_t *pamh, + int pam_status + ); + + `pam_end()' is called to terminate the PAM transaction as specified + by `pamh', and to free any storage area allocated by the PAM modules + with `pam_set_item()'. + + int + pam_set_item( + pam_handle_t *pamh, + int item_type, + void *item + ); + + int + pam_get_item( + pam_handle_t *pamh, + int item_type, + void **item); + + `pam_get_item()' and `pam_set_item()' allow the parameters specified + in the initial call to `pam_start()' to be read and updated. This is + useful when a particular parameter is not available when + `pam_start()' is called or must be modified after the initial call to + `pam_start()'. `pam_set_item()' is passed a pointer to the object, + `item', and its type, `item_type'. `pam_get_item()' is passed the + address of the pointer, `item', which is assigned the address of the + requested object. + + The `item_type' is one of the following: + + Table 5: Possible Values for Item_type + + Item Name Description + --------- ----------- + PAM_SERVICE The service name + PAM_USER The user name + PAM_TTY The tty name + PAM_RHOST The remote host name + PAM_CONV The pam_conv structure + PAM_AUTHTOK The authentication token (password) + PAM_OLDAUTHTOK The old authentication token + PAM_RUSER The remote user name + + + + + Samar, Schemers Page 19 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + Note that the values of `PAM_AUTHTOK' and `PAM_OLDAUTHTOK' are only + available to PAM modules and not to the applications. They are + explicitly cleared out by the framework before returning to the + application. + + char * + pam_strerror( + int errnum + ); + + `pam_strerror()' maps the error number to a PAM error message string, + and returns a pointer to that string. + + int + pam_set_data( + pam_handle_t *pamh, + char *module_data_name, + char *data, + (*cleanup)(pam_handle_t *pamh, char *data, + int error_status) + ); + + The `pam_set_data()' function stores module specific data within the + PAM handle. The `module_data_name' uniquely specifies the name to + which some data and cleanup callback function can be attached. The + cleanup function is called when `pam_end()' is invoked. + + int + pam_get_data( + pam_handle_t *pamh, + char *module_data_name, + void **datap + ); + + The `pam_get_data()' function obtains module-specific data from the + PAM handle stored previously by the `pam_get_data()' function. The + `module_data_name' uniquely specifies the name for which data has to + be obtained. This function is normally used to retrieve module + specific state information. + + A.2. Authentication API's + + int + pam_authenticate( + pam_handle_t *pamh, + int flags + ); + + The `pam_authenticate()' function is called to verify the identity of + the current user. The user is usually required to enter a password + or similar authentication token, depending upon the authentication + + + + Samar, Schemers Page 20 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + module configured with the system. The user in question is specified + by a prior call to `pam_start()', and is referenced by the + authentication handle, `pamh'. + + int + pam_setcred( + pam_handle_t *pamh, + int flags + ); + + The `pam_setcred()' function is called to set the credentials of the + current process associated with the authentication handle, `pamh'. + The actions that can be denoted through `flags' include credential + initialization, refresh, reinitialization and deletion. + + A.3. Account Management API + + int + pam_acct_mgmt( + pam_handle_t *pamh, + int flags + ); + + The function `pam_acct_mgmt()' is called to determine whether the + current user's account and password are valid. This typically + includes checking for password and account expiration, valid login + times, etc. The user in question is specified by a prior call to + `pam_start()', and is referenced by the authentication handle, + `pamh'. + + A.4. Session Management API's + + int + pam_open_session( + pam_handle_t *pamh, + int flags + ); + + `pam_open_session()' is called to inform the session modules that a + new session has been initialized. All programs which use PAM should + invoke `pam_open_session()' when beginning a new session. + + int + pam_close_session( + pam_handle_t *pamh, + int flags + ); + + Upon termination of this session, the `pam_close_session()' function + should be invoked to inform the underlying modules that the session + has terminated. + + + + Samar, Schemers Page 21 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + A.5. Password Management API's + + int + pam_chauthtok( + pam_handle_t *pamh, + int flags + ); + + `pam_chauthtok()' is called to change the authentication token + associated with the user referenced by the authentication handle + `pamh'. After the call, the authentication token of the user will be + changed in accordance with the authentication module configured on + the system. + + + APPENDIX B. SAMPLE PAM APPLICATION + + This appendix shows a sample `login' application which uses the PAM + API's. It is not meant to be a fully functional login program, as + some functionality has been left out in order to emphasize the use of + PAM API's. + + #include + + static int login_conv(int num_msg, struct pam_message **msg, + struct pam_response **response, void *appdata_ptr); + + static struct pam_conv pam_conv = {login_conv, NULL}; + + static pam_handle_t *pamh; /* Authentication handle */ + + void + main(int argc, char *argv[], char **renvp) + { + + /* + * Call pam_start to initiate a PAM authentication operation + */ + + if ((pam_start("login", user_name, &pam_conv, &pamh)) + != PAM_SUCCESS) + login_exit(1); + + pam_set_item(pamh, PAM_TTY, ttyn); + pam_set_item(pamh, PAM_RHOST, remote_host); + + while (!authenticated && retry < MAX_RETRIES) { + status = pam_authenticate(pamh, 0); + authenticated = (status == PAM_SUCCESS); + } + + + + + Samar, Schemers Page 22 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + if (status != PAM_SUCCESS) { + fprintf(stderr,"error: %s\n", pam_strerror(status)); + login_exit(1); + } + + /* now check if the authenticated user is allowed to login. */ + + if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) { + if (status == PAM_AUTHTOK_EXPIRED) { + status = pam_chauthtok(pamh, 0); + if (status != PAM_SUCCESS) + login_exit(1); + } else { + login_exit(1); + } + } + + /* + * call pam_open_session to open the authenticated session + * pam_close_session gets called by the process that + * cleans up the utmp entry (i.e., init) + */ + if (status = pam_open_session(pamh, 0) != PAM_SUCCESS) { + login_exit(status); + } + + /* set up the process credentials */ + setgid(pwd->pw_gid); + + /* + * Initialize the supplementary group access list. + * This should be done before pam_setcred because + * the PAM modules might add groups during the pam_setcred call + */ + initgroups(user_name, pwd->pw_gid); + + status = pam_setcred(pamh, PAM_ESTABLISH_CRED); + if (status != PAM_SUCCESS) { + login_exit(status); + } + + /* set the real (and effective) UID */ + setuid(pwd->pw_uid); + + pam_end(pamh, PAM_SUCCESS); /* Done using PAM */ + + /* + * Add DCE/Kerberos cred name, if any. + * XXX - The module specific stuff should be removed from login + * program eventually. This is better placed in DCE module and + * will be once PAM has routines for "exporting" environment + + + + Samar, Schemers Page 23 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + * variables. + */ + krb5p = getenv("KRB5CCNAME"); + if (krb5p != NULL) { + ENVSTRNCAT(krb5ccname, krb5p); + envinit[basicenv++] = krb5ccname; + } + environ = envinit; /* Switch to the new environment. */ + exec_the_shell(); + + /* All done */ + } + + /* + * login_exit - Call exit() and terminate. + * This function is here for PAM so cleanup can + * be done before the process exits. + */ + static void + login_exit(int exit_code) + { + if (pamh) + pam_end(pamh, PAM_ABORT); + exit(exit_code); + /*NOTREACHED*/ + } + + /* + * login_conv(): + * This is the conv (conversation) function called from + * a PAM authentication module to print error messages + * or garner information from the user. + */ + + static int + login_conv(int num_msg, struct pam_message **msg, + struct pam_response **response, void *appdata_ptr) + { + + while (num_msg--) { + switch (m->msg_style) { + + case PAM_PROMPT_ECHO_OFF: + r->resp = strdup(getpass(m->msg)); + break; + + case PAM_PROMPT_ECHO_ON: + (void) fputs(m->msg, stdout); + r->resp = malloc(PAM_MAX_RESP_SIZE); + fgets(r->resp, PAM_MAX_RESP_SIZE, stdin); + /* add code here to remove \n from fputs */ + + + + Samar, Schemers Page 24 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + break; + + case PAM_ERROR_MSG: + (void) fputs(m->msg, stderr); + break; + + case PAM_TEXT_INFO: + (void) fputs(m->msg, stdout); + break; + + default: + /* add code here to log error message, etc */ + break; + } + } + return (PAM_SUCCESS); + } + + + APPENDIX C. DCE MODULE + + This appendix describes a sample implementation of a DCE PAM module. + In order to simplify the description, we do not address the issues + raised by password-mapping or stacking. The intent is to show which + DCE calls are being made by the DCE module. + + The `pam_sm_*()' functions implement the PAM SPI functions which are + called from the PAM API functions. + + C.1. DCE Authentication Management + + The algorithm for authenticating with DCE (not including error + checking, prompting for passwords, etc.) is as follows: + + pam_sm_authenticate() + { + sec_login_setup_identity(...); + pam_set_data(...); + sec_login_valid_and_cert_ident(...); + } + + pam_sm_setcred() + { + pam_get_data(...); + sec_login_set_context(...); + } + + The `pam_sm_authenticate()' function for DCE uses the + `pam_set_data()' and `pam_get_data()' functions to keep state (like + the `sec_login_handle_t' context) between calls. The following + cleanup function is also registered and gets called when `pam_end()' + + + + Samar, Schemers Page 25 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + is called: + + dce_cleanup() + { + if (/* PAM_SUCCESS and + sec_login_valid_and_cert_ident success */) { + sec_login_release_context(...); + } else { + sec_login_purge_context(...); + } + } + + If everything was successful we release the login context, but leave + the credentials file intact. If the status passed to `pam_end()' was + not `PAM_SUCCESS' (i.e., a required module failed) we purge the login + context which also removes the credentials file. + + C.2. DCE Account Management + + The algorithm for DCE account management is as follows: + + pam_sm_acct_mgmt() + { + pam_get_data(...); + sec_login_inquire_net_info(...); + /* check for expired password and account */ + sec_login_free_net_info(...); + } + + The `sec_login_inquire_net_info()' function is called to obtain + information about when the user's account and/or password are going + to expire. A warning message is displayed (using the conversation + function) if the user's account or password is going to expire in the + near future, or has expired. These warning messages can be disabled + using the `nowarn' option in the `pam.conf' file. + + C.3. DCE Session Management + + The DCE session management functions are currently empty. They could + be modified to optionally remove the DCE credentials file upon + logout, etc. + + C.4. DCE Password Management + + The algorithm for DCE password management is as follows: + + + + + + + + + + Samar, Schemers Page 26 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + pam_sm_chauthtok + { + sec_rgy_site_open(...); + sec_rgy_acct_lookup(...); + sec_rgy_acct_passwd(...); + sec_rgy_site_close(...); + } + + The `sec_rgy_acct_passwd()' function is called to change the user's + password in the DCE registry. + + + REFERENCES + + [Adamson 95] W. A. Adamson, J. Rees, and P. Honeyman, "Joining + Security Realms: A Single Login for Netware and + Kerberos", CITI Technical Report 95-1, Center for + Information Technology Integration, University of + Michigan, Ann Arbor, MI, February 1995. + + [Diffie 76] W. Diffie and M. E. Hellman, "New Directions in + Cryptography", IEEE Transactions on Information + Theory, November 1976. + + [Linn 93] J. Linn, "Generic Security Service Application + Programming Interface", Internet RFC 1508, 1509, 1993. + + [Rivest 78] R. L. Rivest, A. Shamir, and L. Adleman., "A Method + for Obtaining Digital Signatures and Pubic-key + Cryptosystems", Communications of the ACM, 21(2), + 1978. + + [SIA 95] "Digital UNIX Security", Digital Equipment + Corporation, Order Number AA-Q0R2C-TE, July 1995. + + [Skey 94] N. M. Haller, "The S/Key One-Time Password System", + ISOC Symposium on Network and Distributed Security, + 1994. + + [Steiner 88] J.G. Steiner, B. C. Neuman, and J. I. Schiller, + "Kerberos, An Authentication Service for Open Network + Systems", in Proceedings of the Winter USENIX + Conference, Dallas, Jan 1988. + + [Taylor 88] B. Taylor and D. Goldberg, "Secure Networking in the + Sun Environment", Sun Microsystems Technical Paper, + 1988. + + [XFN 94] "Federated Naming: the XFN Specifications", X/Open + Preliminary Specification, X/Open Document #P403, + ISBN:1-85912-045-8, X/Open Co. Ltd., July 1994. + + + + Samar, Schemers Page 27 + + + + + + + + OSF-RFC 86.0 PAM October 1995 + + + + AUTHOR'S ADDRESS + + Vipin Samar Internet email: vipin@eng.sun.com + SunSoft, Inc. Telephone: +1-415-336-1002 + 2550 Garcia Avenue + Mountain View, CA 94043 + USA + + Roland J. Schemers III Internet email: schemers@eng.sun.com + SunSoft, Inc. Telephone: +1-415-336-1035 + 2550 Garcia Avenue + Mountain View, CA 94043 + USA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Samar, Schemers Page 28 diff --git a/doc/specs/std-agent-id.raw b/doc/specs/std-agent-id.raw new file mode 100644 index 0000000..d5fbdd5 --- /dev/null +++ b/doc/specs/std-agent-id.raw @@ -0,0 +1,95 @@ +PAM working group ## A.G. Morgan + +## $Id$ ## + +## Pluggable Authentication Modules ## + +## REGISTERED AGENTS AND THEIR AGENT-ID'S ## + +#$ Purpose of this document + +#$$#{definition} Definition of an agent-id + +The most complete version of a "PAM agent-id" is contained in this +reference [#$R#{PAM_RFC2}]. A copy of a recent definition is +reproduced here for convenience. The reader is recommended to consult +reference [#{PAM_RFC2}] for definitions of other terms that are +used in this document. + +## -------------- ## + +The agent_id is a sequence of characters satisfying the following +regexp: + + /^[a-z0-9\_]+(@[a-z0-9\_.]+)?$/ + +and has a specific form for each independent agent. + +o Agent_ids that do not contain an at-sign (@) are to be considered as + representing some authentication mode that is a "public + standard". Registered names MUST NOT contain an at-sign (@). + +o Anyone can define additional agents by using names in the format + name@domainname, e.g. "ouragent@example.com". The part following + the at-sign MUST be a valid fully qualified internet domain name + [RFC-1034] controlled by the person or organization defining the + name. (Said another way, if you control the email address that + your agent has as an identifier, they you are entitled to use + this identifier.) It is up to each domain how it manages its local + namespace. + +## -------------- ## + +#$ Registered agent-id's + +The structure of this section is a single subsection for each +registered agent-id. This section includes a full definition of binary +prompts accepted by the agent and example responses of said +agent. Using the defining section alone, it should be possible for a +third party to create a conforming agent and modules that can +interoperate with other implementations of these objects. + +*$ "userpass" - the user+password agent + +Many legacy authentication systems are hardcoded to support one and +only one authentication method. Namely, + + username: joe + password: + +Indeed, this authentication method is often embedded into parts of the +transport protocol. The "user+password" agent with PAM agent-id: + + "userpass" + +Is intended to support this legacy authentication scheme. The protocol +for binary prompt exchange with this 'standard agent' is as follows: + +Case 1: module does not know the username, but expects the agent to + obtain this information and also the user's password: + + module: {LENGTH;PAM_BP_SELECT;userpass;'/'} + agent: {} + +Case 2: module has suggested username, but would like agent to confirm + it and gather password: + + module: {} + agent: {} + +Case 3: module knows username and will not permit the agent to change it: + + module: {} + agent: {} + +#$ References + +[#{PAM_RFC2}] Internet draft, "Pluggable Authentication Modules + (PAM)", available here: + +# http://linux.kernel.org/pub/linux/libs/pam/pre/doc/current-draft.txt # + +#$ Author's Address + +Andrew G. Morgan +Email: morgan@kernel.org diff --git a/examples/README b/examples/README new file mode 100644 index 0000000..fee734d --- /dev/null +++ b/examples/README @@ -0,0 +1,12 @@ + +(now we are getting networked apps, be careful to try and test on a +securely isolated system!) + +These programs grant no privileges, but they give an idea of how well +the modules are working. + +blank is new as of Linux-PAM-0.21. If you are writing/modifying an +application it might be a place to start... + +xsh is new as of Linux-PAM-0.31, it is identical to blank, but invokes +/bin/sh if the user is authenticated. diff --git a/examples/blank.c b/examples/blank.c new file mode 100644 index 0000000..8210013 --- /dev/null +++ b/examples/blank.c @@ -0,0 +1,158 @@ +/* + * $Id$ + */ + +/* Andrew Morgan (morgan@parc.power.net) -- a self contained `blank' + * application + * + * I am not very proud of this code. It makes use of a possibly ill- + * defined pamh pointer to call pam_strerror() with. The reason that + * I was sloppy with this is historical (pam_strerror, prior to 0.59, + * did not require a pamh argument) and if this program is used as a + * model for anything, I should wish that you will take this error into + * account. + */ + +#include +#include + +#include +#include + +/* ------ some local (static) functions ------- */ + +static void bail_out(pam_handle_t *pamh, int really, int code, const char *fn) +{ + fprintf(stderr,"==> called %s()\n got: `%s'\n", fn, + pam_strerror(pamh, code)); + if (really && code) + exit (1); +} + +/* ------ some static data objects ------- */ + +static struct pam_conv conv = { + misc_conv, + NULL +}; + +/* ------- the application itself -------- */ + +int main(int argc, char **argv) +{ + pam_handle_t *pamh=NULL; + char *username=NULL; + int retcode; + + /* did the user call with a username as an argument ? */ + + if (argc > 2) { + fprintf(stderr,"usage: %s [username]\n",argv[0]); + } else if (argc == 2) { + username = argv[1]; + } + + /* initialize the Linux-PAM library */ + retcode = pam_start("blank", username, &conv, &pamh); + bail_out(pamh,1,retcode,"pam_start"); + + /* test the environment stuff */ + { +#define MAXENV 15 + const char *greek[MAXENV] = { + "a=alpha", "b=beta", "c=gamma", "d=delta", "e=epsilon", + "f=phi", "g=psi", "h=eta", "i=iota", "j=mu", "k=nu", + "l=zeta", "h=", "d", "k=xi" + }; + char **env; + int i; + + for (i=0; i + slight modifications by AGM. + + You need to add the following (or equivalent) to the /etc/pam.conf file. + # check authorization + check auth required pam_unix_auth.so + check account required pam_unix_acct.so +*/ + +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + +int main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + int retval; + const char *user="nobody"; + + if(argc == 2) { + user = argv[1]; + } + + if(argc > 2) { + fprintf(stderr, "Usage: check_user [username]\n"); + exit(1); + } + + retval = pam_start("check", user, &conv, &pamh); + + if (retval == PAM_SUCCESS) + retval = pam_authenticate(pamh, 0); /* is user really user? */ + + if (retval == PAM_SUCCESS) + retval = pam_acct_mgmt(pamh, 0); /* permitted access? */ + + /* This is where we have been authorized or not. */ + + if (retval == PAM_SUCCESS) { + fprintf(stdout, "Authenticated\n"); + } else { + fprintf(stdout, "Not Authenticated\n"); + } + + if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */ + pamh = NULL; + fprintf(stderr, "check_user: failed to release authenticator\n"); + exit(1); + } + + return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */ +} diff --git a/examples/meson.build b/examples/meson.build new file mode 100644 index 0000000..4f5e85e --- /dev/null +++ b/examples/meson.build @@ -0,0 +1,14 @@ +foreach prog: ['xsh', + 'vpass', + 'blank', + 'check_user', + 'tty_conv' + ] + executable( + prog, + sources: prog + '.c', + link_args: exe_link_args, + include_directories: [libpamc_inc], + dependencies: [libpam_dep, libpam_misc_dep], + ) +endforeach diff --git a/examples/tty_conv.c b/examples/tty_conv.c new file mode 100644 index 0000000..59bbb3b --- /dev/null +++ b/examples/tty_conv.c @@ -0,0 +1,178 @@ +/* PlanC (hubenchang0515@outlook.com) -- an example application + * that implements a custom conversation */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/*************************************** + * @brief echo off/on + * @param[in] fd file descriptor + * @param[in] off 1 - echo off,0 - echo on + ***************************************/ +static void echoOff(int fd, int off) +{ + struct termios tty; + if (ioctl(fd, TCGETA, &tty) < 0) + { + fprintf(stderr, "TCGETA failed: %s\n", strerror(errno)); + return; + } + + if (off) + { + tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + if (ioctl(fd, TCSETAF, &tty) < 0) + { + fprintf(stderr, "TCSETAF failed: %s\n", strerror(errno)); + } + } + else + { + tty.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL); + if (ioctl(fd, TCSETAW, &tty) < 0) + { + fprintf(stderr, "TCSETAW failed: %s\n", strerror(errno)); + } + } +} + +/*************************************** + * @brief echo off stdin + ***************************************/ +static void echoOffStdin(void) +{ + echoOff(fileno(stdin), 1); +} + +/*************************************** + * @brief echo on stdin + ***************************************/ +static void echoOnStdin(void) +{ + echoOff(fileno(stdin), 0); +} + +/*************************************** + * @brief read a line input + * @return the input string + ***************************************/ +static char *readline(void) +{ + char input[PAM_MAX_RESP_SIZE]; + int i; + + flockfile(stdin); + for (i = 0; i < PAM_MAX_RESP_SIZE - 1; i++) + { + int ch = getchar_unlocked(); + if (ch == '\n' || ch == '\r' ||ch == EOF) + break; + input[i] = ch; + } + funlockfile(stdin); + input[i] = '\0'; + + return (strdup(input)); +} + +/************************************************** + * @brief callback of PAM conversation + * @param[in] num_msg the count of message + * @param[in] msg PAM message + * @param[out] resp our response + * @param[in] appdata_ptr custom data passed by struct pam_conv.appdata_ptr + * @return state + **************************************************/ +static int conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) +{ + (void)(appdata_ptr); + int i; + + /* check the count of message */ + if (num_msg <= 0 || num_msg >= PAM_MAX_MSG_SIZE) + { + fprintf(stderr, "invalid num_msg(%d)\n", num_msg); + return PAM_CONV_ERR; + } + + /* alloc memory for response */ + if ((resp[0] = malloc(num_msg * sizeof(struct pam_response))) == NULL) + { + fprintf(stderr, "bad alloc\n"); + return PAM_BUF_ERR; + } + + /* response for message */ + for (i = 0; i < num_msg; i++) + { + const struct pam_message *m = *msg + i; + struct pam_response *r = *resp + i; + r->resp_retcode = 0; /* currently un-used, zero expected */ + switch (m->msg_style) + { + case PAM_PROMPT_ECHO_OFF: /* get the input with echo off, like the password */ + printf("%s", m->msg); + echoOffStdin(); + r->resp = readline(); + echoOnStdin(); + printf("\n"); + break; + + case PAM_PROMPT_ECHO_ON: /* get the input with echo on, like the username */ + printf("%s", m->msg); + r->resp = readline(); + break; + + case PAM_TEXT_INFO: /* normal info */ + printf("%s\n", m->msg); + break; + + case PAM_ERROR_MSG: /* error info */ + fprintf(stderr, "%s\n", m->msg); + break; + + default: + fprintf(stderr, "unexpected msg_style: %d\n", m->msg_style); + break; + } + } + return PAM_SUCCESS; +} + +int main(void) +{ + struct pam_conv pam_conv = {conversation, NULL}; + pam_handle_t *pamh; + + /* echo on while exist, like Ctrl+C on input password */ + atexit(echoOnStdin); + + if (PAM_SUCCESS != pam_start("login", NULL, &pam_conv, &pamh)) + { + fprintf(stderr, "pam_start failed\n"); + return EXIT_FAILURE; + } + + if (PAM_SUCCESS != pam_authenticate(pamh, 0)) + { + fprintf(stderr, "pam_authenticate failed\n"); + pam_end(pamh, 0); + return EXIT_FAILURE; + } + + if (PAM_SUCCESS != pam_acct_mgmt(pamh, 0)) + { + fprintf(stderr, "pam_acct_mgmt failed\n"); + pam_end(pamh, 0); + return EXIT_FAILURE; + } + + pam_end(pamh, 0); + return EXIT_SUCCESS; +} diff --git a/examples/vpass.c b/examples/vpass.c new file mode 100644 index 0000000..f73b361 --- /dev/null +++ b/examples/vpass.c @@ -0,0 +1,49 @@ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +static int +test_conv (int num_msg UNUSED, const struct pam_message **msgm UNUSED, + struct pam_response **response UNUSED, void *appdata_ptr UNUSED) +{ + return 0; +} + +static struct pam_conv conv = { + test_conv, + NULL +}; + +int main(void) +{ + char *user; + pam_handle_t *pamh; + struct passwd *pw; + uid_t uid; + int res; + + uid = geteuid(); + pw = getpwuid(uid); + if (pw) { + user = pw->pw_name; + } else { + fprintf(stderr, "Invalid userid: %lu\n", (unsigned long) uid); + exit(1); + } + + pam_start("vpass", user, &conv, &pamh); + pam_set_item(pamh, PAM_TTY, "/dev/tty"); + if ((res = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { + fprintf(stderr, "Oops: %s\n", pam_strerror(pamh, res)); + exit(1); + } + + pam_end(pamh, res); + exit(0); +} diff --git a/examples/xsh.c b/examples/xsh.c new file mode 100644 index 0000000..5b34fc1 --- /dev/null +++ b/examples/xsh.c @@ -0,0 +1,173 @@ +/* Andrew Morgan (morgan@kernel.org) -- an example application + * that invokes a shell, based on blank.c */ + +#include "config.h" + +#include +#include + +#include +#include + +#include +#include +#include + +/* ------ some local (static) functions ------- */ + +static void bail_out(pam_handle_t *pamh,int really, int code, const char *fn) +{ + fprintf(stderr,"==> called %s()\n got: `%s'\n", fn, + pam_strerror(pamh,code)); + if (really && code) + exit (1); +} + +/* ------ some static data objects ------- */ + +static struct pam_conv conv = { + misc_conv, + NULL +}; + +/* ------- the application itself -------- */ + +int main(int argc, char **argv) +{ + pam_handle_t *pamh=NULL; + const void *username=NULL; + const char *service="xsh"; + int retcode; + + /* did the user call with a username as an argument ? + * did they also */ + + if (argc > 3) { + fprintf(stderr,"usage: %s [username [service-name]]\n",argv[0]); + } + if ((argc >= 2) && (argv[1][0] != '-')) { + username = argv[1]; + } + if (argc == 3) { + service = argv[2]; + } + + /* initialize the Linux-PAM library */ + retcode = pam_start(service, username, &conv, &pamh); + bail_out(pamh,1,retcode,"pam_start"); + + /* fill in the RUSER and RHOST etc. fields */ + { + char buffer[100]; + struct passwd *pw; + const char *tty; + + pw = getpwuid(getuid()); + if (pw != NULL) { + retcode = pam_set_item(pamh, PAM_RUSER, pw->pw_name); + bail_out(pamh,1,retcode,"pam_set_item(PAM_RUSER)"); + } + + retcode = gethostname(buffer, sizeof(buffer)-1); + if (retcode) { + perror("failed to look up hostname"); + retcode = pam_end(pamh, PAM_ABORT); + bail_out(pamh,1,retcode,"pam_end"); + } + retcode = pam_set_item(pamh, PAM_RHOST, buffer); + bail_out(pamh,1,retcode,"pam_set_item(PAM_RHOST)"); + + tty = ttyname(fileno(stdin)); + if (tty) { + retcode = pam_set_item(pamh, PAM_TTY, tty); + bail_out(pamh,1,retcode,"pam_set_item(PAM_TTY)"); + } + } + + /* to avoid using goto we abuse a loop here */ + for (;;) { + /* authenticate the user --- `0' here, could have been PAM_SILENT + * | PAM_DISALLOW_NULL_AUTHTOK */ + + retcode = pam_authenticate(pamh, 0); + bail_out(pamh,0,retcode,"pam_authenticate"); + + /* has the user proved themself valid? */ + if (retcode != PAM_SUCCESS) { + fprintf(stderr,"%s: invalid request\n",argv[0]); + break; + } + + /* the user is valid, but should they have access at this + time? */ + + retcode = pam_acct_mgmt(pamh, 0); /* `0' could be as above */ + bail_out(pamh,0,retcode,"pam_acct_mgmt"); + + if (retcode == PAM_NEW_AUTHTOK_REQD) { + fprintf(stderr,"Application must request new password...\n"); + retcode = pam_chauthtok(pamh,PAM_CHANGE_EXPIRED_AUTHTOK); + bail_out(pamh,0,retcode,"pam_chauthtok"); + } + + if (retcode != PAM_SUCCESS) { + fprintf(stderr,"%s: invalid request\n",argv[0]); + break; + } + + /* `0' could be as above */ + retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); + bail_out(pamh,0,retcode,"pam_setcred"); + + if (retcode != PAM_SUCCESS) { + fprintf(stderr,"%s: problem setting user credentials\n" + ,argv[0]); + break; + } + + /* open a session for the user --- `0' could be PAM_SILENT */ + retcode = pam_open_session(pamh,0); + bail_out(pamh,0,retcode,"pam_open_session"); + if (retcode != PAM_SUCCESS) { + fprintf(stderr,"%s: problem opening a session\n",argv[0]); + break; + } + + pam_get_item(pamh, PAM_USER, &username); + fprintf(stderr, + "The user [%s] has been authenticated and `logged in'\n", + (const char *)username); + + /* this is always a really bad thing for security! */ + retcode = system("/bin/sh"); + + /* close a session for the user --- `0' could be PAM_SILENT + * it is possible that this pam_close_call is in another program.. + */ + + retcode = pam_close_session(pamh,0); + bail_out(pamh,0,retcode,"pam_close_session"); + if (retcode != PAM_SUCCESS) { + fprintf(stderr,"%s: problem closing a session\n",argv[0]); + break; + } + + /* `0' could be as above */ + retcode = pam_setcred(pamh, PAM_DELETE_CRED); + bail_out(pamh,0,retcode,"pam_setcred"); + if (retcode != PAM_SUCCESS) { + fprintf(stderr,"%s: problem deleting user credentials\n" + ,argv[0]); + break; + } + + break; /* don't go on for ever! */ + } + + /* close the Linux-PAM library */ + retcode = pam_end(pamh, PAM_SUCCESS); + pamh = NULL; + bail_out(pamh,1,retcode,"pam_end"); + + return (0); +} diff --git a/libpam/include/meson.build b/libpam/include/meson.build new file mode 100644 index 0000000..1bf46f3 --- /dev/null +++ b/libpam/include/meson.build @@ -0,0 +1,3 @@ +configure_file(output: 'config.h', configuration: cdata) + +subdir('security') diff --git a/libpam/include/pam_cc_compat.h b/libpam/include/pam_cc_compat.h new file mode 100644 index 0000000..0a6e32d --- /dev/null +++ b/libpam/include/pam_cc_compat.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2020 Dmitry V. Levin + */ + +#ifndef PAM_CC_COMPAT_H +#define PAM_CC_COMPAT_H + +#include "config.h" +#include + +#if defined __clang__ && defined __clang_major__ && defined __clang_minor__ +# define PAM_CLANG_PREREQ(maj, min) \ + ((__clang_major__ << 16) + __clang_minor__ >= ((maj) << 16) + (min)) +#else +# define PAM_CLANG_PREREQ(maj, min) 0 +#endif + +#if PAM_GNUC_PREREQ(2, 7) +# define PAM_ATTRIBUTE_ALIGNED(arg) __attribute__((__aligned__(arg))) +#else +# define PAM_ATTRIBUTE_ALIGNED(arg) /* empty */ +#endif + +#if PAM_GNUC_PREREQ(4, 6) +# define DIAG_PUSH_IGNORE_CAST_QUAL \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +# define DIAG_POP_IGNORE_CAST_QUAL \ + _Pragma("GCC diagnostic pop") +# define DIAG_PUSH_IGNORE_CAST_ALIGN \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wcast-align\"") +# define DIAG_POP_IGNORE_CAST_ALIGN \ + _Pragma("GCC diagnostic pop") +# define DIAG_PUSH_IGNORE_FORMAT_NONLITERAL \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") +# define DIAG_POP_IGNORE_FORMAT_NONLITERAL \ + _Pragma("GCC diagnostic pop") +#elif PAM_CLANG_PREREQ(2, 6) +# define DIAG_PUSH_IGNORE_CAST_QUAL \ + _Pragma("clang diagnostic push"); \ + _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +# define DIAG_POP_IGNORE_CAST_QUAL \ + _Pragma("clang diagnostic pop") +# define DIAG_PUSH_IGNORE_CAST_ALIGN \ + _Pragma("clang diagnostic push"); \ + _Pragma("clang diagnostic ignored \"-Wcast-align\"") +# define DIAG_POP_IGNORE_CAST_ALIGN \ + _Pragma("clang diagnostic pop") +# define DIAG_PUSH_IGNORE_FORMAT_NONLITERAL \ + _Pragma("clang diagnostic push"); \ + _Pragma("clang diagnostic ignored \"-Wformat-nonliteral\"") +# define DIAG_POP_IGNORE_FORMAT_NONLITERAL \ + _Pragma("clang diagnostic pop") +#else +# define DIAG_PUSH_IGNORE_CAST_QUAL /* empty */ +# define DIAG_POP_IGNORE_CAST_QUAL /* empty */ +# define DIAG_PUSH_IGNORE_CAST_ALIGN /* empty */ +# define DIAG_POP_IGNORE_CAST_ALIGN /* empty */ +# define DIAG_PUSH_IGNORE_FORMAT_NONLITERAL /* empty */ +# define DIAG_POP_IGNORE_FORMAT_NONLITERAL /* empty */ +#endif + +/* + * Evaluates to + * 1, if the given two types are known to be the same + * 0, otherwise. + */ +#if PAM_GNUC_PREREQ(3, 0) +# define PAM_IS_SAME_TYPE(x_, y_) \ + __builtin_types_compatible_p(__typeof__(x_), __typeof__(y_)) +#else +/* Cannot tell whether these types are the same. */ +# define PAM_IS_SAME_TYPE(x_, y_) 0 +#endif + +#endif /* PAM_CC_COMPAT_H */ diff --git a/libpam/include/pam_i18n.h b/libpam/include/pam_i18n.h new file mode 100644 index 0000000..85a2121 --- /dev/null +++ b/libpam/include/pam_i18n.h @@ -0,0 +1,17 @@ +#ifndef PAM_I18N_H +#define PAM_I18N_H + +#ifdef ENABLE_NLS + +# include +# define _(msgid) dgettext(PACKAGE, msgid) +# define N_(msgid) msgid + +#else + +# define _(msgid) (msgid) +# define N_(msgid) msgid + +#endif /* ENABLE_NLS */ + +#endif /* PAM_I18N_H */ diff --git a/libpam/include/pam_inline.h b/libpam/include/pam_inline.h new file mode 100644 index 0000000..cc30224 --- /dev/null +++ b/libpam/include/pam_inline.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2020 Dmitry V. Levin + * + * Handy inline functions and macros providing some convenient functionality + * to libpam and its modules. + */ + +#ifndef PAM_INLINE_H +#define PAM_INLINE_H + +#include "pam_cc_compat.h" +#include +#include +#include +#include + +/* + * Evaluates to + * - a syntax error if the argument is 0, + * 0, otherwise. + */ +#define PAM_FAIL_BUILD_ON_ZERO(e_) (sizeof(int[-1 + 2 * !!(e_)]) * 0) + +/* + * Evaluates to + * 1, if the given type is known to be a non-array type + * 0, otherwise. + */ +#define PAM_IS_NOT_ARRAY(a_) PAM_IS_SAME_TYPE((a_), &(a_)[0]) + +/* + * Evaluates to + * - a syntax error if the argument is not an array, + * 0, otherwise. + */ +#define PAM_MUST_BE_ARRAY(a_) PAM_FAIL_BUILD_ON_ZERO(!PAM_IS_NOT_ARRAY(a_)) +/* + * Evaluates to + * - a syntax error if the argument is an array, + * 0, otherwise. + */ +#define PAM_MUST_NOT_BE_ARRAY(a_) PAM_FAIL_BUILD_ON_ZERO(PAM_IS_NOT_ARRAY(a_)) + +/* Evaluates to the number of elements in the specified array. */ +#define PAM_ARRAY_SIZE(a_) (sizeof(a_) / sizeof((a_)[0]) + PAM_MUST_BE_ARRAY(a_)) + +/* + * Zero-extend a signed integer type to unsigned long long. + */ +# define zero_extend_signed_to_ull(v_) \ + (sizeof(v_) == sizeof(char) ? (unsigned long long) (unsigned char) (v_) : \ + sizeof(v_) == sizeof(short) ? (unsigned long long) (unsigned short) (v_) : \ + sizeof(v_) == sizeof(int) ? (unsigned long long) (unsigned int) (v_) : \ + sizeof(v_) == sizeof(long) ? (unsigned long long) (unsigned long) (v_) : \ + (unsigned long long) (v_)) + +/* + * Sign-extend an unsigned integer type to long long. + */ +# define sign_extend_unsigned_to_ll(v_) \ + (sizeof(v_) == sizeof(char) ? (long long) (signed char) (v_) : \ + sizeof(v_) == sizeof(short) ? (long long) (signed short) (v_) : \ + sizeof(v_) == sizeof(int) ? (long long) (signed int) (v_) : \ + sizeof(v_) == sizeof(long) ? (long long) (signed long) (v_) : \ + (long long) (v_)) + +/* + * Returns NULL if STR does not start with PREFIX, + * or a pointer to the first char in STR after PREFIX. + * The length of PREFIX is specified by PREFIX_LEN. + */ +static inline const char * +pam_str_skip_prefix_len(const char *str, const char *prefix, size_t prefix_len) +{ + return strncmp(str, prefix, prefix_len) ? NULL : str + prefix_len; +} + +#define pam_str_skip_prefix(str_, prefix_) \ + pam_str_skip_prefix_len((str_), (prefix_), sizeof(prefix_) - 1 + PAM_MUST_BE_ARRAY(prefix_)) + +/* + * Returns NULL if STR does not start with PREFIX + * (ignoring the case of the characters), + * or a pointer to the first char in STR after PREFIX. + * The length of PREFIX is specified by PREFIX_LEN. + */ +static inline const char * +pam_str_skip_icase_prefix_len(const char *str, const char *prefix, size_t prefix_len) +{ + return strncasecmp(str, prefix, prefix_len) ? NULL : str + prefix_len; +} + +#define pam_str_skip_icase_prefix(str_, prefix_) \ + pam_str_skip_icase_prefix_len((str_), (prefix_), sizeof(prefix_) - 1 + PAM_MUST_BE_ARRAY(prefix_)) + + +/* + * Macros to securely erase memory + */ + +#ifdef HAVE_MEMSET_EXPLICIT +static inline void pam_overwrite_n(void *ptr, size_t len) +{ + if (ptr) + memset_explicit(ptr, '\0', len); +} +#elif defined HAVE_EXPLICIT_BZERO +static inline void pam_overwrite_n(void *ptr, size_t len) +{ + if (ptr) + explicit_bzero(ptr, len); +} +#else +static inline void pam_overwrite_n(void *ptr, size_t len) +{ + if (ptr) { + ptr = memset(ptr, '\0', len); + __asm__ __volatile__ ("" : : "r"(ptr) : "memory"); + } +} +#endif + +#define pam_overwrite_string(x) \ +do { \ + char *xx__ = (x) + PAM_MUST_NOT_BE_ARRAY(x); \ + if (xx__) \ + pam_overwrite_n(xx__, strlen(xx__)); \ +} while(0) + +#define pam_overwrite_array(x) pam_overwrite_n(x, sizeof(x) + PAM_MUST_BE_ARRAY(x)) + +#define pam_overwrite_object(x) pam_overwrite_n(x, sizeof(*(x)) + PAM_MUST_NOT_BE_ARRAY(x)) + +static inline void +pam_drop_response(struct pam_response *reply, int replies) +{ + int reply_i; + + for (reply_i = 0; reply_i < replies; ++reply_i) { + if (reply[reply_i].resp) { + pam_overwrite_string(reply[reply_i].resp); + free(reply[reply_i].resp); + } + } + free(reply); +} + + +static inline int +pam_read_passwords(int fd, int npass, char **passwords) +{ + /* + * The passwords array must contain npass preallocated + * buffers of length PAM_MAX_RESP_SIZE + 1. + */ + int rbytes = 0; + int offset = 0; + int i = 0; + char *pptr; + while (npass > 0) { + rbytes = read(fd, passwords[i]+offset, PAM_MAX_RESP_SIZE+1-offset); + + if (rbytes < 0) { + if (errno == EINTR) { + continue; + } + break; + } + if (rbytes == 0) { + break; + } + + while (npass > 0 && + (pptr = memchr(passwords[i] + offset, '\0', rbytes)) != NULL) { + ++pptr; /* skip the '\0' */ + rbytes -= pptr - (passwords[i] + offset); + i++; + offset = 0; + npass--; + if (rbytes > 0) { + if (npass > 0) { + memcpy(passwords[i], pptr, rbytes); + } + pam_overwrite_n(pptr, rbytes); + } + } + offset += rbytes; + } + + /* clear up */ + if (offset > 0 && npass > 0) { + pam_overwrite_n(passwords[i], offset); + } + + return i; +} + +static inline int +pam_consttime_streq(const char *userinput, const char *secret) { + volatile const char *u = userinput, *s = secret; + volatile int ret = 0; + + do { + ret |= *u ^ *s; + + s += !!*s; + } while (*u++ != '\0'); + + return ret == 0; +} + +#endif /* PAM_INLINE_H */ diff --git a/libpam/include/security/_pam_compat.h b/libpam/include/security/_pam_compat.h new file mode 100644 index 0000000..a5f58e4 --- /dev/null +++ b/libpam/include/security/_pam_compat.h @@ -0,0 +1,126 @@ +#ifndef _PAM_COMPAT_H +#define _PAM_COMPAT_H + +/* + * This file was contributed by Derrick J Brashear + * slight modification by Brad M. Garcia + * + * A number of operating systems have started to implement PAM. + * unfortunately, they have a different set of numeric values for + * certain constants. This file is included for compatibility's sake. + */ + +/* Solaris uses different constants. We redefine to those here */ +#if defined(solaris) || (defined(__SVR4) && defined(sun)) + +# ifdef _SECURITY_PAM_MODULES_H + +/* flags for pam_chauthtok() */ +# undef PAM_PRELIM_CHECK +# define PAM_PRELIM_CHECK 0x1 + +# undef PAM_UPDATE_AUTHTOK +# define PAM_UPDATE_AUTHTOK 0x2 + +# endif /* _SECURITY_PAM_MODULES_H */ + +# ifdef _SECURITY__PAM_TYPES_H + +/* generic for pam_* functions */ +# undef PAM_SILENT +# define PAM_SILENT 0x80000000 + +# undef PAM_CHANGE_EXPIRED_AUTHTOK +# define PAM_CHANGE_EXPIRED_AUTHTOK 0x4 + +/* flags for pam_setcred() */ +# undef PAM_ESTABLISH_CRED +# define PAM_ESTABLISH_CRED 0x1 + +# undef PAM_DELETE_CRED +# define PAM_DELETE_CRED 0x2 + +# undef PAM_REINITIALIZE_CRED +# define PAM_REINITIALIZE_CRED 0x4 + +# undef PAM_REFRESH_CRED +# define PAM_REFRESH_CRED 0x8 + +/* another binary incompatibility comes from the return codes! */ + +# undef PAM_CONV_ERR +# define PAM_CONV_ERR 6 + +# undef PAM_PERM_DENIED +# define PAM_PERM_DENIED 7 + +# undef PAM_MAXTRIES +# define PAM_MAXTRIES 8 + +# undef PAM_AUTH_ERR +# define PAM_AUTH_ERR 9 + +# undef PAM_NEW_AUTHTOK_REQD +# define PAM_NEW_AUTHTOK_REQD 10 + +# undef PAM_CRED_INSUFFICIENT +# define PAM_CRED_INSUFFICIENT 11 + +# undef PAM_AUTHINFO_UNAVAIL +# define PAM_AUTHINFO_UNAVAIL 12 + +# undef PAM_USER_UNKNOWN +# define PAM_USER_UNKNOWN 13 + +# undef PAM_CRED_UNAVAIL +# define PAM_CRED_UNAVAIL 14 + +# undef PAM_CRED_EXPIRED +# define PAM_CRED_EXPIRED 15 + +# undef PAM_CRED_ERR +# define PAM_CRED_ERR 16 + +# undef PAM_ACCT_EXPIRED +# define PAM_ACCT_EXPIRED 17 + +# undef PAM_AUTHTOK_EXPIRED +# define PAM_AUTHTOK_EXPIRED 18 + +# undef PAM_SESSION_ERR +# define PAM_SESSION_ERR 19 + +# undef PAM_AUTHTOK_ERR +# define PAM_AUTHTOK_ERR 20 + +# undef PAM_AUTHTOK_RECOVERY_ERR +# define PAM_AUTHTOK_RECOVERY_ERR 21 + +# undef PAM_AUTHTOK_LOCK_BUSY +# define PAM_AUTHTOK_LOCK_BUSY 22 + +# undef PAM_AUTHTOK_DISABLE_AGING +# define PAM_AUTHTOK_DISABLE_AGING 23 + +# undef PAM_NO_MODULE_DATA +# define PAM_NO_MODULE_DATA 24 + +# undef PAM_IGNORE +# define PAM_IGNORE 25 + +# undef PAM_ABORT +# define PAM_ABORT 26 + +# undef PAM_TRY_AGAIN +# define PAM_TRY_AGAIN 27 + +#endif /* _SECURITY__PAM_TYPES_H */ + +#else + +/* For compatibility with old Linux-PAM implementations. */ +#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR + +#endif /* defined(solaris) || (defined(__SVR4) && defined(sun)) */ + +#endif /* _PAM_COMPAT_H */ diff --git a/libpam/include/security/_pam_macros.h b/libpam/include/security/_pam_macros.h new file mode 100644 index 0000000..d01f51f --- /dev/null +++ b/libpam/include/security/_pam_macros.h @@ -0,0 +1,224 @@ +#ifndef PAM_MACROS_H +#define PAM_MACROS_H + +/* + * All kind of macros used by PAM, but usable in some other + * programs too. + * Organized by Cristian Gafton + */ + +#include "_pam_types.h" + +/* a 'safe' version of strdup */ + +#include +#include + +#define x_strdup(s) ( (s) ? strdup(s):NULL ) + +/* + * WARNING: Do NOT use these overwrite macros, as they do not reliable + * override the memory. + */ + +#define _pam_overwrite(x) \ +do { \ + PAM_DEPRECATED register char *xx_; \ + if ((xx_=(x))) \ + while (*xx_) \ + *xx_++ = '\0'; \ +} while (0) + +#define _pam_overwrite_n(x,n) \ +do { \ + PAM_DEPRECATED register char *xx_; \ + register unsigned int i_ = 0; \ + if ((xx_=(x))) \ + for (;i_<(n); i_++) \ + xx_[i_] = 0; \ +} while (0) + +/* + * Don't just free it, forget it too. + */ + +#define _pam_drop(X) \ +do { \ + free(X); \ + (X)=NULL; \ +} while (0) + +/* + * WARNING: Do NOT use this macro, as it does not reliably override the memory. + */ + +#define _pam_drop_reply(/* struct pam_response * */ reply, /* int */ replies) \ +do { \ + PAM_DEPRECATED int reply_i; \ + \ + for (reply_i=0; reply_i<(replies); ++reply_i) { \ + if ((reply)[reply_i].resp) { \ + _pam_overwrite((reply)[reply_i].resp); \ + free((reply)[reply_i].resp); \ + } \ + } \ + free(reply); \ +} while (0) + +/* some debugging code */ + +#ifdef PAM_DEBUG + +/* + * This provides the necessary function to do debugging in PAM. + * Cristian Gafton + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * This is for debugging purposes ONLY. DO NOT use on live systems !!! + * You have been warned :-) - CG + * + * to get automated debugging to the log file, it must be created manually. + * _PAM_LOGFILE must exist and be writable to the programs you debug. + */ + +#ifndef _PAM_LOGFILE +#define _PAM_LOGFILE "/var/run/pam-debug.log" +#endif + +#ifdef PAM_NO_HEADER_FUNCTIONS +UNUSED +extern void _pam_output_debug_info(const char *file, const char *fn + , const int line); +UNUSED +PAM_FORMAT((printf, 1, 2)) +extern void _pam_output_debug(const char *format, ...); +#else +#ifdef PAM_DEBUG_C +#define PAM_DEBUG_SCOPE +#else +#define PAM_DEBUG_SCOPE static +#endif + +#ifdef UNUSED +UNUSED +#endif +PAM_DEBUG_SCOPE void _pam_output_debug_info(const char *file, const char *fn + , const int line) +{ + FILE *logfile; + int must_close = 1, fd; + +#ifdef O_NOFOLLOW + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_NOFOLLOW|O_APPEND)) != -1) { +#else + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_APPEND)) != -1) { +#endif + if (!(logfile = fdopen(fd,"a"))) { + logfile = stderr; + must_close = 0; + close(fd); + } + } else { + logfile = stderr; + must_close = 0; + } + fprintf(logfile,"[%s:%s(%d)] ",file, fn, line); + fflush(logfile); + if (must_close) + fclose(logfile); +} + +#ifdef UNUSED +UNUSED +#endif +PAM_FORMAT((printf, 1, 2)) +PAM_DEBUG_SCOPE void _pam_output_debug(const char *format, ...) +{ + va_list args; + FILE *logfile; + int must_close = 1, fd; + + va_start(args, format); + +#ifdef O_NOFOLLOW + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_NOFOLLOW|O_APPEND)) != -1) { +#else + if ((fd = open(_PAM_LOGFILE, O_WRONLY|O_APPEND)) != -1) { +#endif + if (!(logfile = fdopen(fd,"a"))) { + logfile = stderr; + must_close = 0; + close(fd); + } + } else { + logfile = stderr; + must_close = 0; + } + vfprintf(logfile, format, args); + fprintf(logfile, "\n"); + fflush(logfile); + if (must_close) + fclose(logfile); + + va_end(args); +} +#undef PAM_DEBUG_SCOPE +#endif + +#define D(x) do { \ + _pam_output_debug_info(__FILE__, __FUNCTION__, __LINE__); \ + _pam_output_debug x ; \ +} while (0) + +#define _pam_show_mem(X,XS) do { \ + int i; \ + register unsigned char *x; \ + x = (unsigned char *)X; \ + fprintf(stderr, " \n", X); \ + for (i = 0; i < XS ; ++x, ++i) { \ + fprintf(stderr, " %02X. <%p:%02X>\n", i, x, *x); \ + } \ + fprintf(stderr, " \n", X, XS); \ +} while (0) + +#define _pam_show_reply(/* struct pam_response * */reply, /* int */replies) \ +do { \ + int reply_i; \ + setbuf(stderr, NULL); \ + fprintf(stderr, "array at %p of size %d\n",reply,replies); \ + fflush(stderr); \ + if (reply) { \ + for (reply_i = 0; reply_i < replies; reply_i++) { \ + fprintf(stderr, " elem# %d at %p: resp = %p, retcode = %d\n", \ + reply_i, reply+reply_i, reply[reply_i].resp, \ + reply[reply_i].resp, _retcode); \ + fflush(stderr); \ + if (reply[reply_i].resp) { \ + fprintf(stderr, " resp[%d] = '%s'\n", \ + strlen(reply[reply_i].resp), reply[reply_i].resp); \ + fflush(stderr); \ + } \ + } \ + } \ + fprintf(stderr, "done here\n"); \ + fflush(stderr); \ +} while (0) + +#else + +#define D(x) do { } while (0) +#define _pam_show_mem(X,XS) do { } while (0) +#define _pam_show_reply(reply, replies) do { } while (0) + +#endif /* PAM_DEBUG */ + +#endif /* PAM_MACROS_H */ diff --git a/libpam/include/security/_pam_types.h b/libpam/include/security/_pam_types.h new file mode 100644 index 0000000..f2d3e20 --- /dev/null +++ b/libpam/include/security/_pam_types.h @@ -0,0 +1,339 @@ +/* + * + * + * This file defines all of the types common to the Linux-PAM library + * applications and modules. + * + * Note, the copyright+license information is at end of file. + */ + +#ifndef _SECURITY__PAM_TYPES_H +#define _SECURITY__PAM_TYPES_H + +/* This is a blind structure; users aren't allowed to see inside a + * pam_handle_t, so we don't define struct pam_handle here. This is + * defined in a file private to the PAM library. (i.e., it's private + * to PAM service modules, too!) */ + +typedef struct pam_handle pam_handle_t; + +/* ---------------- The Linux-PAM Version defines ----------------- */ + +/* Major and minor version number of the Linux-PAM package. Use + these macros to test for features in specific releases. */ +#define __LINUX_PAM__ 1 +#define __LINUX_PAM_MINOR__ 7 + +/* ----------------- The Linux-PAM return values ------------------ */ + +#define PAM_SUCCESS 0 /* Successful function return */ +#define PAM_OPEN_ERR 1 /* dlopen() failure when dynamically */ + /* loading a service module */ +#define PAM_SYMBOL_ERR 2 /* Symbol not found */ +#define PAM_SERVICE_ERR 3 /* Error in service module */ +#define PAM_SYSTEM_ERR 4 /* System error */ +#define PAM_BUF_ERR 5 /* Memory buffer error */ +#define PAM_PERM_DENIED 6 /* Permission denied */ +#define PAM_AUTH_ERR 7 /* Authentication failure */ +#define PAM_CRED_INSUFFICIENT 8 /* Can not access authentication data */ + /* due to insufficient credentials */ +#define PAM_AUTHINFO_UNAVAIL 9 /* Underlying authentication service */ + /* can not retrieve authentication */ + /* information */ +#define PAM_USER_UNKNOWN 10 /* User not known to the underlying */ + /* authentication module */ +#define PAM_MAXTRIES 11 /* An authentication service has */ + /* maintained a retry count which has */ + /* been reached. No further retries */ + /* should be attempted */ +#define PAM_NEW_AUTHTOK_REQD 12 /* New authentication token required. */ + /* This is normally returned if the */ + /* machine security policies require */ + /* that the password should be changed */ + /* because the password is NULL or it */ + /* has aged */ +#define PAM_ACCT_EXPIRED 13 /* User account has expired */ +#define PAM_SESSION_ERR 14 /* Can not make/remove an entry for */ + /* the specified session */ +#define PAM_CRED_UNAVAIL 15 /* Underlying authentication service */ + /* can not retrieve user credentials */ + /* unavailable */ +#define PAM_CRED_EXPIRED 16 /* User credentials expired */ +#define PAM_CRED_ERR 17 /* Failure setting user credentials */ +#define PAM_NO_MODULE_DATA 18 /* No module specific data is present */ +#define PAM_CONV_ERR 19 /* Conversation error */ +#define PAM_AUTHTOK_ERR 20 /* Authentication token manipulation error */ +#define PAM_AUTHTOK_RECOVERY_ERR 21 /* Authentication information */ + /* cannot be recovered */ +#define PAM_AUTHTOK_LOCK_BUSY 22 /* Authentication token lock busy */ +#define PAM_AUTHTOK_DISABLE_AGING 23 /* Authentication token aging disabled */ +#define PAM_TRY_AGAIN 24 /* Preliminary check by password service */ +#define PAM_IGNORE 25 /* Ignore underlying account module */ + /* regardless of whether the control */ + /* flag is required, optional, or sufficient */ +#define PAM_ABORT 26 /* Critical error (?module fail now request) */ +#define PAM_AUTHTOK_EXPIRED 27 /* user's authentication token has expired */ +#define PAM_MODULE_UNKNOWN 28 /* module is not known */ + +#define PAM_BAD_ITEM 29 /* Bad item passed to pam_*_item() */ +#define PAM_CONV_AGAIN 30 /* conversation function is event driven + and data is not available yet */ +#define PAM_INCOMPLETE 31 /* please call this function again to + complete authentication stack. Before + calling again, verify that conversation + is completed */ + +/* + * Add new #define's here - take care to also extend the libpam code: + * pam_strerror() and "libpam/pam_tokens.h" . + */ + +#define _PAM_RETURN_VALUES 32 /* this is the number of return values */ + + +/* ---------------------- The Linux-PAM flags -------------------- */ + +/* Authentication service should not generate any messages */ +#define PAM_SILENT 0x8000U + +/* Note: these flags are used by pam_authenticate{,_secondary}() */ + +/* The authentication service should return PAM_AUTH_ERROR if the + * user has a null authentication token */ +#define PAM_DISALLOW_NULL_AUTHTOK 0x0001U + +/* Note: these flags are used for pam_setcred() */ + +/* Set user credentials for an authentication service */ +#define PAM_ESTABLISH_CRED 0x0002U + +/* Delete user credentials associated with an authentication service */ +#define PAM_DELETE_CRED 0x0004U + +/* Reinitialize user credentials */ +#define PAM_REINITIALIZE_CRED 0x0008U + +/* Extend lifetime of user credentials */ +#define PAM_REFRESH_CRED 0x0010U + +/* Note: these flags are used by pam_chauthtok */ + +/* The password service should only update those passwords that have + * aged. If this flag is not passed, the password service should + * update all passwords. */ +#define PAM_CHANGE_EXPIRED_AUTHTOK 0x0020U + +/* ------------------ The Linux-PAM item types ------------------- */ + +/* These defines are used by pam_set_item() and pam_get_item(). + Please check the spec which are allowed for use by applications + and which are only allowed for use by modules. */ + +#define PAM_SERVICE 1 /* The service name */ +#define PAM_USER 2 /* The user name */ +#define PAM_TTY 3 /* The tty name */ +#define PAM_RHOST 4 /* The remote host name */ +#define PAM_CONV 5 /* The pam_conv structure */ +#define PAM_AUTHTOK 6 /* The authentication token (password) */ +#define PAM_OLDAUTHTOK 7 /* The old authentication token */ +#define PAM_RUSER 8 /* The remote user name */ +#define PAM_USER_PROMPT 9 /* the prompt for getting a username */ +/* Linux-PAM extensions */ +#define PAM_FAIL_DELAY 10 /* app supplied function to override failure + delays */ +#define PAM_XDISPLAY 11 /* X display name */ +#define PAM_XAUTHDATA 12 /* X server authentication data */ +#define PAM_AUTHTOK_TYPE 13 /* The type for pam_get_authtok */ + +/* -------------- Special defines used by Linux-PAM -------------- */ + +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +# define PAM_GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +# define PAM_GNUC_PREREQ(maj, min) 0 +#endif + +#if PAM_GNUC_PREREQ(2,5) +# define PAM_FORMAT(params) __attribute__((__format__ params)) +#else +# define PAM_FORMAT(params) +#endif + +#if PAM_GNUC_PREREQ(3,1) +# define PAM_DEPRECATED __attribute__((__deprecated__)) +#else +# define PAM_DEPRECATED +#endif + +#if PAM_GNUC_PREREQ(3,3) && !defined(LIBPAM_COMPILE) +# define PAM_NONNULL(params) __attribute__((__nonnull__ params)) +#else +# define PAM_NONNULL(params) +#endif + +/* ---------- Common Linux-PAM application/module PI ----------- */ + +extern int PAM_NONNULL((1)) +pam_set_item(pam_handle_t *pamh, int item_type, const void *item); + +extern int PAM_NONNULL((1)) +pam_get_item(const pam_handle_t *pamh, int item_type, const void **item); + +extern const char * +pam_strerror(pam_handle_t *pamh, int errnum); + +extern int PAM_NONNULL((1,2)) +pam_putenv(pam_handle_t *pamh, const char *name_value); + +extern const char * PAM_NONNULL((1,2)) +pam_getenv(pam_handle_t *pamh, const char *name); + +extern char ** PAM_NONNULL((1)) +pam_getenvlist(pam_handle_t *pamh); + +/* ---------- Common Linux-PAM application/module PI ----------- */ + +/* + * here are some proposed error status definitions for the + * 'error_status' argument used by the cleanup function associated + * with data items they should be logically OR'd with the error_status + * of the latest return from libpam -- new with .52 and positive + * impression from Sun although not official as of 1996/9/4 + * [generally the other flags are to be found in pam_modules.h] + */ + +#define PAM_DATA_SILENT 0x40000000 /* used to suppress messages... */ + +/* + * here we define an externally (by apps or modules) callable function + * that primes the libpam library to delay when a stacked set of + * modules results in a failure. In the case of PAM_SUCCESS this delay + * is ignored. + * + * Note, the pam_[gs]et_item(... PAM_FAIL_DELAY ...) can be used to set + * a function pointer which can override the default fail-delay behavior. + * This item was added to accommodate event driven programs that need to + * manage delays more carefully. The function prototype for this data + * item is + * void (*fail_delay)(int status, unsigned int delay, void *appdata_ptr); + */ + +#define HAVE_PAM_FAIL_DELAY +extern int pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay); + +/* ------------ The Linux-PAM conversation structures ------------ */ + +/* Message styles */ + +#define PAM_PROMPT_ECHO_OFF 1 +#define PAM_PROMPT_ECHO_ON 2 +#define PAM_ERROR_MSG 3 +#define PAM_TEXT_INFO 4 + +/* Linux-PAM specific types */ + +#define PAM_RADIO_TYPE 5 /* yes/no/maybe conditionals */ + +/* This is for server client non-human interaction.. these are NOT + part of the X/Open PAM specification. */ + +#define PAM_BINARY_PROMPT 7 + +/* maximum size of messages/responses etc.. (these are mostly + arbitrary so Linux-PAM should handle longer values). */ + +#define PAM_MAX_NUM_MSG 32 +#define PAM_MAX_MSG_SIZE 512 +#define PAM_MAX_RESP_SIZE 512 + +/* Used to pass prompting text, error messages, or other informatory + * text to the user. This structure is allocated and freed by the PAM + * library (or loaded module). */ + +struct pam_message { + int msg_style; + const char *msg; +}; + +/* if the pam_message.msg_style = PAM_BINARY_PROMPT + the 'pam_message.msg' is a pointer to a 'const *' for the following + pseudo-structure. When used with a PAM_BINARY_PROMPT, the returned + pam_response.resp pointer points to an object with the following + structure: + + struct { + u32 length; # network byte order + unsigned char type; + unsigned char data[length-5]; + }; + + The 'libpamc' library is designed around this flavor of + message and should be used to handle this flavor of msg_style. + */ + +/* Used to return the user's response to the PAM library. This + structure is allocated by the application program, and free()'d by + the Linux-PAM library (or calling module). */ + +struct pam_response { + char *resp; + int resp_retcode; /* currently un-used, zero expected */ +}; + +/* The actual conversation structure itself */ + +struct pam_conv { + int (*conv)(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr); + void *appdata_ptr; +}; + +/* Used by the PAM_XAUTHDATA pam item. Contains X authentication + data used by modules to connect to the user's X display. Note: + this structure is intentionally compatible with xcb_auth_info_t. */ + +struct pam_xauth_data { + int namelen; + char *name; + int datalen; + char *data; +}; + +/* ... adapted from the pam_appl.h file created by Theodore Ts'o and + * + * Copyright Theodore Ts'o, 1996. All rights reserved. + * Copyright (c) Andrew G. Morgan , 1996-8 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#endif /* _SECURITY__PAM_TYPES_H */ diff --git a/libpam/include/security/meson.build b/libpam/include/security/meson.build new file mode 100644 index 0000000..1a712c9 --- /dev/null +++ b/libpam/include/security/meson.build @@ -0,0 +1,10 @@ +install_headers([ + '_pam_compat.h', + '_pam_macros.h', + '_pam_types.h', + 'pam_appl.h', + 'pam_ext.h', + 'pam_modules.h', + 'pam_modutil.h', + ], + install_dir: includedir) diff --git a/libpam/include/security/pam_appl.h b/libpam/include/security/pam_appl.h new file mode 100644 index 0000000..cf97a49 --- /dev/null +++ b/libpam/include/security/pam_appl.h @@ -0,0 +1,104 @@ +/* + * + * + * This header file collects definitions for the PAM API --- that is, + * public interface between the PAM library and an application program + * that wishes to use it. + * + * Note, the copyright information is at end of file. + */ + +#ifndef _SECURITY_PAM_APPL_H +#define _SECURITY_PAM_APPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* Linux-PAM common defined types */ + +/* -------------- The Linux-PAM Framework layer API ------------- */ + +extern int PAM_NONNULL((1,3,4)) +pam_start(const char *service_name, const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh); + +extern int PAM_NONNULL((1,3,5)) +pam_start_confdir(const char *service_name, const char *user, + const struct pam_conv *pam_conversation, + const char *confdir, pam_handle_t **pamh); + +extern int PAM_NONNULL((1)) +pam_end(pam_handle_t *pamh, int pam_status); + +/* Authentication API's */ + +extern int PAM_NONNULL((1)) +pam_authenticate(pam_handle_t *pamh, int flags); + +extern int PAM_NONNULL((1)) +pam_setcred(pam_handle_t *pamh, int flags); + +/* Account Management API's */ + +extern int PAM_NONNULL((1)) +pam_acct_mgmt(pam_handle_t *pamh, int flags); + +/* Session Management API's */ + +extern int PAM_NONNULL((1)) +pam_open_session(pam_handle_t *pamh, int flags); + +extern int PAM_NONNULL((1)) +pam_close_session(pam_handle_t *pamh, int flags); + +/* Password Management API's */ + +extern int PAM_NONNULL((1)) +pam_chauthtok(pam_handle_t *pamh, int flags); + + +/* take care of any compatibility issues */ +#include + +#ifdef __cplusplus +} +#endif + +/* + * Copyright Theodore Ts'o, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#endif /* _SECURITY_PAM_APPL_H */ diff --git a/libpam/include/security/pam_ext.h b/libpam/include/security/pam_ext.h new file mode 100644 index 0000000..7542861 --- /dev/null +++ b/libpam/include/security/pam_ext.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2005, 2006, 2008, 2009 Thorsten Kukuk. + * + * + * + * This header file collects definitions for the extended PAM API. + * This is a public interface of the PAM library for PAM modules, + * which makes the life of PAM developers easier, but are not documented + * in any standard and are not portable between different PAM + * implementations. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SECURITY__PAM_EXT_H_ +#define _SECURITY__PAM_EXT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +extern void PAM_FORMAT((printf, 3, 0)) PAM_NONNULL((3)) +pam_vsyslog (const pam_handle_t *pamh, int priority, + const char *fmt, va_list args); + +extern void PAM_FORMAT((printf, 3, 4)) PAM_NONNULL((3)) +pam_syslog (const pam_handle_t *pamh, int priority, const char *fmt, ...); + +extern int PAM_FORMAT((printf, 4, 0)) PAM_NONNULL((1,4)) +pam_vprompt (pam_handle_t *pamh, int style, char **response, + const char *fmt, va_list args); + +extern int PAM_FORMAT((printf, 4, 5)) PAM_NONNULL((1,4)) +pam_prompt (pam_handle_t *pamh, int style, char **response, + const char *fmt, ...); + +#define pam_error(pamh, fmt...) \ + pam_prompt(pamh, PAM_ERROR_MSG, NULL, fmt) +#define pam_verror(pamh, fmt, args) \ + pam_vprompt(pamh, PAM_ERROR_MSG, NULL, fmt, args) + +#define pam_info(pamh, fmt...) pam_prompt(pamh, PAM_TEXT_INFO, NULL, fmt) +#define pam_vinfo(pamh, fmt, args) pam_vprompt(pamh, PAM_TEXT_INFO, NULL, fmt, args) + +extern int PAM_NONNULL((1,3)) +pam_get_authtok (pam_handle_t *pamh, int item, const char **authtok, + const char *prompt); +extern int PAM_NONNULL((1,2)) +pam_get_authtok_noverify (pam_handle_t *pamh, const char **authtok, + const char *prompt); +extern int PAM_NONNULL((1,2)) +pam_get_authtok_verify (pam_handle_t *pamh, const char **authtok, + const char *prompt); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libpam/include/security/pam_modules.h b/libpam/include/security/pam_modules.h new file mode 100644 index 0000000..ec65e3e --- /dev/null +++ b/libpam/include/security/pam_modules.h @@ -0,0 +1,124 @@ +/* + * + * + * This header file collects definitions for the PAM API --- that is, + * public interface between the PAM library and PAM modules. + * + * Note, the copyright information is at end of file. + */ + +#ifndef _SECURITY_PAM_MODULES_H +#define _SECURITY_PAM_MODULES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* Linux-PAM common defined types */ + +/* -------------- The Linux-PAM Module PI ------------- */ + +extern int PAM_NONNULL((1,2)) +pam_set_data(pam_handle_t *pamh, const char *module_data_name, void *data, + void (*cleanup)(pam_handle_t *pamh, void *data, + int error_status)); + +extern int PAM_NONNULL((1,2,3)) +pam_get_data(const pam_handle_t *pamh, const char *module_data_name, + const void **data); + +extern int PAM_NONNULL((1,2)) +pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt); + +/* Authentication API's */ +int pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char **argv); +int pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +/* Account Management API's */ +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +/* Session Management API's */ +int pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +int pam_sm_close_session(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +/* Password Management API's */ +int pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc, const char **argv); + +/* The following two flags are for use across the Linux-PAM/module + * interface only. The Application is not permitted to use these + * tokens. + * + * The password service should only perform preliminary checks. No + * passwords should be updated. */ +#define PAM_PRELIM_CHECK 0x4000 + +/* The password service should update passwords Note: PAM_PRELIM_CHECK + * and PAM_UPDATE_AUTHTOK cannot both be set simultaneously! */ +#define PAM_UPDATE_AUTHTOK 0x2000 + + +/* + * here are some proposed error status definitions for the + * 'error_status' argument used by the cleanup function associated + * with data items they should be logically OR'd with the error_status + * of the latest return from libpam -- new with .52 and positive + * impression from Sun although not official as of 1996/9/4 there are + * others in _pam_types.h -- they are for common module/app use. + */ + +#define PAM_DATA_REPLACE 0x20000000 /* used when replacing a data item */ + +/* PAM_EXTERN isn't needed anymore, but don't remove it to not break + lot of external code using it. */ +#define PAM_EXTERN extern + +/* take care of any compatibility issues */ +#include + +#ifdef __cplusplus +} +#endif + +/* Copyright (C) Theodore Ts'o, 1996. + * Copyright (C) Andrew Morgan, 1996-8. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU General Public License, in which case the provisions of the + * GNU GPL are required INSTEAD OF the above restrictions. (This + * clause is necessary due to a potential bad interaction between the + * GNU GPL and the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#endif /* _SECURITY_PAM_MODULES_H */ diff --git a/libpam/include/security/pam_modutil.h b/libpam/include/security/pam_modutil.h new file mode 100644 index 0000000..c257832 --- /dev/null +++ b/libpam/include/security/pam_modutil.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2001-2002 Andrew Morgan + * + * + * + * This file is a list of handy libc wrappers that attempt to provide some + * thread-safe and other convenient functionality to modules in a common form. + * + * A number of these functions reserve space in a pam_[sg]et_data item. + * In all cases, the name of the item is prefixed with "pam_modutil_*". + * + * On systems that simply can't support thread safe programming, these + * functions don't support it either - sorry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SECURITY__PAM_MODUTIL_H +#define _SECURITY__PAM_MODUTIL_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +extern int PAM_NONNULL((1,2)) +pam_modutil_check_user_in_passwd(pam_handle_t *pamh, + const char *user_name, + const char *file_name); + +extern struct passwd * PAM_NONNULL((1,2)) +pam_modutil_getpwnam(pam_handle_t *pamh, const char *user); + +extern struct passwd * PAM_NONNULL((1)) +pam_modutil_getpwuid(pam_handle_t *pamh, uid_t uid); + +extern struct group * PAM_NONNULL((1,2)) +pam_modutil_getgrnam(pam_handle_t *pamh, const char *group); + +extern struct group * PAM_NONNULL((1)) +pam_modutil_getgrgid(pam_handle_t *pamh, gid_t gid); + +extern struct spwd * PAM_NONNULL((1,2)) +pam_modutil_getspnam(pam_handle_t *pamh, const char *user); + +extern int PAM_NONNULL((1,2,3)) +pam_modutil_user_in_group_nam_nam(pam_handle_t *pamh, + const char *user, + const char *group); + +extern int PAM_NONNULL((1,2)) +pam_modutil_user_in_group_nam_gid(pam_handle_t *pamh, + const char *user, + gid_t group); + +extern int PAM_NONNULL((1,3)) +pam_modutil_user_in_group_uid_nam(pam_handle_t *pamh, + uid_t user, + const char *group); + +extern int PAM_NONNULL((1)) +pam_modutil_user_in_group_uid_gid(pam_handle_t *pamh, + uid_t user, + gid_t group); + +extern const char * PAM_NONNULL((1)) +pam_modutil_getlogin(pam_handle_t *pamh); + +extern int +pam_modutil_read(int fd, char *buffer, int count); + +extern int +pam_modutil_write(int fd, const char *buffer, int count); + +extern int PAM_NONNULL((1,3)) +pam_modutil_audit_write(pam_handle_t *pamh, int type, + const char *message, int retval); + +struct pam_modutil_privs { + gid_t *grplist; + int number_of_groups; + int allocated; + gid_t old_gid; + uid_t old_uid; + int is_dropped; +}; + +#define PAM_MODUTIL_NGROUPS 64 +#define PAM_MODUTIL_DEF_PRIVS(n) \ + gid_t n##_grplist[PAM_MODUTIL_NGROUPS]; \ + struct pam_modutil_privs n = { n##_grplist, PAM_MODUTIL_NGROUPS, 0, -1, -1, 0 } + +extern int PAM_NONNULL((1,2,3)) +pam_modutil_drop_priv(pam_handle_t *pamh, + struct pam_modutil_privs *p, + const struct passwd *pw); + +extern int PAM_NONNULL((1,2)) +pam_modutil_regain_priv(pam_handle_t *pamh, + struct pam_modutil_privs *p); + +enum pam_modutil_redirect_fd { + PAM_MODUTIL_IGNORE_FD, /* do not redirect */ + PAM_MODUTIL_PIPE_FD, /* redirect to a pipe */ + PAM_MODUTIL_NULL_FD, /* redirect to /dev/null */ +}; + +/* redirect standard descriptors, close all other descriptors. */ +extern int PAM_NONNULL((1)) +pam_modutil_sanitize_helper_fds(pam_handle_t *pamh, + enum pam_modutil_redirect_fd redirect_stdin, + enum pam_modutil_redirect_fd redirect_stdout, + enum pam_modutil_redirect_fd redirect_stderr); + +/************************************************** + * @brief Lookup a value for the key in the file (i.e. login.defs or a similar + * key-value format file). + * + * @param[in] pamh The pam handle structure + * @param[in] file_name Configuration file name + * @param[in] key Lookup key + * + * @return value, or NULL if key was not found. + **************************************************/ +extern char * PAM_NONNULL((1,2,3)) +pam_modutil_search_key(pam_handle_t *pamh, + const char *file_name, + const char *key); + +#ifdef __cplusplus +} +#endif + +#endif /* _SECURITY__PAM_MODUTIL_H */ diff --git a/libpam/include/test_assert.h b/libpam/include/test_assert.h new file mode 100644 index 0000000..879f581 --- /dev/null +++ b/libpam/include/test_assert.h @@ -0,0 +1,58 @@ +/* + * Assert definitions for tests. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#ifndef TEST_ASSERT_H +# define TEST_ASSERT_H + +# include + +# include +# include +# include + +# define ASSERT_(expected_, expected_str_, op_, seen_, seen_str_) \ + do { \ + __typeof__(expected_) e_ = (expected_); \ + __typeof__(seen_) s_ = (seen_); \ + if (e_ op_ s_) break; \ + fprintf(stderr, \ + "%s:%d: Assertion failed: %s (%#lx) %s %s (%#lx)\n", \ + __FILE__, __LINE__, \ + (expected_str_), (unsigned long) e_, #op_, \ + (seen_str_), (unsigned long) s_); \ + abort(); \ + } while (0) \ +/* End of ASSERT_ definition. */ + +# define ASSERT_EQ(expected_, seen_) \ + ASSERT_((expected_), #expected_, ==, (seen_), #seen_) \ +/* End of ASSERT_EQ definition. */ + +# define ASSERT_NE(expected_, seen_) \ + ASSERT_((expected_), #expected_, !=, (seen_), #seen_) \ +/* End of ASSERT_NE definition. */ + +# define ASSERT_LT(expected_, seen_) \ + ASSERT_((expected_), #expected_, <, (seen_), #seen_) \ +/* End of ASSERT_LT definition. */ + +# define ASSERT_LE(expected_, seen_) \ + ASSERT_((expected_), #expected_, <=, (seen_), #seen_) \ +/* End of ASSERT_LT definition. */ + +# define ASSERT_GT(expected_, seen_) \ + ASSERT_((expected_), #expected_, >, (seen_), #seen_) \ +/* End of ASSERT_LT definition. */ + +# define ASSERT_GE(expected_, seen_) \ + ASSERT_((expected_), #expected_, >=, (seen_), #seen_) \ +/* End of ASSERT_LT definition. */ + +# ifndef PATH_MAX +# define PATH_MAX 4096 +# endif + +#endif /* TEST_ASSERT_H */ diff --git a/libpam/libpam.map b/libpam/libpam.map new file mode 100644 index 0000000..3cc7ef3 --- /dev/null +++ b/libpam/libpam.map @@ -0,0 +1,89 @@ +LIBPAM_1.0 { + global: + pam_acct_mgmt; + pam_authenticate; + pam_chauthtok; + pam_close_session; + pam_end; + pam_open_session; + pam_setcred; + pam_start; + pam_getenv; + pam_putenv; + pam_getenvlist; + pam_set_item; + pam_get_item; + pam_strerror; + pam_fail_delay; + pam_set_data; + pam_get_data; + pam_get_user; + + local: + *; +}; +LIBPAM_EXTENSION_1.0 { + global: + pam_prompt; + pam_vprompt; + pam_syslog; + pam_vsyslog; +}; + +LIBPAM_EXTENSION_1.1 { + global: + pam_get_authtok; +} LIBPAM_EXTENSION_1.0; + +LIBPAM_EXTENSION_1.1.1 { + global: + pam_get_authtok_noverify; + pam_get_authtok_verify; +} LIBPAM_EXTENSION_1.1; + +LIBPAM_MODUTIL_1.0 { + global: + pam_modutil_getpwnam; + pam_modutil_getpwuid; + pam_modutil_getgrnam; + pam_modutil_getgrgid; + pam_modutil_getspnam; + pam_modutil_user_in_group_nam_nam; + pam_modutil_user_in_group_nam_gid; + pam_modutil_user_in_group_uid_nam; + pam_modutil_user_in_group_uid_gid; + pam_modutil_getlogin; + pam_modutil_read; + pam_modutil_write; +}; + +LIBPAM_MODUTIL_1.1 { + global: + pam_modutil_audit_write; +} LIBPAM_MODUTIL_1.0; + +LIBPAM_MODUTIL_1.1.3 { + global: + pam_modutil_drop_priv; + pam_modutil_regain_priv; +} LIBPAM_MODUTIL_1.1; + +LIBPAM_MODUTIL_1.1.9 { + global: + pam_modutil_sanitize_helper_fds; +} LIBPAM_MODUTIL_1.1.3; + +LIBPAM_MODUTIL_1.3.2 { + global: + pam_modutil_search_key; +} LIBPAM_MODUTIL_1.1.9; + +LIBPAM_1.4 { + global: + pam_start_confdir; +} LIBPAM_1.0; + +LIBPAM_MODUTIL_1.4.1 { + global: + pam_modutil_check_user_in_passwd; +} LIBPAM_MODUTIL_1.3.2; diff --git a/libpam/meson.build b/libpam/meson.build new file mode 100644 index 0000000..3e8a531 --- /dev/null +++ b/libpam/meson.build @@ -0,0 +1,73 @@ +subdir('include') + +libpam_src = [ + 'pam_account.c', + 'pam_audit.c', + 'pam_auth.c', + 'pam_data.c', + 'pam_delay.c', + 'pam_dispatch.c', + 'pam_dynamic.c', + 'pam_end.c', + 'pam_env.c', + 'pam_get_authtok.c', + 'pam_handlers.c', + 'pam_item.c', + 'pam_misc.c', + 'pam_modutil_check_user.c', + 'pam_modutil_cleanup.c', + 'pam_modutil_getgrgid.c', + 'pam_modutil_getgrnam.c', + 'pam_modutil_getlogin.c', + 'pam_modutil_getpwnam.c', + 'pam_modutil_getpwuid.c', + 'pam_modutil_getspnam.c', + 'pam_modutil_ingroup.c', + 'pam_modutil_ioloop.c', + 'pam_modutil_priv.c', + 'pam_modutil_sanitize.c', + 'pam_modutil_searchkey.c', + 'pam_password.c', + 'pam_prelude.c', + 'pam_session.c', + 'pam_start.c', + 'pam_strerror.c', + 'pam_syslog.c', + 'pam_vprompt.c', +] + +libpam_version = '0.85.1' +libpam_map = 'libpam.map' +libpam_map_path = meson.current_source_dir() / libpam_map + +libpam_link_deps = [libpam_map] +libpam_link_args = ['-Wl,--version-script=' + libpam_map_path] + +libpam = shared_library( + 'pam', + sources: libpam_src, + include_directories: [libpam_inc], + c_args: [ + '-DDEFAULT_MODULE_PATH="@0@/"'.format(securedir), + '-DLIBPAM_COMPILE', + ], + dependencies: [libpam_internal_dep, libaudit, libeconf, libdl, libintl], + link_depends: libpam_link_deps, + link_args: libpam_link_args, + version: libpam_version, + install: true, +) + +libpam_dep = declare_dependency( + include_directories: [libpam_inc], + link_with: [libpam], +) + +pkgconfig.generate( + libpam, + description: 'The primary Linux-PAM library. It is used by PAM modules and PAM-aware applications.', + name: 'PAM', + filebase: 'pam', + version: meson.project_version(), + url: 'http://www.linux-pam.org/' +) diff --git a/libpam/pam.pc.in b/libpam/pam.pc.in new file mode 100644 index 0000000..73049f1 --- /dev/null +++ b/libpam/pam.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@pkgconfig_includedir@ + +Name: PAM +Description: The primary Linux-PAM library. It is used by PAM modules and PAM-aware applications. +URL: http://www.linux-pam.org/ +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lpam diff --git a/libpam/pam_account.c b/libpam/pam_account.c new file mode 100644 index 0000000..c74fa44 --- /dev/null +++ b/libpam/pam_account.c @@ -0,0 +1,23 @@ +/* pam_account.c - PAM Account Management */ + +#include "pam_private.h" + +#include + +int pam_acct_mgmt(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("called")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + retval = _pam_dispatch(pamh, flags, PAM_ACCOUNT); + + return retval; +} diff --git a/libpam/pam_audit.c b/libpam/pam_audit.c new file mode 100644 index 0000000..1fe37c3 --- /dev/null +++ b/libpam/pam_audit.c @@ -0,0 +1,242 @@ +/* pam_audit.c -- Instrumentation code for Linux Auditing System */ + +/* (C) 2005-2006 Red Hat, Inc. -- Licensing details are in the COPYING + file accompanying the Linux-PAM source distribution. + + Authors: + Steve Grubb */ + +#include "pam_private.h" +#include "pam_modutil_private.h" + +#ifdef HAVE_LIBAUDIT +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAMAUDIT_LOGGED 1 + +static int +_pam_audit_writelog(pam_handle_t *pamh, int audit_fd, int type, + const char *message, const char *grantors, int retval) +{ + static int old_errno = -1; + int rc = -ENOMEM; + char *buf; + const char *grantors_field = " grantors="; + + if (grantors == NULL) { + grantors = ""; + grantors_field = ""; + } + + if (asprintf(&buf, "PAM:%s%s%s", message, grantors_field, grantors) >= 0) { + rc = audit_log_acct_message(audit_fd, type, NULL, buf, + (retval != PAM_USER_UNKNOWN && pamh->user) ? pamh->user : "?", + -1, pamh->rhost, NULL, pamh->tty, retval == PAM_SUCCESS); + free(buf); + } + + /* libaudit sets errno to its own negative error code. This can be + an official errno number, but must not. It can also be a audit + internal error code. Which makes errno useless :-((. Try the + best to fix it. */ + errno = -rc; + + pamh->audit_state |= PAMAUDIT_LOGGED; + + if (rc < 0) { + if (rc == -EPERM) + return 0; + if (errno != old_errno) { + old_errno = errno; + pam_syslog (pamh, LOG_CRIT, "audit_log_acct_message() failed: %m"); + } + } + return rc; +} + +static int +_pam_audit_open(pam_handle_t *pamh) +{ + int audit_fd; + audit_fd = audit_open(); + if (audit_fd < 0) { + /* You get these error codes only when the kernel doesn't have + * audit compiled in. */ + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) + return -2; + + /* this should only fail in case of extreme resource shortage, + * need to prevent login in that case for CAPP compliance. + */ + pam_syslog(pamh, LOG_CRIT, "audit_open() failed: %m"); + return -1; + } + + return audit_fd; +} + +static int +_pam_list_grantors(struct handler *hlist, int retval, char **list) +{ + *list = NULL; + + if (retval == PAM_SUCCESS) { + struct handler *h; + char *p = NULL; + size_t len = 0; + + for (h = hlist; h != NULL; h = h->next) { + if (h->grantor) { + len += strlen(h->mod_name) + 1; + } + } + + if (len == 0) { + return 0; + } + + *list = malloc(len); + if (*list == NULL) { + return -1; + } + + for (h = hlist; h != NULL; h = h->next) { + if (h->grantor) { + if (p == NULL) { + p = *list; + } else { + p = stpcpy(p, ","); + } + + p = stpcpy(p, h->mod_name); + } + } + } + + return 0; +} + +int +_pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct handler *h) +{ + const char *message; + int type; + int audit_fd; + char *grantors; + + if ((audit_fd=_pam_audit_open(pamh)) == -1) { + return PAM_SYSTEM_ERR; + } else if (audit_fd == -2) { + return retval; + } + + switch (action) { + case PAM_AUTHENTICATE: + message = "authentication"; + type = AUDIT_USER_AUTH; + break; + case PAM_OPEN_SESSION: + message = "session_open"; + type = AUDIT_USER_START; + break; + case PAM_CLOSE_SESSION: + message = "session_close"; + type = AUDIT_USER_END; + break; + case PAM_ACCOUNT: + message = "accounting"; + type = AUDIT_USER_ACCT; + break; + case PAM_CHAUTHTOK: + message = "chauthtok"; + type = AUDIT_USER_CHAUTHTOK; + break; + case PAM_SETCRED: + message = "setcred"; + if (flags & PAM_ESTABLISH_CRED) + type = AUDIT_CRED_ACQ; + else if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED)) + type = AUDIT_CRED_REFR; + else if (flags & PAM_DELETE_CRED) + type = AUDIT_CRED_DISP; + else + type = AUDIT_USER_ERR; + break; + case _PAM_ACTION_DONE: + message = "bad_ident"; + type = AUDIT_USER_ERR; + break; + default: + message = "UNKNOWN"; + type = AUDIT_USER_ERR; + pam_syslog(pamh, LOG_CRIT, "_pam_auditlog() should never get here"); + retval = PAM_SYSTEM_ERR; + } + + if (_pam_list_grantors(h, retval, &grantors) < 0) { + /* allocation failure */ + pam_syslog(pamh, LOG_CRIT, "_pam_list_grantors() failed: %m"); + retval = PAM_SYSTEM_ERR; + } + + if (_pam_audit_writelog(pamh, audit_fd, type, message, + grantors ? grantors : "?", retval) < 0) + retval = PAM_SYSTEM_ERR; + + free(grantors); + + audit_close(audit_fd); + return retval; +} + +int +_pam_audit_end(pam_handle_t *pamh, int status UNUSED) +{ + if (! (pamh->audit_state & PAMAUDIT_LOGGED)) { + /* PAM library is being shut down without any of the audited + * stacks having been run. Assume that this is sshd faking + * things for an unknown user. + */ + _pam_auditlog(pamh, _PAM_ACTION_DONE, PAM_USER_UNKNOWN, 0, NULL); + } + + return 0; +} + +int +pam_modutil_audit_write(pam_handle_t *pamh, int type, + const char *message, int retval) +{ + int audit_fd; + int rc; + + if ((audit_fd=_pam_audit_open(pamh)) == -1) { + return PAM_SYSTEM_ERR; + } else if (audit_fd == -2) { + return retval; + } + + rc = _pam_audit_writelog(pamh, audit_fd, type, message, NULL, retval); + + audit_close(audit_fd); + + return rc < 0 ? PAM_SYSTEM_ERR : PAM_SUCCESS; +} + +#else +int pam_modutil_audit_write(pam_handle_t *pamh UNUSED, int type UNUSED, + const char *message UNUSED, int retval UNUSED) +{ + return PAM_SUCCESS; +} +#endif /* HAVE_LIBAUDIT */ diff --git a/libpam/pam_auth.c b/libpam/pam_auth.c new file mode 100644 index 0000000..fdaf95d --- /dev/null +++ b/libpam/pam_auth.c @@ -0,0 +1,73 @@ +/* + * pam_auth.c -- PAM authentication + * + * $Id$ + * + */ + +#include "pam_private.h" +#include "pam_prelude.h" + +#include +#include + +int pam_authenticate(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("called.")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + if (pamh->former.choice == PAM_NOT_STACKED) { + _pam_sanitize(pamh); + _pam_start_timer(pamh); /* we try to make the time for a failure + independent of the time it takes to + fail */ + } + + retval = _pam_dispatch(pamh, flags, PAM_AUTHENTICATE); + + if (retval != PAM_INCOMPLETE) { + _pam_sanitize(pamh); + _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ + D(("exiting")); + } else { + D(("will resume when ready")); + } + +#ifdef PRELUDE + prelude_send_alert(pamh, retval); +#endif + + return retval; +} + +int pam_setcred(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("called.")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + if (! flags) { + flags = PAM_ESTABLISH_CRED; + } + + retval = _pam_dispatch(pamh, flags, PAM_SETCRED); + + D(("exiting")); + + return retval; +} diff --git a/libpam/pam_data.c b/libpam/pam_data.c new file mode 100644 index 0000000..2ad2a16 --- /dev/null +++ b/libpam/pam_data.c @@ -0,0 +1,164 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pam_private.h" + +#include +#include + +static struct pam_data *_pam_locate_data(const pam_handle_t *pamh, + const char *name) +{ + struct pam_data *data; + + D(("called")); + + IF_NO_PAMH(pamh, NULL); + + data = pamh->data; + + while (data) { + if (!strcmp(data->name, name)) { + return data; + } + data = data->next; + } + + return NULL; +} + +int pam_set_data( + pam_handle_t *pamh, + const char *module_data_name, + void *data, + void (*cleanup)(pam_handle_t *pamh, void *data, int error_status)) +{ + struct pam_data *data_entry; + + D(("called")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_APP(pamh)) { + D(("called from application!?")); + return PAM_SYSTEM_ERR; + } + + /* module_data_name should not be NULL */ + if (module_data_name == NULL) { + D(("called with NULL as module_data_name")); + return PAM_SYSTEM_ERR; + } + + /* first check if there is some data already. If so clean it up */ + + if ((data_entry = _pam_locate_data(pamh, module_data_name))) { + if (data_entry->cleanup) { + data_entry->cleanup(pamh, data_entry->data, + PAM_DATA_REPLACE | PAM_SUCCESS ); + } + } else if ((data_entry = malloc(sizeof(*data_entry)))) { + char *tname; + + if ((tname = _pam_strdup(module_data_name)) == NULL) { + pam_syslog(pamh, LOG_CRIT, + "pam_set_data: no memory for data name"); + _pam_drop(data_entry); + return PAM_BUF_ERR; + } + data_entry->next = pamh->data; + pamh->data = data_entry; + data_entry->name = tname; + } else { + pam_syslog(pamh, LOG_CRIT, + "pam_set_data: cannot allocate data entry"); + return PAM_BUF_ERR; + } + + data_entry->data = data; /* note this could be NULL */ + data_entry->cleanup = cleanup; + + return PAM_SUCCESS; +} + +int pam_get_data( + const pam_handle_t *pamh, + const char *module_data_name, + const void **datap) +{ + struct pam_data *data; + + D(("called")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_APP(pamh)) { + D(("called from application!?")); + return PAM_SYSTEM_ERR; + } + + /* module_data_name should not be NULL */ + if (module_data_name == NULL) { + D(("called with NULL as module_data_name")); + return PAM_SYSTEM_ERR; + } + + data = _pam_locate_data(pamh, module_data_name); + if (data) { + *datap = data->data; + return PAM_SUCCESS; + } + + return PAM_NO_MODULE_DATA; +} + +void _pam_free_data(pam_handle_t *pamh, int status) +{ + struct pam_data *last; + struct pam_data *data; + + D(("called")); + + IF_NO_PAMH(pamh, /* no return value for void fn */); + data = pamh->data; + + while (data) { + last = data; + data = data->next; + if (last->cleanup) { + last->cleanup(pamh, last->data, status); + } + _pam_drop(last->name); + _pam_drop(last); + } +} diff --git a/libpam/pam_delay.c b/libpam/pam_delay.c new file mode 100644 index 0000000..9c6716a --- /dev/null +++ b/libpam/pam_delay.c @@ -0,0 +1,179 @@ +/* + * pam_delay.c + * + * Copyright (c) Andrew G. Morgan 1996-9 + * All rights reserved. + * + * $Id$ + * + */ + +/* + * This is a simple implementation of a delay on failure mechanism; an + * attempt to overcome authentication-time attacks in a simple manner. + */ + +#include "pam_private.h" +#include +#include +#include + +#ifdef HAVE_SYS_RANDOM_H +#include +#endif + +/* ********************************************************************** + * initialize the time as unset, this is set on the return from the + * authenticating pair of the libpam pam_XXX calls. + */ + +void _pam_reset_timer(pam_handle_t *pamh) +{ + D(("setting pamh->fail_delay.set to FALSE")); + pamh->fail_delay.set = PAM_FALSE; +} + +/* ********************************************************************** + * this function sets the start time for possible delayed failing. + * + * Eventually, it may set the timer so libpam knows how long the program + * has already been executing. Currently, this value is used to seed + * a pseudo-random number generator... + */ + +void _pam_start_timer(pam_handle_t *pamh) +{ + pamh->fail_delay.begin = time(NULL); + D(("starting timer...")); +} + +/* ******************************************************************* + * Compute a pseudo random time. The value is base*(1 +/- 1/5) where + * the distribution is pseudo gaussian (the sum of three evenly + * distributed random numbers -- central limit theorem and all ;^) The + * linear random numbers are based on a formulae given in Knuth's + * Seminumerical recipes that was reproduced in `Numerical Recipes + * in C'. It is *not* a cryptographically strong generator, but it is + * probably "good enough" for our purposes here. + * + * If getrandom is available, retrieve random number from there. + */ + +static unsigned int _pam_rand(unsigned int seed) +{ +#ifdef HAVE_GETRANDOM + unsigned int value; + + if (getrandom(&value, sizeof(value), GRND_NONBLOCK) == + (ssize_t) sizeof(value)) { + return value; + } +#endif + +#define N1 1664525 +#define N2 1013904223 + return N1*seed + N2; +} + +static unsigned long long _pam_compute_delay(unsigned int seed, + unsigned int base) +{ + int i; + double sum; + unsigned long long ans; + + for (sum=i=0; i<3; ++i) { + seed = _pam_rand(seed); + sum += (double) ((seed / 10) % 1000000); + } + sum = (sum/3.)/1e6 - .5; /* rescale */ + sum = base*(1.+sum); + ans = sum > (double) ULLONG_MAX ? ULLONG_MAX : (unsigned long long) sum; + D(("random number: base=%u -> ans=%llu\n", base, ans)); + + return ans; +} + +/* ********************************************************************** + * By default, the following function sleeps for a random time. The + * actual time slept is computed above. It is based on the requested + * time but will differ by up to +/- 50%. If the PAM_FAIL_DELAY item is + * set by the client, this function will call the function referenced by + * that item, overriding the default behavior. + */ + +void _pam_await_timer(pam_handle_t *pamh, int status) +{ + unsigned long long delay; + D(("waiting?...")); + + delay = _pam_compute_delay(pamh->fail_delay.begin, + pamh->fail_delay.delay); + if (pamh->fail_delay.delay_fn_ptr) { + union { + const void *value; + void (*fn)(int, unsigned, void *); + } hack_fn_u; + void *appdata_ptr; + unsigned int delay_uint; + + if (pamh->pam_conversation) { + appdata_ptr = pamh->pam_conversation->appdata_ptr; + } else { + appdata_ptr = NULL; + } + + delay_uint = delay > UINT_MAX ? UINT_MAX : (unsigned int) delay; + + /* always call the application's delay function, even if + the delay is zero - indicate status */ + hack_fn_u.value = pamh->fail_delay.delay_fn_ptr; + hack_fn_u.fn(status, delay_uint, appdata_ptr); + + } else if (status != PAM_SUCCESS && pamh->fail_delay.set) { + + D(("will wait %llu usec", delay)); + + if (delay > 0) { + struct timeval tval; + + tval.tv_sec = delay / 1000000; + tval.tv_usec = delay % 1000000; + select(0, NULL, NULL, NULL, &tval); + } + } + + _pam_reset_timer(pamh); + D(("waiting done")); +} + +/* ********************************************************************** + * this function is known to both the module and the application, it + * keeps a running score of the largest-requested delay so far, as + * specified by either modules or an application. + */ + +int pam_fail_delay(pam_handle_t *pamh, unsigned int usec) +{ + unsigned int largest; + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + D(("setting delay to %u",usec)); + + if (pamh->fail_delay.set) { + largest = pamh->fail_delay.delay; + } else { + pamh->fail_delay.set = PAM_TRUE; + largest = 0; + } + + D(("largest = %u",largest)); + + if (largest < usec) { + D(("resetting largest delay")); + pamh->fail_delay.delay = usec; + } + + return PAM_SUCCESS; +} diff --git a/libpam/pam_dispatch.c b/libpam/pam_dispatch.c new file mode 100644 index 0000000..3a1d59e --- /dev/null +++ b/libpam/pam_dispatch.c @@ -0,0 +1,448 @@ +/* pam_dispatch.c - handles module function dispatch */ + +/* + * Copyright (c) 1998, 2005 Andrew G. Morgan + * + */ + +#include "pam_private.h" + +#include +#include + +/* + * this is the return code we return when a function pointer is NULL + * or, the handler structure indicates a broken module config line + */ +#define PAM_MUST_FAIL_CODE PAM_PERM_DENIED + +/* impression codes - this gives some sense to the logical choices */ +#define _PAM_UNDEF 0 +#define _PAM_POSITIVE (+1) +#define _PAM_NEGATIVE (-1) + +/* frozen chain required codes */ +#define _PAM_PLEASE_FREEZE 0 +#define _PAM_MAY_BE_FROZEN 1 +#define _PAM_MUST_BE_FROZEN 2 + +/* + * walk a stack of modules. Interpret the administrator's instructions + * when combining the return codes of each module. + */ + +static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h, + _pam_boolean resumed, int use_cached_chain) +{ + int depth, impression, status, skip_depth, prev_level, stack_level; + struct _pam_substack_state *substates = NULL; + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (h == NULL) { + const void *service=NULL; + + (void) pam_get_item(pamh, PAM_SERVICE, &service); + pam_syslog(pamh, LOG_ERR, "no modules loaded for `%s' service", + service ? (const char *)service:"" ); + service = NULL; + return PAM_MUST_FAIL_CODE; + } + + /* if we are recalling this module stack because a former call did + not complete, we restore the state of play from pamh. */ + if (resumed) { + skip_depth = pamh->former.depth; + status = pamh->former.status; + impression = pamh->former.impression; + substates = pamh->former.substates; + /* forget all that */ + pamh->former.impression = _PAM_UNDEF; + pamh->former.status = PAM_MUST_FAIL_CODE; + pamh->former.depth = 0; + pamh->former.substates = NULL; + } else { + skip_depth = 0; + substates = malloc(PAM_SUBSTACK_MAX_LEVEL * sizeof(*substates)); + if (substates == NULL) { + pam_syslog(pamh, LOG_CRIT, + "_pam_dispatch_aux: no memory for substack states"); + return PAM_BUF_ERR; + } + substates[0].impression = impression = _PAM_UNDEF; + substates[0].status = status = PAM_MUST_FAIL_CODE; + } + + prev_level = 0; + + /* Loop through module logic stack */ + for (depth=0 ; h != NULL ; prev_level = stack_level, h = h->next, ++depth) { + int retval, cached_retval, action; + + stack_level = h->stack_level; + + /* skip leading modules if they have already returned */ + if (depth < skip_depth) { + continue; + } + + /* remember state if we are entering a substack */ + if (prev_level < stack_level) { + substates[stack_level].impression = impression; + substates[stack_level].status = status; + } + + /* attempt to call the module */ + if (h->handler_type == PAM_HT_MUST_FAIL) { + D(("module poorly listed in PAM config; forcing failure")); + retval = PAM_MUST_FAIL_CODE; + } else if (h->handler_type == PAM_HT_SUBSTACK) { + D(("skipping substack handler")); + continue; + } else if (h->func == NULL) { + D(("module function is not defined, indicating failure")); + retval = PAM_MODULE_UNKNOWN; + } else { + D(("passing control to module...")); + pamh->mod_name=h->mod_name; + pamh->mod_argc = h->argc; + pamh->mod_argv = h->argv; + retval = h->func(pamh, flags, h->argc, h->argv); + pamh->mod_name=NULL; + pamh->mod_argc = 0; + pamh->mod_argv = NULL; + D(("module returned: %s", pam_strerror(pamh, retval))); + } + + /* + * PAM_INCOMPLETE return is special. It indicates that the + * module wants to wait for the application before continuing. + * In order to return this, the module will have saved its + * state so it can resume from an equivalent position when it + * is called next time. (This was added as of 0.65) + */ + if (retval == PAM_INCOMPLETE) { + pamh->former.impression = impression; + pamh->former.status = status; + pamh->former.depth = depth; + pamh->former.substates = substates; + + D(("module %d returned PAM_INCOMPLETE", depth)); + return retval; + } + + /* + * use_cached_chain is how we ensure that the setcred and + * close_session modules are called in the same order as they did + * when they were invoked as auth/open_session. This feature was + * added in 0.75 to make the behavior of pam_setcred sane. + */ + if (use_cached_chain != _PAM_PLEASE_FREEZE) { + + /* a former stack execution should have frozen the chain */ + + cached_retval = *(h->cached_retval_p); + if (cached_retval == _PAM_INVALID_RETVAL) { + + /* This may be a problem condition. It implies that + the application is running setcred, close_session, + chauthtok(2nd) without having first run + authenticate, open_session, chauthtok(1st) + [respectively]. */ + + D(("use_cached_chain is set to [%d]," + " but cached_retval == _PAM_INVALID_RETVAL", + use_cached_chain)); + + /* In the case of close_session and setcred there is a + backward compatibility reason for allowing this, in + the chauthtok case we have encountered a bug in + libpam! */ + + if (use_cached_chain == _PAM_MAY_BE_FROZEN) { + /* (not ideal) force non-frozen stack control. */ + cached_retval = retval; + } else { + D(("BUG in libpam -" + " chain is required to be frozen but isn't")); + + /* cached_retval is already _PAM_INVALID_RETVAL */ + } + } + } else { + /* this stack execution is defining the frozen chain */ + cached_retval = h->cached_retval = retval; + } + + /* verify that the return value is a valid one */ + if ((cached_retval < PAM_SUCCESS) + || (cached_retval >= _PAM_RETURN_VALUES)) { + + retval = PAM_MUST_FAIL_CODE; + action = _PAM_ACTION_BAD; + } else { + /* We treat the current retval with some respect. It may + (for example, in the case of setcred) have a value that + needs to be propagated to the user. We want to use the + cached_retval to determine the modules to be executed + in the stacked chain, but we want to treat each + non-ignored module in the cached chain as now being + 'required'. We only need to treat the, + _PAM_ACTION_IGNORE, _PAM_ACTION_IS_JUMP and + _PAM_ACTION_RESET actions specially. */ + + action = h->actions[cached_retval]; + } + + D(("use_cached_chain=%d action=%d cached_retval=%d retval=%d", + use_cached_chain, action, cached_retval, retval)); + + /* decide what to do */ + switch (action) { + case _PAM_ACTION_RESET: + + impression = substates[stack_level].impression; + status = substates[stack_level].status; + break; + + case _PAM_ACTION_OK: + case _PAM_ACTION_DONE: + + if ( impression == _PAM_UNDEF + || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) { + /* in case of using cached chain + we could get here with PAM_IGNORE - don't return it */ + if ( retval != PAM_IGNORE || cached_retval == retval ) { + impression = _PAM_POSITIVE; + status = retval; + } + } + if ( impression == _PAM_POSITIVE ) { + if ( retval == PAM_SUCCESS ) { + h->grantor = 1; + } + + if ( action == _PAM_ACTION_DONE ) { + goto decision_made; + } + } + break; + + case _PAM_ACTION_BAD: + case _PAM_ACTION_DIE: +#ifdef PAM_FAIL_NOW_ON + if ( cached_retval == PAM_ABORT ) { + impression = _PAM_NEGATIVE; + status = PAM_PERM_DENIED; + goto decision_made; + } +#endif /* PAM_FAIL_NOW_ON */ + if ( impression != _PAM_NEGATIVE ) { + impression = _PAM_NEGATIVE; + /* Don't return with PAM_IGNORE as status */ + if ( retval == PAM_IGNORE ) + status = PAM_MUST_FAIL_CODE; + else + status = retval; + } + if ( action == _PAM_ACTION_DIE ) { + goto decision_made; + } + break; + + case _PAM_ACTION_IGNORE: + break; + + /* if we get here, we expect action is a positive number -- + this is what the ...JUMP macro checks. */ + + default: + if ( _PAM_ACTION_IS_JUMP(action) ) { + + /* If we are evaluating a cached chain, we treat this + module as required (aka _PAM_ACTION_OK) as well as + executing the jump. */ + + if (use_cached_chain) { + if (impression == _PAM_UNDEF + || (impression == _PAM_POSITIVE + && status == PAM_SUCCESS) ) { + if ( retval != PAM_IGNORE || cached_retval == retval ) { + if ( impression == _PAM_UNDEF && retval == PAM_SUCCESS ) { + h->grantor = 1; + } + impression = _PAM_POSITIVE; + status = retval; + } + } + } + + /* this means that we need to skip #action stacked modules */ + while (h->next != NULL && h->next->stack_level >= stack_level && action > 0) { + do { + h = h->next; + ++depth; + } while (h->next != NULL && h->next->stack_level > stack_level); + --action; + } + + /* note if we try to skip too many modules action is + still non-zero and we snag the next if. */ + } + + /* this case is a syntax error: we can't succeed */ + if (action) { + pam_syslog(pamh, LOG_ERR, "bad jump in stack"); + impression = _PAM_NEGATIVE; + status = PAM_MUST_FAIL_CODE; + } + } + continue; + +decision_made: /* by getting here we have made a decision */ + while (h->next != NULL && h->next->stack_level >= stack_level) { + h = h->next; + ++depth; + } + } + + /* Sanity check */ + if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) { + D(("caught on sanity check -- this is probably a config error!")); + status = PAM_MUST_FAIL_CODE; + } + + free(substates); + /* We have made a decision about the modules executed */ + return status; +} + +static void _pam_clear_grantors(struct handler *h) +{ + for (; h != NULL; h = h->next) { + h->grantor = 0; + } +} + +/* + * This function translates the module dispatch request into a pointer + * to the stack of modules that will actually be run. the + * _pam_dispatch_aux() function (above) is responsible for walking the + * module stack. + */ + +int _pam_dispatch(pam_handle_t *pamh, int flags, int choice) +{ + struct handler *h = NULL; + int retval = PAM_SYSTEM_ERR, use_cached_chain; + _pam_boolean resumed; + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from a module!?")); + goto end; + } + + /* Load all modules, resolve all symbols */ + + if ((retval = _pam_init_handlers(pamh)) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "unable to dispatch function"); + goto end; + } + + use_cached_chain = _PAM_PLEASE_FREEZE; + + switch (choice) { + case PAM_AUTHENTICATE: + h = pamh->handlers.conf.authenticate; + break; + case PAM_SETCRED: + h = pamh->handlers.conf.setcred; + use_cached_chain = _PAM_MAY_BE_FROZEN; + break; + case PAM_ACCOUNT: + h = pamh->handlers.conf.acct_mgmt; + break; + case PAM_OPEN_SESSION: + h = pamh->handlers.conf.open_session; + break; + case PAM_CLOSE_SESSION: + h = pamh->handlers.conf.close_session; + use_cached_chain = _PAM_MAY_BE_FROZEN; + break; + case PAM_CHAUTHTOK: + h = pamh->handlers.conf.chauthtok; + break; + default: + pam_syslog(pamh, LOG_ERR, "undefined fn choice; %d", choice); + retval = PAM_ABORT; + goto end; + } + + if (h == NULL) { /* there was no handlers.conf... entry; will use + * handlers.other... */ + switch (choice) { + case PAM_AUTHENTICATE: + h = pamh->handlers.other.authenticate; + break; + case PAM_SETCRED: + h = pamh->handlers.other.setcred; + break; + case PAM_ACCOUNT: + h = pamh->handlers.other.acct_mgmt; + break; + case PAM_OPEN_SESSION: + h = pamh->handlers.other.open_session; + break; + case PAM_CLOSE_SESSION: + h = pamh->handlers.other.close_session; + break; + case PAM_CHAUTHTOK: + h = pamh->handlers.other.chauthtok; + break; + } + } + + /* Did a module return an "incomplete state" last time? */ + if (pamh->former.choice != PAM_NOT_STACKED) { + if (pamh->former.choice != choice) { + pam_syslog(pamh, LOG_ERR, + "application failed to re-exec stack [%d:%d]", + pamh->former.choice, choice); + retval = PAM_ABORT; + goto end; + } + resumed = PAM_TRUE; + } else { + resumed = PAM_FALSE; + _pam_clear_grantors(h); + } + + __PAM_TO_MODULE(pamh); + + /* call the list of module functions */ + pamh->choice = choice; + retval = _pam_dispatch_aux(pamh, flags, h, resumed, use_cached_chain); + + __PAM_TO_APP(pamh); + + /* Should we recall where to resume next time? */ + if (retval == PAM_INCOMPLETE) { + D(("module [%s] returned PAM_INCOMPLETE", + pamh->mod_name ? pamh->mod_name : "(NULL)")); + pamh->former.choice = choice; + } else { + pamh->former.choice = PAM_NOT_STACKED; + } + +end: + +#ifdef HAVE_LIBAUDIT + if (choice != PAM_CHAUTHTOK || flags & PAM_UPDATE_AUTHTOK || retval != PAM_SUCCESS) { + retval = _pam_auditlog(pamh, choice, retval, flags, h); + } +#endif + + return retval; +} diff --git a/libpam/pam_dynamic.c b/libpam/pam_dynamic.c new file mode 100644 index 0000000..81f49b4 --- /dev/null +++ b/libpam/pam_dynamic.c @@ -0,0 +1,56 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pam_private.h" +#include + +void *_pam_dlopen(const char *mod_path) +{ + return dlopen(mod_path, RTLD_NOW); +} + +servicefn _pam_dlsym(void *handle, const char *symbol) +{ + return (servicefn) dlsym(handle, symbol); +} + +void _pam_dlclose(void *handle) +{ + dlclose(handle); +} + +const char * +_pam_dlerror (void) +{ + return dlerror (); +} diff --git a/libpam/pam_end.c b/libpam/pam_end.c new file mode 100644 index 0000000..c728f1d --- /dev/null +++ b/libpam/pam_end.c @@ -0,0 +1,99 @@ +/* pam_end.c */ + +/* + * $Id$ + */ + +#include "pam_private.h" +#include "pam_inline.h" + +#include + +int pam_end(pam_handle_t *pamh, int pam_status) +{ + int ret; + + D(("called.")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + +#ifdef HAVE_LIBAUDIT + _pam_audit_end(pamh, pam_status); +#endif + + /* first liberate the modules (it is not inconceivable that the + modules may need to use the service_name etc. to clean up) */ + + _pam_free_data(pamh, pam_status); + + /* now drop all modules */ + + if ((ret = _pam_free_handlers(pamh)) != PAM_SUCCESS) { + return ret; /* error occurred */ + } + + /* from this point we cannot call the modules any more. Free the remaining + memory used by the Linux-PAM interface */ + + _pam_drop_env(pamh); /* purge the environment */ + + pam_overwrite_string(pamh->authtok); /* blank out old token */ + _pam_drop(pamh->authtok); + + pam_overwrite_string(pamh->oldauthtok); /* blank out old token */ + _pam_drop(pamh->oldauthtok); + + pam_overwrite_string(pamh->former.prompt); + _pam_drop(pamh->former.prompt); /* drop saved prompt */ + + pam_overwrite_string(pamh->service_name); + _pam_drop(pamh->service_name); + + pam_overwrite_string(pamh->user); + _pam_drop(pamh->user); + + pam_overwrite_string(pamh->confdir); + _pam_drop(pamh->confdir); + + pam_overwrite_string(pamh->prompt); + _pam_drop(pamh->prompt); /* prompt for pam_get_user() */ + + pam_overwrite_string(pamh->tty); + _pam_drop(pamh->tty); + + pam_overwrite_string(pamh->rhost); + _pam_drop(pamh->rhost); + + pam_overwrite_string(pamh->ruser); + _pam_drop(pamh->ruser); + + _pam_drop(pamh->pam_conversation); + pamh->fail_delay.delay_fn_ptr = NULL; + + _pam_drop(pamh->former.substates); + + pam_overwrite_string(pamh->xdisplay); + _pam_drop(pamh->xdisplay); + + pam_overwrite_string(pamh->xauth.name); + _pam_drop(pamh->xauth.name); + pam_overwrite_n(pamh->xauth.data, (unsigned int)pamh->xauth.datalen); + _pam_drop(pamh->xauth.data); + pam_overwrite_object(&pamh->xauth); + + pam_overwrite_string(pamh->authtok_type); + _pam_drop(pamh->authtok_type); + + /* and finally liberate the memory for the pam_handle structure */ + + _pam_drop(pamh); + + D(("exiting successfully")); + + return PAM_SUCCESS; +} diff --git a/libpam/pam_env.c b/libpam/pam_env.c new file mode 100644 index 0000000..2b3e395 --- /dev/null +++ b/libpam/pam_env.c @@ -0,0 +1,400 @@ +/* + * pam_env.c + * + * Copyright (c) Andrew G. Morgan 1996,1997 + * All rights reserved. + * + * This file was written from a "hint" provided by the people at SUN. + * and the X/Open XSSO draft of March 1997. + * + * $Id$ + */ + +#include "pam_private.h" +#include "pam_inline.h" + +#include +#include +#include + +#ifdef sunos +#define memmove(x,y,z) bcopy(y,x,z) +#endif + +/* helper functions */ + +#ifdef PAM_DEBUG +static void _pam_dump_env(pam_handle_t *pamh) +{ + int i; + + D(("Listing environment of pamh=%p", pamh)); + D(("pamh->env = %p", pamh->env)); + D(("environment entries used = %d [of %d allocated]" + , pamh->env->requested, pamh->env->entries)); + + for (i=0; ienv->requested; ++i) { + _pam_output_debug( +#if UINTPTR_MAX == UINT32_MAX + ">%-3d [%10p]:[%s]" +#else + ">%-3d [%18p]:[%s]" +#endif + , i, pamh->env->list[i], pamh->env->list[i]); + } + _pam_output_debug("*NOTE* the last item should be (nil)"); +} +#else +#define _pam_dump_env(x) +#endif + +/* + * Create the environment + */ + +int _pam_make_env(pam_handle_t *pamh) +{ + D(("called.")); + + IF_NO_PAMH(pamh, PAM_ABORT); + + /* + * get structure memory + */ + + pamh->env = malloc(sizeof(struct pam_environ)); + if (pamh->env == NULL) { + pam_syslog(pamh, LOG_CRIT, "_pam_make_env: out of memory"); + return PAM_BUF_ERR; + } + + /* + * get list memory + */ + + pamh->env->list = calloc( PAM_ENV_CHUNK, sizeof(char *) ); + if (pamh->env->list == NULL) { + pam_syslog(pamh, LOG_CRIT, "_pam_make_env: no memory for list"); + _pam_drop(pamh->env); + return PAM_BUF_ERR; + } + + /* + * fill entries in pamh->env + */ + + pamh->env->entries = PAM_ENV_CHUNK; + pamh->env->requested = 1; + pamh->env->list[0] = NULL; + + _pam_dump_env(pamh); /* only active when debugging */ + + return PAM_SUCCESS; +} + +/* + * purge the environment + */ + +void _pam_drop_env(pam_handle_t *pamh) +{ + D(("called.")); + IF_NO_PAMH(pamh, /* nothing to return */); + + if (pamh->env != NULL) { + int i; + /* we will only purge the pamh->env->requested number of elements */ + + for (i=pamh->env->requested-1; i-- > 0; ) { + D(("dropping #%3d>%s<", i, pamh->env->list[i])); + pam_overwrite_string(pamh->env->list[i]); /* clean */ + _pam_drop(pamh->env->list[i]); /* forget */ + } + pamh->env->requested = 0; + pamh->env->entries = 0; + _pam_drop(pamh->env->list); /* forget */ + _pam_drop(pamh->env); /* forget */ + } else { + D(("no environment present in pamh?")); + } +} + +/* + * Return the item number of the given variable = first 'length' chars + * of 'name_value'. Since this is a static function, it is safe to + * assume its supplied arguments are well defined. + */ + +static int _pam_search_env(const struct pam_environ *env + , const char *name_value, size_t length) +{ + int i; + + for (i=env->requested-1; i-- > 0; ) { + if (strncmp(name_value,env->list[i],length) == 0 + && env->list[i][length] == '=') { + + return i; /* Got it! */ + + } + } + + return -1; /* no luck */ +} + +/* + * externally visible functions + */ + +/* + * pam_putenv(): Add/replace/delete a PAM-environment variable. + * + * Add/replace: + * name_value = "NAME=VALUE" or "NAME=" (for empty value="\0") + * + * delete: + * name_value = "NAME" + */ + +int pam_putenv(pam_handle_t *pamh, const char *name_value) +{ + size_t l2eq; + int item, retval; + + D(("called.")); + IF_NO_PAMH(pamh, PAM_ABORT); + + if (name_value == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_putenv: no variable indicated"); + return PAM_PERM_DENIED; + } + + /* + * establish if we are setting or deleting; scan for '=' + */ + + for (l2eq=0; name_value[l2eq] && name_value[l2eq] != '='; ++l2eq); + if (l2eq == 0) { + pam_syslog(pamh, LOG_ERR, "pam_putenv: bad variable"); + return PAM_BAD_ITEM; + } + + /* + * Look first for environment. + */ + + if (pamh->env == NULL || pamh->env->list == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_putenv: no env%s found", + pamh->env == NULL ? "":"-list"); + return PAM_ABORT; + } + + /* find the item to replace */ + + item = _pam_search_env(pamh->env, name_value, l2eq); + + if (name_value[l2eq]) { /* (re)setting */ + + if (item == -1) { /* new variable */ + D(("adding item: %s", name_value)); + /* enough space? */ + if (pamh->env->entries <= pamh->env->requested) { + register int i; + register char **tmp; + + /* get some new space */ + tmp = calloc( pamh->env->entries + PAM_ENV_CHUNK + , sizeof(char *) ); + if (tmp == NULL) { + /* nothing has changed - old env intact */ + pam_syslog(pamh, LOG_CRIT, + "pam_putenv: cannot grow environment"); + return PAM_BUF_ERR; + } + + /* copy old env-item pointers/forget old */ + for (i=0; ienv->requested; ++i) { + tmp[i] = pamh->env->list[i]; + pamh->env->list[i] = NULL; + } + + /* drop old list and replace with new */ + _pam_drop(pamh->env->list); + pamh->env->list = tmp; + pamh->env->entries += PAM_ENV_CHUNK; + + D(("resized env list")); + _pam_dump_env(pamh); /* only when debugging */ + } + + item = pamh->env->requested-1; /* old last item (NULL) */ + + /* add a new NULL entry at end; increase counter */ + pamh->env->list[pamh->env->requested++] = NULL; + + } else { /* replace old */ + D(("replacing item: %s\n with: %s" + , pamh->env->list[item], name_value)); + pam_overwrite_string(pamh->env->list[item]); + _pam_drop(pamh->env->list[item]); + } + + /* + * now we have a place to put the new env-item, insert at 'item' + */ + + pamh->env->list[item] = _pam_strdup(name_value); + if (pamh->env->list[item] != NULL) { + _pam_dump_env(pamh); /* only when debugging */ + return PAM_SUCCESS; + } + + /* something went wrong; we should delete the item - fall through */ + + retval = PAM_BUF_ERR; /* an error occurred */ + } else { + retval = PAM_SUCCESS; /* we requested delete */ + } + + /* getting to here implies we are deleting an item */ + + if (item < 0) { + pam_syslog(pamh, LOG_ERR, + "pam_putenv: delete non-existent entry; %s", name_value); + return PAM_BAD_ITEM; + } + + /* + * remove item: purge memory; reset counter; resize [; display-env] + */ + + D(("deleting: env#%3d:[%s]", item, pamh->env->list[item])); + pam_overwrite_string(pamh->env->list[item]); + _pam_drop(pamh->env->list[item]); + --(pamh->env->requested); + D(("memmove: item[%d]+%d -> item[%d]" + , item+1, ( pamh->env->requested - item ), item)); + (void) memmove(&pamh->env->list[item], &pamh->env->list[item+1] + , ( pamh->env->requested - item )*sizeof(char *) ); + + _pam_dump_env(pamh); /* only when debugging */ + + /* + * deleted. + */ + + return retval; +} + +/* + * Return the value of the requested environment variable + */ + +const char *pam_getenv(pam_handle_t *pamh, const char *name) +{ + int item; + + D(("called.")); + IF_NO_PAMH(pamh, NULL); + + if (name == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_getenv: no variable indicated"); + return NULL; + } + + if (pamh->env == NULL || pamh->env->list == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_getenv: no env%s found", + pamh->env == NULL ? "":"-list" ); + return NULL; + } + + /* find the requested item */ + + item = _pam_search_env(pamh->env, name, strlen(name)); + if (item != -1) { + + D(("env-item: %s, found!", name)); + return (pamh->env->list[item] + 1 + strlen(name)); + + } else { + + D(("env-item: %s, not found", name)); + return NULL; + + } +} + +static char **_copy_env(pam_handle_t *pamh) +{ + char **dump; + int i = pamh->env->requested; /* reckon size of environment */ + char *const *env = pamh->env->list; + + D(("now get some memory for dump")); + + /* allocate some memory for this (plus the null tail-pointer) */ + dump = calloc(i, sizeof(char *)); + D(("dump = %p", dump)); + if (dump == NULL) { + return NULL; + } + + /* now run through entries and copy the variables over */ + dump[--i] = NULL; + while (i-- > 0) { + D(("env[%d]=`%s'", i,env[i])); + dump[i] = _pam_strdup(env[i]); + D(("->dump[%d]=`%s'", i,dump[i])); + if (dump[i] == NULL) { + /* out of memory */ + + while (dump[++i]) { + pam_overwrite_string(dump[i]); + _pam_drop(dump[i]); + } + _pam_drop(dump); + return NULL; + } + } + + env = NULL; /* forget now */ + + /* return transcribed environment */ + return dump; +} + +char **pam_getenvlist(pam_handle_t *pamh) +{ + int i; + + D(("called.")); + IF_NO_PAMH(pamh, NULL); + + if (pamh->env == NULL || pamh->env->list == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_getenvlist: no env%s found", + pamh->env == NULL ? "":"-list" ); + return NULL; + } + + /* some quick checks */ + + if (pamh->env->requested > pamh->env->entries) { + pam_syslog(pamh, LOG_ERR, "pam_getenvlist: environment corruption"); + _pam_dump_env(pamh); /* only active when debugging */ + return NULL; + } + + for (i=pamh->env->requested-1; i-- > 0; ) { + if (pamh->env->list[i] == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_getenvlist: environment broken"); + _pam_dump_env(pamh); /* only active when debugging */ + return NULL; /* somehow we've broken the environment!? */ + } + } + + /* Seems fine; copy environment */ + + _pam_dump_env(pamh); /* only active when debugging */ + + return _copy_env(pamh); +} diff --git a/libpam/pam_get_authtok.c b/libpam/pam_get_authtok.c new file mode 100644 index 0000000..2e7a599 --- /dev/null +++ b/libpam/pam_get_authtok.c @@ -0,0 +1,289 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pam_private.h" +#include "pam_inline.h" +#include "pam_i18n.h" + +#include + +#define PROMPT _("Password: ") +/* For Translators: "%s" is replaced with "". */ +#define PROMPT_CURRENT_ARG _("Current %s password: ") +#define PROMPT_CURRENT_NOARG _("Current password: ") +/* For Translators: "%s" is replaced with "". */ +#define PROMPT_NEW_ARG _("New %s password: ") +#define PROMPT_NEW_NOARG _("New password: ") +/* For Translators: "%s" is replaced with "". */ +#define PROMPT_RETYPE_ARG _("Retype new %s password: ") +#define PROMPT_RETYPE_NOARG _("Retype new password: ") +#define MISTYPED_PASS _("Sorry, passwords do not match.") + +#define PAM_GETAUTHTOK_NOVERIFY 1 + +static const char * +get_option (pam_handle_t *pamh, const char *option) +{ + int i; + size_t len; + + + if (option == NULL || pamh == NULL || + pamh->mod_argc == 0 || pamh->mod_argv == NULL) + return NULL; + + len = strlen (option); + + for (i = 0; i < pamh->mod_argc; i++) + { + if (strncmp (option, pamh->mod_argv[i], len) == 0) + { + if (pamh->mod_argv[i][len] == '=') + return &(pamh->mod_argv[i][len+1]); + else if (pamh->mod_argv[i][len] == '\0') + return ""; + } + } + return NULL; +} + + +static int +pam_get_authtok_internal (pam_handle_t *pamh, int item, + const char **authtok, const char *prompt, + unsigned int flags) + +{ + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + char *resp[2] = {NULL, NULL}; + const void *prevauthtok; + const char *authtok_type = ""; + int chpass = 0; /* Password change, ask twice for it */ + int retval; + + if (authtok == NULL) + return PAM_SYSTEM_ERR; + + /* PAM_AUTHTOK in password stack returns new password, + which needs to be verified. */ + if (pamh->choice == PAM_CHAUTHTOK) + { + if (item == PAM_AUTHTOK) + { + chpass = 1; + if (!(flags & PAM_GETAUTHTOK_NOVERIFY)) + ++chpass; + } + authtok_type = get_option (pamh, "authtok_type"); + if (authtok_type == NULL) + { + retval = pam_get_item (pamh, PAM_AUTHTOK_TYPE, (const void **)&authtok_type); + if (retval != PAM_SUCCESS || authtok_type == NULL) + authtok_type = ""; + } + else + pam_set_item(pamh, PAM_AUTHTOK_TYPE, authtok_type); + } + + retval = pam_get_item (pamh, item, &prevauthtok); + if (retval == PAM_SUCCESS && prevauthtok != NULL) + { + *authtok = prevauthtok; + return PAM_SUCCESS; + } + else if (get_option (pamh, "use_first_pass") || + (chpass && get_option (pamh, "use_authtok"))) + { + if (prevauthtok == NULL) + { + if (chpass) + return PAM_AUTHTOK_ERR; + else + return PAM_AUTH_ERR; + } + else + return retval; + } + + if (prompt != NULL) + { + retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0], + "%s", prompt); + if (retval == PAM_SUCCESS && chpass > 1 && resp[0] != NULL) + retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[1], + _("Retype %s"), prompt); + } + else if (chpass) + { + pamh->authtok_verified = 0; + + retval = *authtok_type ? + pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0], + PROMPT_NEW_ARG, authtok_type) : + pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0], + "%s", PROMPT_NEW_NOARG); + if (retval == PAM_SUCCESS && chpass > 1 && resp[0] != NULL) + { + retval = *authtok_type ? + pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[1], + PROMPT_RETYPE_ARG, authtok_type) : + pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[1], + "%s", PROMPT_RETYPE_NOARG); + } + } + else if (item == PAM_OLDAUTHTOK) + { + retval = *authtok_type ? + pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0], + PROMPT_CURRENT_ARG, authtok_type) : + pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0], + "%s", PROMPT_CURRENT_NOARG); + } + else + retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0], "%s", PROMPT); + + if (retval != PAM_SUCCESS || resp[0] == NULL || + (chpass > 1 && resp[1] == NULL)) + { + /* We want to abort */ + pam_overwrite_string (resp[0]); + _pam_drop (resp[0]); + pam_overwrite_string (resp[1]); + _pam_drop (resp[1]); + if (chpass) + pam_error (pamh, _("Password change has been aborted.")); + return PAM_AUTHTOK_ERR; + } + + if (chpass > 1 && strcmp (resp[0], resp[1]) != 0) + { + pam_error (pamh, MISTYPED_PASS); + pam_overwrite_string (resp[0]); + _pam_drop (resp[0]); + pam_overwrite_string (resp[1]); + _pam_drop (resp[1]); + return PAM_TRY_AGAIN; + } + + pam_overwrite_string (resp[1]); + _pam_drop (resp[1]); + + retval = pam_set_item (pamh, item, resp[0]); + pam_overwrite_string (resp[0]); + _pam_drop (resp[0]); + if (retval != PAM_SUCCESS) + return retval; + + if (chpass > 1) + pamh->authtok_verified = 1; + + return pam_get_item(pamh, item, (const void **)authtok); +} + +int +pam_get_authtok (pam_handle_t *pamh, int item, const char **authtok, + const char *prompt) +{ + return pam_get_authtok_internal (pamh, item, authtok, prompt, 0); +} + + +int +pam_get_authtok_noverify (pam_handle_t *pamh, const char **authtok, + const char *prompt) +{ + return pam_get_authtok_internal (pamh, PAM_AUTHTOK, authtok, prompt, + PAM_GETAUTHTOK_NOVERIFY); +} + +int +pam_get_authtok_verify (pam_handle_t *pamh, const char **authtok, + const char *prompt) +{ + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + char *resp = NULL; + const char *authtok_type = ""; + int retval; + + if (authtok == NULL || pamh->choice != PAM_CHAUTHTOK) + return PAM_SYSTEM_ERR; + + if (pamh->authtok_verified) + return pam_get_item (pamh, PAM_AUTHTOK, (const void **)authtok); + + if (prompt != NULL) + { + retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp, + _("Retype %s"), prompt); + } + else + { + retval = pam_get_item (pamh, PAM_AUTHTOK_TYPE, (const void **)&authtok_type); + if (retval != PAM_SUCCESS || authtok_type == NULL) + authtok_type = ""; + retval = *authtok_type ? + pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp, + PROMPT_RETYPE_ARG, authtok_type) : + pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp, + "%s", PROMPT_RETYPE_NOARG); + } + + if (retval != PAM_SUCCESS || resp == NULL) + { + /* We want to abort the password change */ + pam_set_item (pamh, PAM_AUTHTOK, NULL); + pam_error (pamh, _("Password change has been aborted.")); + return PAM_AUTHTOK_ERR; + } + + if (strcmp (*authtok, resp) != 0) + { + pam_set_item (pamh, PAM_AUTHTOK, NULL); + pam_error (pamh, MISTYPED_PASS); + pam_overwrite_string (resp); + _pam_drop (resp); + return PAM_TRY_AGAIN; + } + + retval = pam_set_item (pamh, PAM_AUTHTOK, resp); + pam_overwrite_string (resp); + _pam_drop (resp); + if (retval != PAM_SUCCESS) + return retval; + + pamh->authtok_verified = 1; + + return pam_get_item(pamh, PAM_AUTHTOK, (const void **)authtok); +} diff --git a/libpam/pam_handlers.c b/libpam/pam_handlers.c new file mode 100644 index 0000000..7fd6ce8 --- /dev/null +++ b/libpam/pam_handlers.c @@ -0,0 +1,963 @@ +/* pam_handlers.c -- pam config file parsing and module loading */ + +/* + * created by Marc Ewing. + * Currently maintained by Andrew G. Morgan + * + */ + +#include "pam_private.h" +#include "pam_inline.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pam_line.h" + +#define MODULE_CHUNK 4 +#define UNKNOWN_MODULE "<*unknown module*>" +#ifndef _PAM_ISA +#define _PAM_ISA "." +#endif + +static void _pam_free_handlers_aux(struct handler **hp); + +static int _pam_add_handler(pam_handle_t *pamh + , int must_fail, int other, int stack_level, int type + , int *actions, const char *mod_path + , int argc, char **argv, size_t argvlen); + +/* Values for module type */ + +#define PAM_T_ANY 0 +#define PAM_T_AUTH 1 +#define PAM_T_SESS 2 +#define PAM_T_ACCT 4 +#define PAM_T_PASS 8 + +static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name + , const char *service /* specific file */ + , int module_type /* specific type */ + , int include_level /* level of include */ + , int stack_level /* level of substack */ +#ifdef PAM_READ_BOTH_CONFS + , int not_other +#endif /* PAM_READ_BOTH_CONFS */ + ); + +static int _pam_parse_conf_file(pam_handle_t *pamh, FILE *f + , const char *known_service /* specific file */ + , int requested_module_type /* specific type */ + , int include_level /* level of include */ + , int stack_level /* level of substack */ +#ifdef PAM_READ_BOTH_CONFS + , int not_other +#endif /* PAM_READ_BOTH_CONFS */ + ) +{ + struct pam_line_buffer buffer; + int x; /* read a line from the FILE *f ? */ + + _pam_line_buffer_init(&buffer); + /* + * read a line from the configuration (FILE *) f + */ + while ((x = _pam_line_assemble(f, &buffer, ' ')) > 0) { + char *buf = buffer.assembled; + char *tok, *nexttok=NULL; + const char *this_service; + const char *mod_path; + int module_type, actions[_PAM_RETURN_VALUES]; + int other; /* set if module is for PAM_DEFAULT_SERVICE */ + int res; /* module added successfully? */ + int handler_type = PAM_HT_MODULE; /* regular handler from a module */ + int argc; + char **argv; + size_t argvlen; + + D(("LINE: %s", buf)); + if (known_service != NULL) { + nexttok = buf; + /* No service field: all lines are for the known service. */ + this_service = known_service; + } else { + this_service = tok = _pam_tokenize(buf, &nexttok); + } + +#ifdef PAM_READ_BOTH_CONFS + if (not_other) + other = 0; + else +#endif /* PAM_READ_BOTH_CONFS */ + other = !strcasecmp(this_service, PAM_DEFAULT_SERVICE); + + /* accept "service name" or PAM_DEFAULT_SERVICE modules */ + if (!strcasecmp(this_service, pamh->service_name) || other) { + int pam_include = 0; + int substack = 0; + + /* This is a service we are looking for */ + D(("Found PAM config entry for: %s", this_service)); + + tok = _pam_tokenize(NULL, &nexttok); + if (tok == NULL) { + /* module type does not exist */ + D(("empty module type for %s", this_service)); + pam_syslog(pamh, LOG_ERR, + "(%s) empty module type", this_service); + module_type = (requested_module_type != PAM_T_ANY) ? + requested_module_type : PAM_T_AUTH; /* most sensitive */ + handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */ + } else { + if (tok[0] == '-') { /* do not log module load errors */ + handler_type = PAM_HT_SILENT_MODULE; + ++tok; + } + if (!strcasecmp("auth", tok)) { + module_type = PAM_T_AUTH; + } else if (!strcasecmp("session", tok)) { + module_type = PAM_T_SESS; + } else if (!strcasecmp("account", tok)) { + module_type = PAM_T_ACCT; + } else if (!strcasecmp("password", tok)) { + module_type = PAM_T_PASS; + } else { + /* Illegal module type */ + D(("bad module type: %s", tok)); + pam_syslog(pamh, LOG_ERR, "(%s) illegal module type: %s", + this_service, tok); + module_type = (requested_module_type != PAM_T_ANY) ? + requested_module_type : PAM_T_AUTH; /* most sensitive */ + handler_type = PAM_HT_MUST_FAIL; /* install as normal but fail when dispatched */ + } + } + D(("Using %sconfig entry: %s", handler_type?"BAD ":"", tok)); + if (requested_module_type != PAM_T_ANY && + module_type != requested_module_type) { + D(("Skipping config entry: %s (requested=%d, found=%d)", + tok, requested_module_type, module_type)); + continue; + } + + /* reset the actions to .._UNDEF's -- this is so that + we can work out which entries are not yet set (for default). */ + { + int i; + for (i=0; i<_PAM_RETURN_VALUES; + actions[i++] = _PAM_ACTION_UNDEF); + } + tok = _pam_tokenize(NULL, &nexttok); + if (tok == NULL) { + /* no module name given */ + D(("no control flag supplied")); + pam_syslog(pamh, LOG_ERR, + "(%s) no control flag supplied", this_service); + _pam_set_default_control(actions, _PAM_ACTION_BAD); + handler_type = PAM_HT_MUST_FAIL; + } else if (!strcasecmp("required", tok)) { + D(("*PAM_F_REQUIRED*")); + actions[PAM_SUCCESS] = _PAM_ACTION_OK; + actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK; + actions[PAM_IGNORE] = _PAM_ACTION_IGNORE; + _pam_set_default_control(actions, _PAM_ACTION_BAD); + } else if (!strcasecmp("requisite", tok)) { + D(("*PAM_F_REQUISITE*")); + actions[PAM_SUCCESS] = _PAM_ACTION_OK; + actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK; + actions[PAM_IGNORE] = _PAM_ACTION_IGNORE; + _pam_set_default_control(actions, _PAM_ACTION_DIE); + } else if (!strcasecmp("optional", tok)) { + D(("*PAM_F_OPTIONAL*")); + actions[PAM_SUCCESS] = _PAM_ACTION_OK; + actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_OK; + _pam_set_default_control(actions, _PAM_ACTION_IGNORE); + } else if (!strcasecmp("sufficient", tok)) { + D(("*PAM_F_SUFFICIENT*")); + actions[PAM_SUCCESS] = _PAM_ACTION_DONE; + actions[PAM_NEW_AUTHTOK_REQD] = _PAM_ACTION_DONE; + _pam_set_default_control(actions, _PAM_ACTION_IGNORE); + } else if (!strcasecmp("include", tok)) { + D(("*PAM_F_INCLUDE*")); + pam_include = 1; + substack = 0; + } else if (!strcasecmp("substack", tok)) { + D(("*PAM_F_SUBSTACK*")); + pam_include = 1; + substack = 1; + } else { + D(("will need to parse %s", tok)); + _pam_parse_control(actions, tok); + /* by default the default is to treat as failure */ + _pam_set_default_control(actions, _PAM_ACTION_BAD); + } + + tok = _pam_tokenize(NULL, &nexttok); + if (pam_include) { + if (substack) { + res = _pam_add_handler(pamh, PAM_HT_SUBSTACK, other, + stack_level, module_type, actions, tok, + 0, NULL, 0); + if (res != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "error adding substack %s", tok); + D(("failed to load module - aborting")); + return PAM_ABORT; + } + } + if (_pam_load_conf_file(pamh, tok, this_service, module_type, + include_level + 1, stack_level + substack +#ifdef PAM_READ_BOTH_CONFS + , !other +#endif /* PAM_READ_BOTH_CONFS */ + ) == PAM_SUCCESS) + continue; + _pam_set_default_control(actions, _PAM_ACTION_BAD); + mod_path = NULL; + handler_type = PAM_HT_MUST_FAIL; + nexttok = NULL; + } else if (tok != NULL) { + mod_path = tok; + D(("mod_path = %s",mod_path)); + } else { + /* no module name given */ + D(("no module name supplied")); + pam_syslog(pamh, LOG_ERR, + "(%s) no module name supplied", this_service); + mod_path = NULL; + handler_type = PAM_HT_MUST_FAIL; + } + + /* nexttok points to remaining arguments... */ + + if (nexttok != NULL) { + D(("list: %s",nexttok)); + argvlen = _pam_mkargv(nexttok, &argv, &argc); + D(("argvlen = %zu",argvlen)); + if (argvlen == 0) { + /* memory allocation failed */ + D(("failed to allocate argument vector")); + pam_syslog(pamh, LOG_ERR, + "(%s) argument vector allocation failed", + this_service); + mod_path = NULL; + handler_type = PAM_HT_MUST_FAIL; + } + } else { /* there are no arguments so fix by hand */ + D(("empty argument list")); + argvlen = 0; + argc = 0; + argv = NULL; + } + +#ifdef PAM_DEBUG + { + int y; + + D(("CONF%s: %s%s %d %s %d" + , handler_type==PAM_HT_MUST_FAIL?"<*will fail*>":"" + , this_service, other ? "(backup)":"" + , module_type + , mod_path, argc)); + for (y = 0; y < argc; y++) { + D(("CONF: %s", argv[y])); + } + for (y = 0; y<_PAM_RETURN_VALUES; ++y) { + D(("RETURN %s(%d) -> %d %s", + _pam_token_returns[y], y, actions[y], + actions[y]>0 ? "jump": + _pam_token_actions[-actions[y]])); + } + } +#endif + + res = _pam_add_handler(pamh, handler_type, other, stack_level + , module_type, actions, mod_path + , argc, argv, argvlen); + if (res != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "error loading %s", mod_path); + D(("failed to load module - aborting")); + return PAM_ABORT; + } + } + } + + return ( (x < 0) ? PAM_ABORT:PAM_SUCCESS ); +} + +static int +_pam_open_config_file(pam_handle_t *pamh + , const char *service + , char **path + , FILE **file) +{ + const char *const pamd_dirs[] = { PAM_CONFIG_DF, PAM_CONFIG_DIST_DF +#ifdef VENDORDIR + , PAM_CONFIG_DIST2_DF +#endif + }; + char *p = NULL; + FILE *f; + size_t i; + + /* Absolute path */ + if (service[0] == '/') { + p = _pam_strdup(service); + if (p == NULL) { + pam_syslog(pamh, LOG_CRIT, "strdup failed"); + return PAM_BUF_ERR; + } + } else if (pamh->confdir != NULL) { + if (asprintf (&p, "%s/%s", pamh->confdir, service) < 0) { + pam_syslog(pamh, LOG_CRIT, "asprintf failed"); + return PAM_BUF_ERR; + } + } + + if (p != NULL) { + D(("opening %s", p)); + f = fopen(p, "r"); + if (f != NULL) { + *path = p; + *file = f; + return PAM_SUCCESS; + } + _pam_drop(p); + return PAM_ABORT; + } + + for (i = 0; i < PAM_ARRAY_SIZE(pamd_dirs); i++) { + DIAG_PUSH_IGNORE_FORMAT_NONLITERAL + if (asprintf (&p, pamd_dirs[i], service) < 0) { + pam_syslog(pamh, LOG_CRIT, "asprintf failed"); + return PAM_BUF_ERR; + } + DIAG_POP_IGNORE_FORMAT_NONLITERAL + + D(("opening %s", p)); + f = fopen(p, "r"); + if (f != NULL) { + *path = p; + *file = f; + return PAM_SUCCESS; + } + _pam_drop(p); + } + + return PAM_ABORT; +} + +static int _pam_load_conf_file(pam_handle_t *pamh, const char *config_name + , const char *service /* specific file */ + , int module_type /* specific type */ + , int include_level /* level of include */ + , int stack_level /* level of substack */ +#ifdef PAM_READ_BOTH_CONFS + , int not_other +#endif /* PAM_READ_BOTH_CONFS */ + ) +{ + FILE *f; + char *path = NULL; + int retval = PAM_ABORT; + + D(("called.")); + + if (include_level >= PAM_SUBSTACK_MAX_LEVEL) { + D(("maximum level of inclusions reached")); + pam_syslog(pamh, LOG_ERR, "maximum level of inclusions reached"); + return PAM_ABORT; + } + + if (config_name == NULL) { + D(("no config file supplied")); + pam_syslog(pamh, LOG_ERR, "(%s) no config name supplied", service); + return PAM_ABORT; + } + + if (_pam_open_config_file(pamh, config_name, &path, &f) == PAM_SUCCESS) { + retval = _pam_parse_conf_file(pamh, f, service, module_type, include_level, stack_level +#ifdef PAM_READ_BOTH_CONFS + , not_other +#endif /* PAM_READ_BOTH_CONFS */ + ); + if (retval != PAM_SUCCESS) + pam_syslog(pamh, LOG_ERR, + "_pam_load_conf_file: error reading %s: %s", + path, pam_strerror(pamh, retval)); + _pam_drop(path); + fclose(f); + } else { + D(("unable to open %s", config_name)); + pam_syslog(pamh, LOG_ERR, + "_pam_load_conf_file: unable to open config for %s", + config_name); + } + + return retval; +} + +/* Parse config file, allocate handler structures, dlopen() */ +int _pam_init_handlers(pam_handle_t *pamh) +{ + FILE *f; + int retval; + + D(("called.")); + IF_NO_PAMH(pamh,PAM_SYSTEM_ERR); + + /* Return immediately if everything is already loaded */ + if (pamh->handlers.handlers_loaded) { + return PAM_SUCCESS; + } + + D(("initializing")); + + /* First clean the service structure */ + + _pam_free_handlers(pamh); + if (! pamh->handlers.module) { + if ((pamh->handlers.module = + malloc(MODULE_CHUNK * sizeof(struct loaded_module))) == NULL) { + pam_syslog(pamh, LOG_CRIT, + "_pam_init_handlers: no memory loading module"); + return PAM_BUF_ERR; + } + pamh->handlers.modules_allocated = MODULE_CHUNK; + pamh->handlers.modules_used = 0; + } + + if (pamh->service_name == NULL) { + return PAM_BAD_ITEM; /* XXX - better error? */ + } + +#ifdef PAM_LOCKING + /* Is the PAM subsystem locked? */ + { + int fd_tmp; + + if ((fd_tmp = open( PAM_LOCK_FILE, O_RDONLY )) != -1) { + pam_syslog(pamh, LOG_ERR, + "_pam_init_handlers: PAM lockfile (" + PAM_LOCK_FILE ") exists - aborting"); + (void) close(fd_tmp); + /* + * to avoid swamping the system with requests + */ + _pam_start_timer(pamh); + pam_fail_delay(pamh, 5000000); + _pam_await_timer(pamh, PAM_ABORT); + + return PAM_ABORT; + } + } +#endif /* PAM_LOCKING */ + + /* + * Now parse the config file(s) and add handlers + */ + { + struct stat test_d; + + /* Is there a PAM_CONFIG_D directory? */ + if (pamh->confdir != NULL || + (stat(PAM_CONFIG_D, &test_d) == 0 && S_ISDIR(test_d.st_mode)) || + (stat(PAM_CONFIG_DIST_D, &test_d) == 0 && S_ISDIR(test_d.st_mode)) +#ifdef PAM_CONFIG_DIST2_D + || (stat(PAM_CONFIG_DIST2_D, &test_d) == 0 + && S_ISDIR(test_d.st_mode)) +#endif + ) { + char *path = NULL; + int read_something=0; + + if (_pam_open_config_file(pamh, pamh->service_name, &path, &f) == PAM_SUCCESS) { + retval = _pam_parse_conf_file(pamh, f, pamh->service_name, + PAM_T_ANY, 0, 0 +#ifdef PAM_READ_BOTH_CONFS + , 0 +#endif /* PAM_READ_BOTH_CONFS */ + ); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, + "_pam_init_handlers: error reading %s", + path); + pam_syslog(pamh, LOG_ERR, "_pam_init_handlers: [%s]", + pam_strerror(pamh, retval)); + } else { + read_something = 1; + } + _pam_drop(path); + fclose(f); + } else { + D(("unable to open configuration for %s", pamh->service_name)); +#ifdef PAM_READ_BOTH_CONFS + D(("checking %s", PAM_CONFIG)); + + if (pamh->confdir == NULL + && (f = fopen(PAM_CONFIG,"r")) != NULL) { + retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0, 0, 1); + fclose(f); + } else +#endif /* PAM_READ_BOTH_CONFS */ + retval = PAM_SUCCESS; + /* + * XXX - should we log an error? Some people want to always + * use "other" + */ + } + + if (retval == PAM_SUCCESS) { + /* now parse the PAM_DEFAULT_SERVICE */ + + if (_pam_open_config_file(pamh, PAM_DEFAULT_SERVICE, &path, &f) == PAM_SUCCESS) { + /* would test magic here? */ + retval = _pam_parse_conf_file(pamh, f, PAM_DEFAULT_SERVICE, + PAM_T_ANY, 0, 0 +#ifdef PAM_READ_BOTH_CONFS + , 0 +#endif /* PAM_READ_BOTH_CONFS */ + ); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, + "_pam_init_handlers: error reading %s", + path); + pam_syslog(pamh, LOG_ERR, + "_pam_init_handlers: [%s]", + pam_strerror(pamh, retval)); + } else { + read_something = 1; + } + _pam_drop(path); + fclose(f); + } else { + D(("unable to open configuration for %s", PAM_DEFAULT_SERVICE)); + pam_syslog(pamh, LOG_ERR, + "_pam_init_handlers: no default config %s", + PAM_DEFAULT_SERVICE); + } + if (!read_something) { /* nothing read successfully */ + retval = PAM_ABORT; + } + } + } else { + if ((f = fopen(PAM_CONFIG, "r")) == NULL) { + pam_syslog(pamh, LOG_ERR, "_pam_init_handlers: could not open " + PAM_CONFIG ); + return PAM_ABORT; + } + + retval = _pam_parse_conf_file(pamh, f, NULL, PAM_T_ANY, 0, 0 +#ifdef PAM_READ_BOTH_CONFS + , 0 +#endif /* PAM_READ_BOTH_CONFS */ + ); + + D(("closing configuration file")); + fclose(f); + } + } + + if (retval != PAM_SUCCESS) { + /* Read error */ + pam_syslog(pamh, LOG_ERR, "error reading PAM configuration file"); + return PAM_ABORT; + } + + pamh->handlers.handlers_loaded = 1; + + D(("exiting")); + return PAM_SUCCESS; +} + +static char * +extract_modulename(const char *mod_path) +{ + const char *p = strrchr (mod_path, '/'); + char *dot, *retval; + + if (p == NULL) + p = mod_path; + else + p++; + + if ((retval = _pam_strdup (p)) == NULL) + return NULL; + + dot = strrchr (retval, '.'); + if (dot) + *dot = '\0'; + + if (*retval == '\0' || strcmp(retval, "?") == 0) { + /* do not allow empty module name or "?" to avoid confusing audit trail */ + _pam_drop(retval); + return NULL; + } + + return retval; +} + +static struct loaded_module * +_pam_load_module(pam_handle_t *pamh, const char *mod_path, int handler_type) +{ + int x = 0; + int success; + struct loaded_module *mod; + + D(("loading module `%s'", mod_path)); + + mod = pamh->handlers.module; + + /* First, ensure the module is loaded */ + while (x < pamh->handlers.modules_used) { + if (!strcmp(mod[x].name, mod_path)) { /* case sensitive ! */ + break; + } + x++; + } + if (x == pamh->handlers.modules_used) { + /* Not found */ + if (pamh->handlers.modules_allocated == pamh->handlers.modules_used) { + /* will need more memory */ + void *tmp = realloc(pamh->handlers.module, + (pamh->handlers.modules_allocated+MODULE_CHUNK) + *sizeof(struct loaded_module)); + if (tmp == NULL) { + D(("cannot enlarge module pointer memory")); + pam_syslog(pamh, LOG_CRIT, + "realloc returned NULL in _pam_load_module"); + return NULL; + } + pamh->handlers.module = tmp; + pamh->handlers.modules_allocated += MODULE_CHUNK; + } + mod = &(pamh->handlers.module[x]); + /* Be pessimistic... */ + success = PAM_ABORT; + + D(("_pam_dlopen(%s)", mod_path)); + mod->dl_handle = _pam_dlopen(mod_path); + D(("_pam_dlopen'ed")); + D(("dlopen'ed")); + if (mod->dl_handle == NULL) { + const char *isa = strstr(mod_path, "$ISA"); + size_t isa_len = strlen("$ISA"); + + if (isa != NULL) { + char *mod_full_isa_path = NULL; + if (strlen(mod_path) >= INT_MAX || + asprintf(&mod_full_isa_path, "%.*s%s%s", + (int)(isa - mod_path), mod_path, _PAM_ISA, isa + isa_len) < 0) { + D(("couldn't get memory for mod_path")); + pam_syslog(pamh, LOG_CRIT, "no memory for module path"); + success = PAM_ABORT; + } else { + mod->dl_handle = _pam_dlopen(mod_full_isa_path); + _pam_drop(mod_full_isa_path); + } + } + } + if (mod->dl_handle == NULL) { + D(("_pam_dlopen(%s) failed", mod_path)); + if (handler_type != PAM_HT_SILENT_MODULE) + pam_syslog(pamh, LOG_ERR, "unable to dlopen(%s): %s", mod_path, + _pam_dlerror()); + /* Don't abort yet; static code may be able to find function. + * But defaults to abort if nothing found below... */ + } else { + D(("module added successfully")); + success = PAM_SUCCESS; + mod->type = PAM_MT_DYNAMIC_MOD; + pamh->handlers.modules_used++; + } + + if (success != PAM_SUCCESS) { /* add a malformed module */ + mod->dl_handle = NULL; + mod->type = PAM_MT_FAULTY_MOD; + pamh->handlers.modules_used++; + if (handler_type != PAM_HT_SILENT_MODULE) + pam_syslog(pamh, LOG_ERR, "adding faulty module: %s", mod_path); + success = PAM_SUCCESS; /* We have successfully added a module */ + } + + /* indicate its name - later we will search for it by this */ + if ((mod->name = _pam_strdup(mod_path)) == NULL) { + D(("couldn't get memory for mod_path")); + pam_syslog(pamh, LOG_CRIT, "no memory for module path"); + success = PAM_ABORT; + } + + } else { /* x != pamh->handlers.modules_used */ + mod += x; /* the located module */ + success = PAM_SUCCESS; + } + return success == PAM_SUCCESS ? mod : NULL; +} + +static int _pam_add_handler(pam_handle_t *pamh + , int handler_type, int other, int stack_level + , int type, int *actions, const char *mod_path + , int argc, char **argv, size_t argvlen) +{ + struct loaded_module *mod = NULL; + struct handler **handler_p; + struct handler **handler_p2; + struct handlers *the_handlers; + const char *sym, *sym2; + char *mod_full_path; + servicefn func, func2; + int mod_type = PAM_MT_FAULTY_MOD; + + D(("called.")); + IF_NO_PAMH(pamh,PAM_SYSTEM_ERR); + + D(("adding type %d, handler_type %d, module `%s'", + type, handler_type, mod_path)); + + if ((handler_type == PAM_HT_MODULE || handler_type == PAM_HT_SILENT_MODULE) && + mod_path != NULL) { + if (mod_path[0] == '/') { + mod = _pam_load_module(pamh, mod_path, handler_type); + } else if (asprintf(&mod_full_path, "%s%s", + DEFAULT_MODULE_PATH, mod_path) >= 0) { + mod = _pam_load_module(pamh, mod_full_path, handler_type); + _pam_drop(mod_full_path); + } else { + pam_syslog(pamh, LOG_CRIT, "cannot malloc full mod path"); + return PAM_ABORT; + } + + if (mod == NULL) { + /* if we get here with NULL it means allocation error */ + return PAM_ABORT; + } + + mod_type = mod->type; + } + + if (mod_path == NULL) + mod_path = UNKNOWN_MODULE; + + /* + * At this point 'mod' points to the stored/loaded module. + */ + + /* Now define the handler(s) based on mod->dlhandle and type */ + + /* decide which list of handlers to use */ + the_handlers = (other) ? &pamh->handlers.other : &pamh->handlers.conf; + + handler_p = handler_p2 = NULL; + func = func2 = NULL; + sym2 = NULL; + + /* point handler_p's at the root addresses of the function stacks */ + switch (type) { + case PAM_T_AUTH: + handler_p = &the_handlers->authenticate; + sym = "pam_sm_authenticate"; + handler_p2 = &the_handlers->setcred; + sym2 = "pam_sm_setcred"; + break; + case PAM_T_SESS: + handler_p = &the_handlers->open_session; + sym = "pam_sm_open_session"; + handler_p2 = &the_handlers->close_session; + sym2 = "pam_sm_close_session"; + break; + case PAM_T_ACCT: + handler_p = &the_handlers->acct_mgmt; + sym = "pam_sm_acct_mgmt"; + break; + case PAM_T_PASS: + handler_p = &the_handlers->chauthtok; + sym = "pam_sm_chauthtok"; + break; + default: + /* Illegal module type */ + D(("illegal module type %d", type)); + return PAM_ABORT; + } + + /* are the modules reliable? */ + if (mod_type != PAM_MT_DYNAMIC_MOD && + mod_type != PAM_MT_FAULTY_MOD) { + D(("illegal module library type; %d", mod_type)); + pam_syslog(pamh, LOG_ERR, + "internal error: module library type not known: %s;%d", + sym, mod_type); + return PAM_ABORT; + } + + /* now identify this module's functions - for non-faulty modules */ + + if ((mod_type == PAM_MT_DYNAMIC_MOD) && + !(func = _pam_dlsym(mod->dl_handle, sym)) ) { + pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym); + } + if (sym2) { + if ((mod_type == PAM_MT_DYNAMIC_MOD) && + !(func2 = _pam_dlsym(mod->dl_handle, sym2)) ) { + pam_syslog(pamh, LOG_ERR, "unable to resolve symbol: %s", sym2); + } + } + + /* here func (and perhaps func2) point to the appropriate functions */ + + /* add new handler to end of existing list */ + while (*handler_p != NULL) { + handler_p = &((*handler_p)->next); + } + + if ((*handler_p = calloc(1, sizeof(struct handler))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "cannot allocate struct handler #1"); + return (PAM_ABORT); + } + + (*handler_p)->handler_type = handler_type; + (*handler_p)->stack_level = stack_level; + (*handler_p)->func = func; + memcpy((*handler_p)->actions,actions,sizeof((*handler_p)->actions)); + (*handler_p)->cached_retval = _PAM_INVALID_RETVAL; + (*handler_p)->cached_retval_p = &((*handler_p)->cached_retval); + (*handler_p)->argc = argc; + (*handler_p)->argv = argv; /* not a copy */ + if (((*handler_p)->mod_name = extract_modulename(mod_path)) == NULL) + return PAM_ABORT; + + /* some of the modules have a second calling function */ + if (handler_p2) { + /* add new handler to end of existing list */ + while (*handler_p2) { + handler_p2 = &((*handler_p2)->next); + } + + if ((*handler_p2 = calloc(1, sizeof(struct handler))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "cannot allocate struct handler #2"); + return (PAM_ABORT); + } + + (*handler_p2)->handler_type = handler_type; + (*handler_p2)->stack_level = stack_level; + (*handler_p2)->func = func2; + memcpy((*handler_p2)->actions,actions,sizeof((*handler_p2)->actions)); + (*handler_p2)->cached_retval = _PAM_INVALID_RETVAL; /* ignored */ + /* Note, this next entry points to the handler_p value! */ + (*handler_p2)->cached_retval_p = &((*handler_p)->cached_retval); + (*handler_p2)->argc = argc; + if (argv) { + if (((*handler_p2)->argv = malloc(argvlen)) == NULL) { + pam_syslog(pamh, LOG_CRIT, "cannot malloc argv for handler #2"); + return (PAM_ABORT); + } + memcpy((*handler_p2)->argv, argv, argvlen); + } + if (((*handler_p2)->mod_name = extract_modulename(mod_path)) == NULL) + return PAM_ABORT; + } + + D(("returning successfully")); + + return PAM_SUCCESS; +} + +/* Free various allocated structures and dlclose() the libs */ +int _pam_free_handlers(pam_handle_t *pamh) +{ + struct loaded_module *mod; + + D(("called.")); + IF_NO_PAMH(pamh,PAM_SYSTEM_ERR); + + mod = pamh->handlers.module; + + /* Close all loaded modules */ + + while (pamh->handlers.modules_used) { + D(("dlclose(%s)", mod->name)); + free(mod->name); + if (mod->type == PAM_MT_DYNAMIC_MOD) { + _pam_dlclose(mod->dl_handle); + } + mod++; + pamh->handlers.modules_used--; + } + + /* Free all the handlers */ + + _pam_free_handlers_aux(&(pamh->handlers.conf.authenticate)); + _pam_free_handlers_aux(&(pamh->handlers.conf.setcred)); + _pam_free_handlers_aux(&(pamh->handlers.conf.acct_mgmt)); + _pam_free_handlers_aux(&(pamh->handlers.conf.open_session)); + _pam_free_handlers_aux(&(pamh->handlers.conf.close_session)); + _pam_free_handlers_aux(&(pamh->handlers.conf.chauthtok)); + + _pam_free_handlers_aux(&(pamh->handlers.other.authenticate)); + _pam_free_handlers_aux(&(pamh->handlers.other.setcred)); + _pam_free_handlers_aux(&(pamh->handlers.other.acct_mgmt)); + _pam_free_handlers_aux(&(pamh->handlers.other.open_session)); + _pam_free_handlers_aux(&(pamh->handlers.other.close_session)); + _pam_free_handlers_aux(&(pamh->handlers.other.chauthtok)); + + /* no more loaded modules */ + + _pam_drop(pamh->handlers.module); + + /* Indicate that handlers are not initialized for this pamh */ + + pamh->handlers.handlers_loaded = 0; + + return PAM_SUCCESS; +} + +void _pam_start_handlers(pam_handle_t *pamh) +{ + D(("called.")); + /* NB. There is no check for a NULL pamh here, since no return + * value to communicate the fact! */ + + /* Indicate that handlers are not initialized for this pamh */ + pamh->handlers.handlers_loaded = 0; + + pamh->handlers.modules_allocated = 0; + pamh->handlers.modules_used = 0; + pamh->handlers.module = NULL; + + /* initialize the .conf and .other entries */ + + pamh->handlers.conf.authenticate = NULL; + pamh->handlers.conf.setcred = NULL; + pamh->handlers.conf.acct_mgmt = NULL; + pamh->handlers.conf.open_session = NULL; + pamh->handlers.conf.close_session = NULL; + pamh->handlers.conf.chauthtok = NULL; + + pamh->handlers.other.authenticate = NULL; + pamh->handlers.other.setcred = NULL; + pamh->handlers.other.acct_mgmt = NULL; + pamh->handlers.other.open_session = NULL; + pamh->handlers.other.close_session = NULL; + pamh->handlers.other.chauthtok = NULL; +} + +void _pam_free_handlers_aux(struct handler **hp) +{ + struct handler *h = *hp; + struct handler *last; + + D(("called.")); + while (h) { + last = h; + _pam_drop(h->argv); /* This is all allocated in a single chunk */ + _pam_drop(h->mod_name); + h = h->next; + pam_overwrite_object(last); + free(last); + } + + *hp = NULL; +} diff --git a/libpam/pam_item.c b/libpam/pam_item.c new file mode 100644 index 0000000..2e43d76 --- /dev/null +++ b/libpam/pam_item.c @@ -0,0 +1,402 @@ +/* pam_item.c */ + +/* + * $Id$ + */ + +#include "pam_private.h" +#include "pam_inline.h" +#include "pam_i18n.h" + +#include +#include +#include +#include + +#define TRY_SET(X, Y) \ +do { \ + if ((X) != (Y)) { \ + char *_TMP_ = _pam_strdup(Y); \ + if (_TMP_ == NULL && (Y) != NULL) \ + return PAM_BUF_ERR; \ + free(X); \ + (X) = _TMP_; \ + } \ +} while(0) + +/* functions */ + +int pam_set_item (pam_handle_t *pamh, int item_type, const void *item) +{ + int retval; + + D(("called")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + retval = PAM_SUCCESS; + + switch (item_type) { + + case PAM_SERVICE: + if (item == NULL) { + pam_syslog(pamh, LOG_ERR, + "pam_set_item: attempt to set service to NULL"); + retval = PAM_BAD_ITEM; + break; + } + /* Setting handlers_loaded to 0 will cause the handlers + * to be reloaded on the next call to a service module. + */ + pamh->handlers.handlers_loaded = 0; + TRY_SET(pamh->service_name, item); + { + char *tmp; + for (tmp=pamh->service_name; *tmp; ++tmp) + *tmp = tolower((unsigned char)*tmp); /* require lower case */ + } + break; + + case PAM_USER: + TRY_SET(pamh->user, item); + pamh->former.fail_user = PAM_SUCCESS; + break; + + case PAM_USER_PROMPT: + TRY_SET(pamh->prompt, item); + pamh->former.fail_user = PAM_SUCCESS; + break; + + case PAM_TTY: + D(("setting tty to %s", (const char *)item)); + TRY_SET(pamh->tty, item); + break; + + case PAM_RUSER: + TRY_SET(pamh->ruser, item); + break; + + case PAM_RHOST: + TRY_SET(pamh->rhost, item); + break; + + case PAM_AUTHTOK: + /* + * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from + * modules. + */ + if (__PAM_FROM_MODULE(pamh)) { + if (pamh->authtok != item) { + pam_overwrite_string(pamh->authtok); + TRY_SET(pamh->authtok, item); + } + } else { + retval = PAM_BAD_ITEM; + } + + break; + + case PAM_OLDAUTHTOK: + /* + * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from + * modules. + */ + if (__PAM_FROM_MODULE(pamh)) { + if (pamh->oldauthtok != item) { + pam_overwrite_string(pamh->oldauthtok); + TRY_SET(pamh->oldauthtok, item); + } + } else { + retval = PAM_BAD_ITEM; + } + + break; + + case PAM_CONV: /* want to change the conversation function */ + if (item == NULL) { + pam_syslog(pamh, LOG_ERR, + "pam_set_item: attempt to set conv() to NULL"); + retval = PAM_PERM_DENIED; + } else { + struct pam_conv *tconv; + + if ((tconv = malloc(sizeof(struct pam_conv)) + ) == NULL) { + pam_syslog(pamh, LOG_CRIT, + "pam_set_item: malloc failed for pam_conv"); + retval = PAM_BUF_ERR; + } else { + memcpy(tconv, item, sizeof(struct pam_conv)); + _pam_drop(pamh->pam_conversation); + pamh->pam_conversation = tconv; + pamh->former.fail_user = PAM_SUCCESS; + } + } + break; + + case PAM_FAIL_DELAY: + pamh->fail_delay.delay_fn_ptr = item; + break; + + case PAM_XDISPLAY: + TRY_SET(pamh->xdisplay, item); + break; + + case PAM_XAUTHDATA: + if (&pamh->xauth == item) + break; + if (pamh->xauth.namelen) { + pam_overwrite_string(pamh->xauth.name); + free(pamh->xauth.name); + } + if (pamh->xauth.datalen) { + pam_overwrite_n(pamh->xauth.data, (unsigned int) pamh->xauth.datalen); + free(pamh->xauth.data); + } + pamh->xauth = *((const struct pam_xauth_data *) item); + if ((pamh->xauth.name=_pam_strdup(pamh->xauth.name)) == NULL) { + pam_overwrite_object(&pamh->xauth); + return PAM_BUF_ERR; + } + if ((pamh->xauth.data=_pam_memdup(pamh->xauth.data, + pamh->xauth.datalen)) == NULL) { + pam_overwrite_string(pamh->xauth.name); + free(pamh->xauth.name); + pam_overwrite_object(&pamh->xauth); + return PAM_BUF_ERR; + } + break; + + case PAM_AUTHTOK_TYPE: + TRY_SET(pamh->authtok_type, item); + break; + + default: + retval = PAM_BAD_ITEM; + } + + return retval; +} + +int pam_get_item (const pam_handle_t *pamh, int item_type, const void **item) +{ + int retval = PAM_SUCCESS; + + D(("called.")); + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (item == NULL) { + pam_syslog(pamh, LOG_ERR, + "pam_get_item: nowhere to place requested item"); + return PAM_PERM_DENIED; + } + else + *item = NULL; + + switch (item_type) { + case PAM_SERVICE: + *item = pamh->service_name; + break; + + case PAM_USER: + D(("returning user=%s", pamh->user)); + *item = pamh->user; + break; + + case PAM_USER_PROMPT: + D(("returning userprompt=%s", pamh->user)); + *item = pamh->prompt; + break; + + case PAM_TTY: + D(("returning tty=%s", pamh->tty)); + *item = pamh->tty; + break; + + case PAM_RUSER: + *item = pamh->ruser; + break; + + case PAM_RHOST: + *item = pamh->rhost; + break; + + case PAM_AUTHTOK: + /* + * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from + * modules. + */ + if (__PAM_FROM_MODULE(pamh)) { + *item = pamh->authtok; + } else { + retval = PAM_BAD_ITEM; + } + break; + + case PAM_OLDAUTHTOK: + /* + * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from + * modules. + */ + if (__PAM_FROM_MODULE(pamh)) { + *item = pamh->oldauthtok; + } else { + retval = PAM_BAD_ITEM; + } + break; + + case PAM_CONV: + *item = pamh->pam_conversation; + break; + + case PAM_FAIL_DELAY: + *item = pamh->fail_delay.delay_fn_ptr; + break; + + case PAM_XDISPLAY: + *item = pamh->xdisplay; + break; + + case PAM_XAUTHDATA: + *item = &pamh->xauth; + break; + + case PAM_AUTHTOK_TYPE: + *item = pamh->authtok_type; + break; + + default: + retval = PAM_BAD_ITEM; + } + + return retval; +} + +/* + * This function is the 'preferred method to obtain the username'. + */ + +int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt) +{ + const char *use_prompt; + int retval; + struct pam_message msg; + const struct pam_message *pmsg; + struct pam_response *resp; + + D(("called.")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (user == NULL) { + /* ensure that the module has supplied a destination */ + pam_syslog(pamh, LOG_ERR, "pam_get_user: nowhere to record username"); + return PAM_SYSTEM_ERR; + } else + *user = NULL; + + if (pamh->pam_conversation == NULL) { + pam_syslog(pamh, LOG_ERR, "pam_get_user: no conv element in pamh"); + return PAM_SYSTEM_ERR; + } + + if (pamh->user) { /* have one so return it */ + *user = pamh->user; + return PAM_SUCCESS; + } + + if (pamh->former.fail_user != PAM_SUCCESS) + return pamh->former.fail_user; + + /* will need a prompt */ + if (prompt != NULL) + use_prompt = prompt; + else if (pamh->prompt != NULL) + use_prompt = pamh->prompt; + else + use_prompt = _("login:"); + + /* If we are resuming an old conversation, we verify that the prompt + is the same. Anything else is an error. */ + if (pamh->former.want_user) { + /* must have a prompt to resume with */ + if (! pamh->former.prompt) { + pam_syslog(pamh, LOG_ERR, + "pam_get_user: failed to resume with prompt" + ); + return PAM_ABORT; + } + + /* must be the same prompt as last time */ + if (strcmp(pamh->former.prompt, use_prompt)) { + pam_syslog(pamh, LOG_ERR, + "pam_get_user: resumed with different prompt"); + return PAM_ABORT; + } + + /* ok, we can resume where we left off last time */ + pamh->former.want_user = PAM_FALSE; + pam_overwrite_string(pamh->former.prompt); + _pam_drop(pamh->former.prompt); + } + + /* converse with application -- prompt user for a username */ + pmsg = &msg; + msg.msg_style = PAM_PROMPT_ECHO_ON; + msg.msg = use_prompt; + resp = NULL; + + retval = pamh->pam_conversation-> + conv(1, &pmsg, &resp, pamh->pam_conversation->appdata_ptr); + + switch (retval) { + case PAM_SUCCESS: + case PAM_BUF_ERR: + case PAM_CONV_AGAIN: + case PAM_CONV_ERR: + break; + default: + retval = PAM_CONV_ERR; + } + + switch (retval) { + case PAM_CONV_AGAIN: + /* conversation function is waiting for an event - save state */ + D(("conversation function is not ready yet")); + pamh->former.want_user = PAM_TRUE; + pamh->former.prompt = _pam_strdup(use_prompt); + break; + case PAM_SUCCESS: + if (resp != NULL && resp->resp != NULL) { + /* + * now we set the PAM_USER item -- this was missing from pre.53 + * releases. However, reading the Sun manual, it is part of + * the standard API. + */ + retval = pam_set_item(pamh, PAM_USER, resp->resp); + *user = pamh->user; + break; + } else { + /* conversation should have given a response */ + D(("no response provided")); + retval = PAM_CONV_ERR; + } + /* fallthrough */ + default: + pamh->former.fail_user = retval; + } + + if (resp) { + if (retval != PAM_SUCCESS) + pam_syslog(pamh, LOG_WARNING, + "unexpected response from failed conversation function"); + /* + * note 'resp' is allocated by the application and is + * correctly free()'d here + */ + pam_drop_response(resp, 1); + } + + D(("completed")); + return retval; /* pass on any error from conversation */ +} diff --git a/libpam/pam_misc.c b/libpam/pam_misc.c new file mode 100644 index 0000000..e379d2f --- /dev/null +++ b/libpam/pam_misc.c @@ -0,0 +1,353 @@ +/* pam_misc.c -- This is random stuff + * + * Copyright (c) Andrew G. Morgan 2000-2003 + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pam_private.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DELIMITERS " \n\t" + +char *_pam_tokenize(char *from, char **next) +/* + * this function is a variant of the standard strtok_r, it differs in that + * it uses a fixed set of delimiters and doesn't nul terminate tokens until + * they are actually reached. + */ +{ + char *end; + + if (from == NULL && (from = *next) == NULL) + return from; + + /* look for first non-format char */ + from += strspn(from, DELIMITERS); + + if (*from == '[') { + /* + * special case, "[...]" is considered to be a single + * object. Note, any '[' inside the outer "[...]" pair will + * survive. Note, the first ']' will terminate this string, + * but that "\]" will get compressed into "]". That is: + * + * "[..[..\]..]..." --> "..[..].." + */ + char *to; + for (to=end=++from; *end && *end != ']'; ++to, ++end) { + if (*end == '\\' && end[1] == ']') + ++end; + if (to != end) { + *to = *end; + } + } + if (to != end) { + *to = '\0'; + } + /* note, this string is stripped of its edges: "..." is what + remains */ + } else if (*from) { + /* simply look for next blank char */ + end = from + strcspn(from, DELIMITERS); + } else { + return (*next = NULL); /* no tokens left */ + } + + /* now terminate what we have */ + if (*end) + *end++ = '\0'; + + /* indicate what it left */ + if (*end) { + *next = end; + } else { + *next = NULL; /* have found last token */ + } + + /* return what we have */ + return from; +} + +/* + * Safe duplication of character strings. "Paranoid"; don't leave + * evidence of old token around for later stack analysis. + */ + +char *_pam_strdup(const char *x) +{ + register char *new=NULL; + + if (x != NULL) { + if ((new = strdup(x)) == NULL) { + pam_syslog(NULL, LOG_CRIT, "_pam_strdup: failed to get memory"); + } + x = NULL; + } + + return new; /* return the duplicate or NULL on error */ +} + +/* + * Safe duplication of memory buffers. "Paranoid"; don't leave + * evidence of old token around for later stack analysis. + */ + +char *_pam_memdup(const char *x, int len) +{ + register char *new=NULL; + + if (x != NULL) { + if ((new = malloc(len)) == NULL) { + len = 0; + pam_syslog(NULL, LOG_CRIT, "_pam_memdup: failed to get memory"); + } else { + memcpy (new, x, len); + } + x = NULL; + } + + return new; /* return the duplicate or NULL on error */ +} + +/* Generate argv, argc from s */ +/* caller must free(argv) */ + +size_t _pam_mkargv(const char *s, char ***argv, int *argc) +{ + size_t l; + size_t argvlen = 0; + char **our_argv = NULL; + + D(("called: %s",s)); + + *argc = 0; + + l = strlen(s); + if (l && l < SIZE_MAX / (sizeof(char) + sizeof(char *))) { + char **argvbuf; + /* Overkill on the malloc, but not large */ + argvlen = (l + 1) * (sizeof(char) + sizeof(char *)); + if ((our_argv = argvbuf = malloc(argvlen)) == NULL) { + pam_syslog(NULL, LOG_CRIT, "pam_mkargv: null returned by malloc"); + argvlen = 0; + } else { + char *argvbufp; + char *tmp=NULL; + char *tok; +#ifdef PAM_DEBUG + unsigned count=0; +#endif + argvbufp = (char *) argvbuf + (l * sizeof(char *)); + strcpy(argvbufp, s); + D(("[%s]",argvbufp)); + while ((tok = _pam_tokenize(argvbufp, &tmp))) { + D(("arg #%u",++count)); + D(("->[%s]",tok)); + *argvbuf++ = tok; + if (*argc == INT_MAX) { + pam_syslog(NULL, LOG_CRIT, + "pam_mkargv: too many arguments"); + argvlen = 0; + _pam_drop(our_argv); + break; + } + (*argc)++; + argvbufp = NULL; + D(("loop again?")); + } + } + } + + *argv = our_argv; + + D(("exiting")); + + return(argvlen); +} + +/* + * this function is used to protect the modules from accidental or + * semi-malicious harm that an application may do to confuse the API. + */ + +void _pam_sanitize(pam_handle_t *pamh) +{ + int old_caller_is = pamh->caller_is; + + /* + * this is for security. We reset the auth-tokens here. + */ + __PAM_TO_MODULE(pamh); + pam_set_item(pamh, PAM_AUTHTOK, NULL); + pam_set_item(pamh, PAM_OLDAUTHTOK, NULL); + pamh->caller_is = old_caller_is; +} + +/* + * This function scans the array and replaces the _PAM_ACTION_UNDEF + * entries with the default action. + */ + +void _pam_set_default_control(int *control_array, int default_action) +{ + int i; + + for (i=0; i<_PAM_RETURN_VALUES; ++i) { + if (control_array[i] == _PAM_ACTION_UNDEF) { + control_array[i] = default_action; + } + } +} + +/* + * This function is used to parse a control string. This string is a + * series of tokens of the following form: + * + * "[ ]*return_code[ ]*=[ ]*action/[ ]". + */ + +#include "pam_tokens.h" + +void _pam_parse_control(int *control_array, char *tok) +{ + const char *error; + int ret; + + while (*tok) { + size_t len; + int act; + + /* skip leading space */ + while (isspace((unsigned char)*tok) && *++tok); + if (!*tok) + break; + + /* identify return code */ + for (ret=0; ret<=_PAM_RETURN_VALUES; ++ret) { + len = strlen(_pam_token_returns[ret]); + if (!strncmp(_pam_token_returns[ret], tok, len)) { + break; + } + } + if (ret > _PAM_RETURN_VALUES || !*(tok += len)) { + error = "expecting return value"; + goto parse_error; + } + + /* observe '=' */ + while (isspace((unsigned char)*tok) && *++tok); + if (!*tok || *tok++ != '=') { + error = "expecting '='"; + goto parse_error; + } + + /* skip leading space */ + while (isspace((unsigned char)*tok) && *++tok); + if (!*tok) { + error = "expecting action"; + goto parse_error; + } + + /* observe action type */ + for (act=0; act < (-(_PAM_ACTION_UNDEF)); ++act) { + len = strlen(_pam_token_actions[act]); + if (!strncmp(_pam_token_actions[act], tok, len)) { + act *= -1; + tok += len; + break; + } + } + if (act > 0) { + /* + * Either we have a number or we have hit an error. In + * principle, there is nothing to stop us accepting + * negative offsets. (Although we would have to think of + * another way of encoding the tokens.) However, I really + * think this would be both hard to administer and easily + * cause looping problems. So, for now, we will just + * allow forward jumps. (AGM 1998/1/7) + */ + if (!isdigit((unsigned char)*tok)) { + error = "expecting jump number"; + goto parse_error; + } + /* parse a number */ + act = 0; + do { + int digit = *tok - '0'; + if (act > INT_MAX / 10) { + error = "expecting smaller jump number"; + goto parse_error; + } + act *= 10; + if (act > INT_MAX - digit) { + error = "expecting smaller jump number"; + goto parse_error; + } + act += digit; /* XXX - this assumes ascii behavior */ + } while (*++tok && isdigit((unsigned char)*tok)); + if (! act) { + /* we do not allow 0 jumps. There is a token ('ignore') + for that */ + error = "expecting non-zero"; + goto parse_error; + } + } + + /* set control_array element */ + if (ret != _PAM_RETURN_VALUES) { + control_array[ret] = act; + } else { + /* set the default to 'act' */ + _pam_set_default_control(control_array, act); + } + } + + /* that was a success */ + return; + +parse_error: + /* treat everything as bad */ + pam_syslog(NULL, LOG_ERR, "pam_parse: %s; [...%s]", error, tok); + for (ret=0; ret<_PAM_RETURN_VALUES; control_array[ret++]=_PAM_ACTION_BAD); + +} diff --git a/libpam/pam_modutil_check_user.c b/libpam/pam_modutil_check_user.c new file mode 100644 index 0000000..4034e59 --- /dev/null +++ b/libpam/pam_modutil_check_user.c @@ -0,0 +1,77 @@ +#include "pam_modutil_private.h" +#include + +#include +#include +#include + +int +pam_modutil_check_user_in_passwd(pam_handle_t *pamh, + const char *user_name, + const char *file_name) +{ + int rc, c = EOF; + FILE *fp; + + /* Validate the user name. */ + if (user_name[0] == '\0') { + pam_syslog(pamh, LOG_NOTICE, "user name is not valid"); + return PAM_SERVICE_ERR; + } + + if (strchr(user_name, ':') != NULL) { + /* + * "root:x" is not a local user name even if the passwd file + * contains a line starting with "root:x:". + */ + return PAM_PERM_DENIED; + } + + /* Open the passwd file. */ + if (file_name == NULL) { + file_name = "/etc/passwd"; + } + if ((fp = fopen(file_name, "r")) == NULL) { + pam_syslog(pamh, LOG_ERR, "error opening %s: %m", file_name); + return PAM_SERVICE_ERR; + } + + /* + * Scan the file using fgetc() instead of fgetpwent_r() because + * the latter is not flexible enough in handling long lines + * in passwd files. + */ + rc = PAM_PERM_DENIED; + do { + const char *p; + + /* + * Does this line start with the user name + * followed by a colon? + */ + for (p = user_name; *p != '\0'; p++) { + c = fgetc(fp); + if (c == EOF || c == '\n' || (char)c != *p) + break; + } + + if (c != EOF && c != '\n') + c = fgetc(fp); + + if (*p == '\0' && c == ':') { + rc = PAM_SUCCESS; + /* + * Continue reading the file to avoid timing attacks. + */ + } + + /* Read till the end of this line. */ + while (c != EOF && c != '\n') + c = fgetc(fp); + + /* Continue with the next line. */ + } while (c != EOF); + + fclose(fp); + return rc; +} diff --git a/libpam/pam_modutil_cleanup.c b/libpam/pam_modutil_cleanup.c new file mode 100644 index 0000000..2077cbd --- /dev/null +++ b/libpam/pam_modutil_cleanup.c @@ -0,0 +1,17 @@ +/* + * $Id$ + * + * This function provides a common pam_set_data() friendly version of free(). + */ + +#include "pam_modutil_private.h" + +#include + +void +pam_modutil_cleanup (pam_handle_t *pamh UNUSED, void *data, + int error_status UNUSED) +{ + /* junk it */ + free(data); +} diff --git a/libpam/pam_modutil_getgrgid.c b/libpam/pam_modutil_getgrgid.c new file mode 100644 index 0000000..fd49510 --- /dev/null +++ b/libpam/pam_modutil_getgrgid.c @@ -0,0 +1,136 @@ +/* + * $Id$ + * + * This function provides a thread safer version of getgrgid() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pam_modutil_private.h" + +#include +#include +#include +#include +#include + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +static int longlen(long number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct group * +pam_modutil_getgrgid(pam_handle_t *pamh, gid_t gid) +{ +#ifdef HAVE_GETGRGID_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct group *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct group) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the user - so delete the memory */ + free(buffer); + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the grp structure */ + errno = 0; + status = getgrgid_r(gid, buffer, + sizeof(struct group) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getgrgid") + 1 + + longlen((long)gid) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getgrgid_%ld_%d", + (long) gid, i); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, pam_modutil_cleanup); + } + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= PWD_LENGTH_SHIFT; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("grp structure took %zu bytes or so of memory", + length+sizeof(struct group))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETGRGID_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getgrgid(). So, we use the standard libc function. + */ + + return getgrgid(gid); + +#endif /* def HAVE_GETGRGID_R */ +} diff --git a/libpam/pam_modutil_getgrnam.c b/libpam/pam_modutil_getgrnam.c new file mode 100644 index 0000000..c7dd175 --- /dev/null +++ b/libpam/pam_modutil_getgrnam.c @@ -0,0 +1,125 @@ +/* + * $Id$ + * + * This function provides a thread safer version of getgrnam() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pam_modutil_private.h" + +#include +#include +#include +#include +#include + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct group * +pam_modutil_getgrnam(pam_handle_t *pamh, const char *group) +{ +#ifdef HAVE_GETGRNAM_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct group *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct group) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the group - so delete the memory */ + free(buffer); + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the grp structure */ + errno = 0; + status = getgrnam_r(group, buffer, + sizeof(struct group) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getgrnam") + 1 + + strlen(group) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getgrnam_%s_%d", group, i); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, pam_modutil_cleanup); + } + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= PWD_LENGTH_SHIFT; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("grp structure took %zu bytes or so of memory", + length+sizeof(struct group))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETGRNAM_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getgrnam(). So, we use the standard libc function. + */ + + return getgrnam(group); + +#endif /* def HAVE_GETGRNAM_R */ +} diff --git a/libpam/pam_modutil_getlogin.c b/libpam/pam_modutil_getlogin.c new file mode 100644 index 0000000..2e7a011 --- /dev/null +++ b/libpam/pam_modutil_getlogin.c @@ -0,0 +1,46 @@ +/* + * $Id$ + * + * A central point for invoking getlogin(). Hopefully, this is a + * little harder to spoof than all the other versions that are out + * there. + */ + +#include "pam_modutil_private.h" + +#include +#include + +#define _PAMMODUTIL_GETLOGIN "_pammodutil_getlogin" + +const char * +pam_modutil_getlogin(pam_handle_t *pamh) +{ + int status; + const void *logname; + char *curr_user; + + status = pam_get_data(pamh, _PAMMODUTIL_GETLOGIN, &logname); + if (status == PAM_SUCCESS) { + return logname; + } + + logname = getlogin(); + if (logname == NULL) { + return NULL; + } + + curr_user = strdup(logname); + if (curr_user == NULL) { + return NULL; + } + + status = pam_set_data(pamh, _PAMMODUTIL_GETLOGIN, curr_user, + pam_modutil_cleanup); + if (status != PAM_SUCCESS) { + free(curr_user); + return NULL; + } + + return curr_user; +} diff --git a/libpam/pam_modutil_getpwnam.c b/libpam/pam_modutil_getpwnam.c new file mode 100644 index 0000000..9c96150 --- /dev/null +++ b/libpam/pam_modutil_getpwnam.c @@ -0,0 +1,125 @@ +/* + * $Id$ + * + * This function provides a thread safer version of getpwnam() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pam_modutil_private.h" + +#include +#include +#include +#include +#include + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct passwd * +pam_modutil_getpwnam(pam_handle_t *pamh, const char *user) +{ +#ifdef HAVE_GETPWNAM_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct passwd *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct passwd) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the user - so delete the memory */ + free(buffer); + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the pwd structure */ + errno = 0; + status = getpwnam_r(user, buffer, + sizeof(struct passwd) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getpwnam") + 1 + + strlen(user) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getpwnam_%s_%d", user, i); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, pam_modutil_cleanup); + } + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= PWD_LENGTH_SHIFT; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("pwd structure took %zu bytes or so of memory", + length+sizeof(struct passwd))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETPWNAM_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getpwnam(). So, we use the standard libc function. + */ + + return getpwnam(user); + +#endif /* def HAVE_GETPWNAM_R */ +} diff --git a/libpam/pam_modutil_getpwuid.c b/libpam/pam_modutil_getpwuid.c new file mode 100644 index 0000000..671fdf2 --- /dev/null +++ b/libpam/pam_modutil_getpwuid.c @@ -0,0 +1,136 @@ +/* + * $Id$ + * + * This function provides a thread safer version of getpwuid() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pam_modutil_private.h" + +#include +#include +#include +#include +#include + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +static int longlen(long number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct passwd * +pam_modutil_getpwuid(pam_handle_t *pamh, uid_t uid) +{ +#ifdef HAVE_GETPWUID_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct passwd *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct passwd) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the user - so delete the memory */ + free(buffer); + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the pwd structure */ + errno = 0; + status = getpwuid_r(uid, buffer, + sizeof(struct passwd) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getpwuid") + 1 + + longlen((long) uid) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getpwuid_%ld_%d", + (long) uid, i); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, pam_modutil_cleanup); + } + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= PWD_LENGTH_SHIFT; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("pwd structure took %zu bytes or so of memory", + length+sizeof(struct passwd))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETPWUID_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getpwuid(). So, we use the standard libc function. + */ + + return getpwuid(uid); + +#endif /* def HAVE_GETPWUID_R */ +} diff --git a/libpam/pam_modutil_getspnam.c b/libpam/pam_modutil_getspnam.c new file mode 100644 index 0000000..8b48db9 --- /dev/null +++ b/libpam/pam_modutil_getspnam.c @@ -0,0 +1,125 @@ +/* + * $Id$ + * + * This function provides a thread safer version of getspnam() for use + * with PAM modules that care about this sort of thing. + * + * XXX - or at least it should provide a thread-safe alternative. + */ + +#include "pam_modutil_private.h" + +#include +#include +#include +#include +#include + +static int intlen(int number) +{ + int len = 2; + while (number != 0) { + number /= 10; + len++; + } + return len; +} + +struct spwd * +pam_modutil_getspnam(pam_handle_t *pamh, const char *user) +{ +#ifdef HAVE_GETSPNAM_R + + void *buffer=NULL; + size_t length = PWD_INITIAL_LENGTH; + + do { + int status; + void *new_buffer; + struct spwd *result = NULL; + + new_buffer = realloc(buffer, sizeof(struct spwd) + length); + if (new_buffer == NULL) { + + D(("out of memory")); + + /* no memory for the user - so delete the memory */ + free(buffer); + return NULL; + } + buffer = new_buffer; + + /* make the re-entrant call to get the spwd structure */ + errno = 0; + status = getspnam_r(user, buffer, + sizeof(struct spwd) + (char *) buffer, + length, &result); + if (!status && (result == buffer)) { + char *data_name; + const void *ignore; + int i; + + data_name = malloc(strlen("_pammodutil_getspnam") + 1 + + strlen(user) + 1 + intlen(INT_MAX) + 1); + if ((pamh != NULL) && (data_name == NULL)) { + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + free(buffer); + return NULL; + } + + if (pamh != NULL) { + for (i = 0; i < INT_MAX; i++) { + sprintf(data_name, "_pammodutil_getspnam_%s_%d", user, i); + status = PAM_NO_MODULE_DATA; + if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { + status = pam_set_data(pamh, data_name, + result, pam_modutil_cleanup); + } + if (status == PAM_SUCCESS) { + break; + } + } + } else { + status = PAM_SUCCESS; + } + + free(data_name); + + if (status == PAM_SUCCESS) { + D(("success")); + return result; + } + + D(("was unable to register the data item [%s]", + pam_strerror(pamh, status))); + + free(buffer); + return NULL; + + } else if (errno != ERANGE && errno != EINTR) { + /* no sense in repeating the call */ + break; + } + + length <<= PWD_LENGTH_SHIFT; + + } while (length < PWD_ABSURD_PWD_LENGTH); + + D(("spwd structure took %zu bytes or so of memory", + length+sizeof(struct spwd))); + + free(buffer); + return NULL; + +#else /* ie. ifndef HAVE_GETSPNAM_R */ + + /* + * Sorry, there does not appear to be a reentrant version of + * getspnam(). So, we use the standard libc function. + */ + + return getspnam(user); + +#endif /* def HAVE_GETSPNAM_R */ +} diff --git a/libpam/pam_modutil_ingroup.c b/libpam/pam_modutil_ingroup.c new file mode 100644 index 0000000..356302e --- /dev/null +++ b/libpam/pam_modutil_ingroup.c @@ -0,0 +1,130 @@ +/* + * $Id$ + * + * This function provides common methods for checking if a user is in a + * specified group. + */ + +#include "pam_modutil_private.h" + +#include +#include +#include + +#ifdef HAVE_GETGROUPLIST + +#define NGROUPS_MIN 100 +#define NGROUPS_MAX 65536 + +static int checkgrouplist(const char *user, gid_t primary, gid_t target) +{ + int ngroups, pgroups, i; + + ngroups = NGROUPS_MIN; + do { + gid_t *grouplist; + + pgroups = ngroups; + grouplist = malloc(sizeof(gid_t) * ngroups); + if (grouplist == NULL) { + return 0; + } + i = getgrouplist(user, primary, grouplist, &ngroups); + if (i >= 0) { + for (i = 0; i < ngroups; i++) { + if (grouplist[i] == target) { + free(grouplist); + return 1; + } + } + } + free(grouplist); + } while (i < 0 && ngroups > 0 && ngroups != pgroups && ngroups <= NGROUPS_MAX); + return 0; +} +#endif + +static int +pam_modutil_user_in_group_common(pam_handle_t *pamh UNUSED, + struct passwd *pwd, + struct group *grp) +{ + int i; + + if (pwd == NULL) { + return 0; + } + if (grp == NULL) { + return 0; + } + + if (pwd->pw_gid == grp->gr_gid) { + return 1; + } + + for (i = 0; (grp->gr_mem != NULL) && (grp->gr_mem[i] != NULL); i++) { + if (strcmp(pwd->pw_name, grp->gr_mem[i]) == 0) { + return 1; + } + } + +#ifdef HAVE_GETGROUPLIST + if (checkgrouplist(pwd->pw_name, pwd->pw_gid, grp->gr_gid)) { + return 1; + } +#endif + + return 0; +} + +int +pam_modutil_user_in_group_nam_nam(pam_handle_t *pamh, + const char *user, const char *group) +{ + struct passwd *pwd; + struct group *grp; + + pwd = pam_modutil_getpwnam(pamh, user); + grp = pam_modutil_getgrnam(pamh, group); + + return pam_modutil_user_in_group_common(pamh, pwd, grp); +} + +int +pam_modutil_user_in_group_nam_gid(pam_handle_t *pamh, + const char *user, gid_t group) +{ + struct passwd *pwd; + struct group *grp; + + pwd = pam_modutil_getpwnam(pamh, user); + grp = pam_modutil_getgrgid(pamh, group); + + return pam_modutil_user_in_group_common(pamh, pwd, grp); +} + +int +pam_modutil_user_in_group_uid_nam(pam_handle_t *pamh, + uid_t user, const char *group) +{ + struct passwd *pwd; + struct group *grp; + + pwd = pam_modutil_getpwuid(pamh, user); + grp = pam_modutil_getgrnam(pamh, group); + + return pam_modutil_user_in_group_common(pamh, pwd, grp); +} + +int +pam_modutil_user_in_group_uid_gid(pam_handle_t *pamh, + uid_t user, gid_t group) +{ + struct passwd *pwd; + struct group *grp; + + pwd = pam_modutil_getpwuid(pamh, user); + grp = pam_modutil_getgrgid(pamh, group); + + return pam_modutil_user_in_group_common(pamh, pwd, grp); +} diff --git a/libpam/pam_modutil_ioloop.c b/libpam/pam_modutil_ioloop.c new file mode 100644 index 0000000..72b5845 --- /dev/null +++ b/libpam/pam_modutil_ioloop.c @@ -0,0 +1,63 @@ +/* + * $Id$ + * + * These functions provide common methods to ensure a complete read or + * write occurs. They handle EINTR and partial read/write returns. + */ + +#include "pam_modutil_private.h" + +#include +#include + +int +pam_modutil_read(int fd, char *buffer, int count) +{ + int block, offset = 0; + + if (count < 0) { + errno = EINVAL; + return -1; + } + + while (count > 0) { + block = read(fd, &buffer[offset], count); + + if (block < 0) { + if (errno == EINTR) continue; + return block; + } + if (block == 0) return offset; + + offset += block; + count -= block; + } + + return offset; +} + +int +pam_modutil_write(int fd, const char *buffer, int count) +{ + int block, offset = 0; + + if (count < 0) { + errno = EINVAL; + return -1; + } + + while (count > 0) { + block = write(fd, &buffer[offset], count); + + if (block < 0) { + if (errno == EINTR) continue; + return block; + } + if (block == 0) return offset; + + offset += block; + count -= block; + } + + return offset; +} diff --git a/libpam/pam_modutil_priv.c b/libpam/pam_modutil_priv.c new file mode 100644 index 0000000..a463e06 --- /dev/null +++ b/libpam/pam_modutil_priv.c @@ -0,0 +1,179 @@ +/* + * $Id$ + * + * This file provides two functions: + * pam_modutil_drop_priv: + * temporarily lower process fs privileges by switching to another uid/gid, + * pam_modutil_regain_priv: + * regain process fs privileges lowered by pam_modutil_drop_priv(). + */ + +#include "pam_modutil_private.h" +#include +#include +#include +#include +#include +#include + +/* + * Two setfsuid() calls in a row are necessary to check + * whether setfsuid() succeeded or not. + */ +static int change_uid(uid_t uid, uid_t *save) +{ + uid_t tmp = setfsuid(uid); + if (save) + *save = tmp; + return (uid_t) setfsuid(uid) == uid ? 0 : -1; +} +static int change_gid(gid_t gid, gid_t *save) +{ + gid_t tmp = setfsgid(gid); + if (save) + *save = tmp; + return (gid_t) setfsgid(gid) == gid ? 0 : -1; +} + +static int cleanup(struct pam_modutil_privs *p) +{ + if (p->allocated) { + p->allocated = 0; + free(p->grplist); + } + p->grplist = NULL; + p->number_of_groups = 0; + return -1; +} + +#define PRIV_MAGIC 0x1004000a +#define PRIV_MAGIC_DONOTHING 0xdead000a + +int pam_modutil_drop_priv(pam_handle_t *pamh, + struct pam_modutil_privs *p, + const struct passwd *pw) +{ + int res; + + if (p->is_dropped) { + pam_syslog(pamh, LOG_CRIT, + "pam_modutil_drop_priv: called with dropped privileges"); + return -1; + } + + /* + * If not root, we can do nothing. + * If switching to root, we have nothing to do. + * That is, in both cases, we do not care. + */ + if (geteuid() != 0 || pw->pw_uid == 0) { + p->is_dropped = PRIV_MAGIC_DONOTHING; + return 0; + } + + if (!p->grplist || p->number_of_groups <= 0) { + pam_syslog(pamh, LOG_CRIT, + "pam_modutil_drop_priv: called without room for supplementary groups"); + return -1; + } + res = getgroups(0, NULL); + if (res < 0) { + pam_syslog(pamh, LOG_ERR, + "pam_modutil_drop_priv: getgroups failed: %m"); + return -1; + } + + p->allocated = 0; + if (res > p->number_of_groups) { + p->grplist = calloc(res, sizeof(gid_t)); + if (!p->grplist) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return cleanup(p); + } + p->allocated = 1; + p->number_of_groups = res; + } + + res = getgroups(p->number_of_groups, p->grplist); + if (res < 0) { + pam_syslog(pamh, LOG_ERR, + "pam_modutil_drop_priv: getgroups failed: %m"); + return cleanup(p); + } + + p->number_of_groups = res; + + /* + * We should care to leave process credentials in consistent state. + * That is, e.g. if change_gid() succeeded but change_uid() failed, + * we should try to restore old gid. + * + * We try to add the supplementary groups on a best-effort + * basis. If it fails, it's not fatal: we fall back to using an + * empty list. + */ + if (initgroups(pw->pw_name, pw->pw_gid)) { + pam_syslog(pamh, LOG_WARNING, + "pam_modutil_drop_priv: initgroups failed: %m"); + + if (setgroups(0, NULL)) { + pam_syslog(pamh, LOG_ERR, + "pam_modutil_drop_priv: setgroups failed: %m"); + return cleanup(p); + } + } + if (change_gid(pw->pw_gid, &p->old_gid)) { + pam_syslog(pamh, LOG_ERR, + "pam_modutil_drop_priv: change_gid failed: %m"); + (void) setgroups(p->number_of_groups, p->grplist); + return cleanup(p); + } + if (change_uid(pw->pw_uid, &p->old_uid)) { + pam_syslog(pamh, LOG_ERR, + "pam_modutil_drop_priv: change_uid failed: %m"); + (void) change_gid(p->old_gid, NULL); + (void) setgroups(p->number_of_groups, p->grplist); + return cleanup(p); + } + + p->is_dropped = PRIV_MAGIC; + return 0; +} + +int pam_modutil_regain_priv(pam_handle_t *pamh, + struct pam_modutil_privs *p) +{ + switch (p->is_dropped) { + case PRIV_MAGIC_DONOTHING: + p->is_dropped = 0; + return 0; + + case PRIV_MAGIC: + break; + + default: + pam_syslog(pamh, LOG_CRIT, + "pam_modutil_regain_priv: called with invalid state"); + return -1; + } + + if (change_uid(p->old_uid, NULL)) { + pam_syslog(pamh, LOG_ERR, + "pam_modutil_regain_priv: change_uid failed: %m"); + return cleanup(p); + } + if (change_gid(p->old_gid, NULL)) { + pam_syslog(pamh, LOG_ERR, + "pam_modutil_regain_priv: change_gid failed: %m"); + return cleanup(p); + } + if (setgroups(p->number_of_groups, p->grplist)) { + pam_syslog(pamh, LOG_ERR, + "pam_modutil_regain_priv: setgroups failed: %m"); + return cleanup(p); + } + + p->is_dropped = 0; + cleanup(p); + return 0; +} diff --git a/libpam/pam_modutil_private.h b/libpam/pam_modutil_private.h new file mode 100644 index 0000000..98a30f6 --- /dev/null +++ b/libpam/pam_modutil_private.h @@ -0,0 +1,24 @@ +#ifndef PAMMODUTIL_PRIVATE_H +#define PAMMODUTIL_PRIVATE_H + +/* + * $Id$ + * + * Copyright (c) 2001 Andrew Morgan + */ + +#include "config.h" + +#include +#include +#include + +#define PWD_INITIAL_LENGTH 0x400 +#define PWD_ABSURD_PWD_LENGTH 0x40001 +#define PWD_LENGTH_SHIFT 4 /* 2^4 == 16 */ + +extern void +pam_modutil_cleanup(pam_handle_t *pamh, void *data, + int error_status); + +#endif /* PAMMODUTIL_PRIVATE_H */ diff --git a/libpam/pam_modutil_sanitize.c b/libpam/pam_modutil_sanitize.c new file mode 100644 index 0000000..1b8af74 --- /dev/null +++ b/libpam/pam_modutil_sanitize.c @@ -0,0 +1,162 @@ +/* + * This file implements the following functions: + * pam_modutil_sanitize_helper_fds: + * redirects standard descriptors, closes all other descriptors. + */ + +#include "pam_modutil_private.h" +#include +#include +#include +#include +#include + +#ifndef CLOSE_RANGE_UNSHARE +#define CLOSE_RANGE_UNSHARE (1U << 1) +#endif /* CLOSE_RANGE_UNSHARE */ + +/* + * Creates a pipe, closes its write end, redirects fd to its read end. + * Returns fd on success, -1 otherwise. + */ +static int +redirect_in_pipe(pam_handle_t *pamh, int fd, const char *name) +{ + int in[2]; + + if (pipe(in) < 0) { + pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m"); + return -1; + } + + close(in[1]); + + if (in[0] == fd) + return fd; + + if (dup2(in[0], fd) != fd) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name); + fd = -1; + } + + close(in[0]); + return fd; +} + +/* + * Opens /dev/null for writing, redirects fd there. + * Returns fd on success, -1 otherwise. + */ +static int +redirect_out_null(pam_handle_t *pamh, int fd, const char *name) +{ + int null = open("/dev/null", O_WRONLY); + + if (null < 0) { + pam_syslog(pamh, LOG_ERR, "open of %s failed: %m", "/dev/null"); + return -1; + } + + if (null == fd) + return fd; + + if (dup2(null, fd) != fd) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", name); + fd = -1; + } + + close(null); + return fd; +} + +static int +redirect_out(pam_handle_t *pamh, enum pam_modutil_redirect_fd mode, + int fd, const char *name) +{ + switch (mode) { + case PAM_MODUTIL_PIPE_FD: + if (redirect_in_pipe(pamh, fd, name) < 0) + return -1; + break; + case PAM_MODUTIL_NULL_FD: + if (redirect_out_null(pamh, fd, name) < 0) + return -1; + break; + case PAM_MODUTIL_IGNORE_FD: + break; + } + return fd; +} + +static void +close_fds_iteratively(void) +{ + /* + * An arbitrary upper limit for the maximum file descriptor number + * returned by RLIMIT_NOFILE. + */ + const int MAX_FD_NO = 65535; + + /* The lower limit is the same as for _POSIX_OPEN_MAX. */ + const int MIN_FD_NO = 20; + + int fd; + struct rlimit rlim; + + if (getrlimit(RLIMIT_NOFILE, &rlim) || rlim.rlim_max > (rlim_t)MAX_FD_NO) + fd = MAX_FD_NO; + else if (rlim.rlim_max < (rlim_t)MIN_FD_NO) + fd = MIN_FD_NO; + else + fd = (int)rlim.rlim_max - 1; + + for (; fd > STDERR_FILENO; --fd) + close(fd); +} + +/* Closes all descriptors after stderr. */ +static void +close_fds(void) +{ +#ifdef HAVE_CLOSE_RANGE + if (close_range(STDERR_FILENO+1, -1U, CLOSE_RANGE_UNSHARE) == 0) + return; +#endif /* HAVE_CLOSE_RANGE */ + + close_fds_iteratively(); +} + +int +pam_modutil_sanitize_helper_fds(pam_handle_t *pamh, + enum pam_modutil_redirect_fd stdin_mode, + enum pam_modutil_redirect_fd stdout_mode, + enum pam_modutil_redirect_fd stderr_mode) +{ + if (stdin_mode != PAM_MODUTIL_IGNORE_FD && + redirect_in_pipe(pamh, STDIN_FILENO, "stdin") < 0) { + return -1; + } + + if (redirect_out(pamh, stdout_mode, STDOUT_FILENO, "stdout") < 0) + return -1; + + /* + * If stderr should not be ignored and + * redirect mode for stdout and stderr are the same, + * optimize by redirecting stderr to stdout. + */ + if (stderr_mode != PAM_MODUTIL_IGNORE_FD && + stdout_mode == stderr_mode) { + if (dup2(STDOUT_FILENO, STDERR_FILENO) != STDERR_FILENO) { + pam_syslog(pamh, LOG_ERR, + "dup2 of %s failed: %m", "stderr"); + return -1; + } + } else { + if (redirect_out(pamh, stderr_mode, STDERR_FILENO, "stderr") < 0) + return -1; + } + + close_fds(); + return 0; +} diff --git a/libpam/pam_modutil_searchkey.c b/libpam/pam_modutil_searchkey.c new file mode 100644 index 0000000..1a497f8 --- /dev/null +++ b/libpam/pam_modutil_searchkey.c @@ -0,0 +1,107 @@ +/* + * This file implements the following functions: + * pam_modutil_search_key: + * lookup a value for key in login.defs file or similar key value format + */ + +#include "pam_private.h" +#include "pam_modutil_private.h" +#include +#include +#include +#include +#include +#ifdef USE_ECONF +#include "pam_econf.h" +#endif + +#ifdef USE_ECONF +#define LOGIN_DEFS "/etc/login.defs" + +#ifndef VENDORDIR +#define VENDORDIR NULL +#endif + +static char * +econf_search_key (const char *name, const char *suffix, const char *key) +{ + econf_file *key_file = NULL; + char *val; + econf_err error; + + error = pam_econf_readconfig (&key_file, VENDORDIR, SYSCONFDIR, name, suffix, + " \t", "#", NULL, NULL); + if (error != ECONF_SUCCESS) { + econf_free (key_file); + return NULL; + } + + if (econf_getStringValue (key_file, NULL, key, &val)) { + econf_free (key_file); + return NULL; + } + + econf_free (key_file); + + return val; +} + +#endif + +/* lookup a value for key in login.defs file or similar key value format */ +char * +pam_modutil_search_key(pam_handle_t *pamh UNUSED, + const char *file_name, + const char *key) +{ + FILE *fp; + char *buf = NULL; + size_t buflen = 0; + char *retval = NULL; + +#ifdef USE_ECONF + if (strcmp (file_name, LOGIN_DEFS) == 0) + return econf_search_key ("login", ".defs", key); +#endif + + fp = fopen(file_name, "r"); + if (NULL == fp) + return NULL; + + while (!feof(fp)) { + char *tmp, *cp; + ssize_t n = getline(&buf, &buflen, fp); + + cp = buf; + + if (n < 1) + break; + if (cp[n - 1] == '\n') + cp[n - 1] = '\0'; + + tmp = strchr(cp, '#'); /* remove comments */ + if (tmp) + *tmp = '\0'; + while (isspace((unsigned char)*cp)) /* remove spaces and tabs */ + ++cp; + if (*cp == '\0') /* ignore empty lines */ + continue; + + tmp = strsep (&cp, " \t="); + if (cp != NULL) + while (isspace((unsigned char)*cp) || *cp == '=') + ++cp; + else + cp = buf + n; /* empty string */ + + if (strcasecmp(tmp, key) == 0) { + retval = strdup(cp); + break; + } + } + fclose(fp); + + free(buf); + + return retval; +} diff --git a/libpam/pam_password.c b/libpam/pam_password.c new file mode 100644 index 0000000..9783dbe --- /dev/null +++ b/libpam/pam_password.c @@ -0,0 +1,61 @@ +/* pam_password.c - PAM Password Management */ + +/* + * $Id$ + */ + +#include "pam_private.h" + +#include +#include + +int pam_chauthtok(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("called.")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + /* applications are not allowed to set these flags */ + if (flags & (PAM_PRELIM_CHECK | PAM_UPDATE_AUTHTOK)) { + pam_syslog (pamh, LOG_ERR, + "PAM_PRELIM_CHECK or PAM_UPDATE_AUTHTOK set by application"); + return PAM_SYSTEM_ERR; + } + + if (pamh->former.choice == PAM_NOT_STACKED) { + _pam_start_timer(pamh); /* we try to make the time for a failure + independent of the time it takes to + fail */ + _pam_sanitize(pamh); + pamh->former.update = PAM_FALSE; + } + + /* first call to check if there will be a problem */ + if (pamh->former.update || + (retval = _pam_dispatch(pamh, flags|PAM_PRELIM_CHECK, + PAM_CHAUTHTOK)) == PAM_SUCCESS) { + D(("completed check ok: former=%d", pamh->former.update)); + pamh->former.update = PAM_TRUE; + retval = _pam_dispatch(pamh, flags|PAM_UPDATE_AUTHTOK, + PAM_CHAUTHTOK); + } + + /* if we completed we should clean up */ + if (retval != PAM_INCOMPLETE) { + _pam_sanitize(pamh); + pamh->former.update = PAM_FALSE; + _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ + D(("exiting %d - %d", retval, pamh->former.choice)); + } else { + D(("will resume when ready")); + } + + return retval; +} diff --git a/libpam/pam_prelude.c b/libpam/pam_prelude.c new file mode 100644 index 0000000..c62e2f2 --- /dev/null +++ b/libpam/pam_prelude.c @@ -0,0 +1,454 @@ +/* + * pam_prelude.c -- prelude reporting + * http://www.prelude-ids.org + * + * (C) Sebastien Tricaud 2005 + */ + +#ifdef PRELUDE + +#include "pam_private.h" + +#include +#include +#include +#include +#include + +#include "pam_prelude.h" + + +#define ANALYZER_CLASS "pam" +#define ANALYZER_MODEL "PAM" +#define ANALYZER_MANUFACTURER "Sebastien Tricaud, http://www.kernel.org/pub/linux/libs/pam/" + +#define DEFAULT_ANALYZER_NAME "PAM" + +static const char * +pam_get_item_service(const pam_handle_t *pamh) +{ + const void *service = NULL; + + pam_get_item(pamh, PAM_SERVICE, &service); + + return service; +} + +static const char * +pam_get_item_user(const pam_handle_t *pamh) +{ + const void *user = NULL; + + pam_get_item(pamh, PAM_USER, &user); + + return user; +} + +static const char * +pam_get_item_user_prompt(const pam_handle_t *pamh) +{ + const void *user_prompt = NULL; + + pam_get_item(pamh, PAM_USER_PROMPT, &user_prompt); + + return user_prompt; +} + +static const char * +pam_get_item_tty(const pam_handle_t *pamh) +{ + const void *tty = NULL; + + pam_get_item(pamh, PAM_TTY, &tty); + + return tty; +} + +static const char * +pam_get_item_ruser(const pam_handle_t *pamh) +{ + const void *ruser = NULL; + + pam_get_item(pamh, PAM_RUSER, &ruser); + + return ruser; +} + +static const char * +pam_get_item_rhost(const pam_handle_t *pamh) +{ + const void *rhost = NULL; + + pam_get_item(pamh, PAM_RHOST, &rhost); + + return rhost; +} + +/* Courteously stolen from prelude-lml */ +static int +generate_additional_data(idmef_alert_t *alert, const char *meaning, + const char *data) +{ + int ret; + prelude_string_t *str; + idmef_additional_data_t *adata; + + ret = idmef_alert_new_additional_data(alert, &adata, -1); + if ( ret < 0 ) + return ret; + + ret = idmef_additional_data_new_meaning(adata, &str); + if ( ret < 0 ) + return ret; + + ret = prelude_string_set_ref(str, meaning); + if ( ret < 0 ) + return ret; + + return idmef_additional_data_set_string_ref(adata, data); +} + +static int +setup_analyzer(const pam_handle_t *pamh, idmef_analyzer_t *analyzer) +{ + int ret; + prelude_string_t *string; + + ret = idmef_analyzer_new_model(analyzer, &string); + if ( ret < 0 ) + goto err; + prelude_string_set_constant(string, ANALYZER_MODEL); + + ret = idmef_analyzer_new_class(analyzer, &string); + if ( ret < 0 ) + goto err; + prelude_string_set_constant(string, ANALYZER_CLASS); + + ret = idmef_analyzer_new_manufacturer(analyzer, &string); + if ( ret < 0 ) + goto err; + prelude_string_set_constant(string, ANALYZER_MANUFACTURER); + + ret = idmef_analyzer_new_version(analyzer, &string); + if ( ret < 0 ) + goto err; + prelude_string_set_constant(string, PAM_VERSION); + + + return 0; + + err: + pam_syslog(pamh, LOG_WARNING, + "%s: IDMEF error: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + + return -1; +} + +static void +pam_alert_prelude(const char *msg, void *data, + pam_handle_t *pamh, int authval) +{ + int ret; + idmef_time_t *clienttime; + idmef_alert_t *alert; + prelude_string_t *str; + idmef_message_t *idmef = NULL; + idmef_classification_t *class; + prelude_client_t *client = (prelude_client_t *)data; + idmef_source_t *source; + idmef_target_t *target; + idmef_user_t *user; + idmef_user_id_t *user_id; + idmef_process_t *process; + idmef_classification_t *classification; + idmef_impact_t *impact; + idmef_assessment_t *assessment; + idmef_node_t *node; + idmef_analyzer_t *analyzer; + + + ret = idmef_message_new(&idmef); + if ( ret < 0 ) + goto err; + + ret = idmef_message_new_alert(idmef, &alert); + if ( ret < 0 ) + goto err; + + ret = idmef_alert_new_classification(alert, &class); + if ( ret < 0 ) + goto err; + + ret = idmef_classification_new_text(class, &str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_new_ref(&str, msg); + if ( ret < 0 ) + goto err; + + idmef_classification_set_text(class, str); + + ret = idmef_time_new_from_gettimeofday(&clienttime); + if ( ret < 0 ) + goto err; + idmef_alert_set_create_time(alert, clienttime); + + idmef_alert_set_analyzer(alert, + idmef_analyzer_ref(prelude_client_get_analyzer(client)), + 0); + + /********** + * SOURCE * + **********/ + ret = idmef_alert_new_source(alert, &source, -1); + if ( ret < 0 ) + goto err; + + /* BEGIN: Sets the user doing authentication stuff */ + ret = idmef_source_new_user(source, &user); + if ( ret < 0 ) + goto err; + idmef_user_set_category(user, IDMEF_USER_CATEGORY_APPLICATION); + + ret = idmef_user_new_user_id(user, &user_id, 0); + if ( ret < 0 ) + goto err; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_ORIGINAL_USER); + + if ( pam_get_item_ruser(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_ruser(pamh)); + if ( ret < 0 ) + goto err; + + idmef_user_id_set_name(user_id, str); + } + /* END */ + /* BEGIN: Adds TTY infos */ + if ( pam_get_item_tty(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_tty(pamh)); + if ( ret < 0 ) + goto err; + + idmef_user_id_set_tty(user_id, str); + } + /* END */ + /* BEGIN: Sets the source node (rhost) */ + ret = idmef_source_new_node(source, &node); + if ( ret < 0 ) + goto err; + idmef_node_set_category(node, IDMEF_NODE_CATEGORY_HOSTS); + + if ( pam_get_item_rhost(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_rhost(pamh)); + if ( ret < 0 ) + goto err; + + idmef_node_set_name(node, str); + } + /* END */ + /* BEGIN: Describe the service */ + ret = idmef_source_new_process(source, &process); + if ( ret < 0 ) + goto err; + idmef_process_set_pid(process, getpid()); + + if ( pam_get_item_service(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_service(pamh)); + if ( ret < 0 ) + goto err; + + idmef_process_set_name(process, str); + } + /* END */ + + /********** + * TARGET * + **********/ + + ret = idmef_alert_new_target(alert, &target, -1); + if ( ret < 0 ) + goto err; + + + /* BEGIN: Sets the target node */ + analyzer = prelude_client_get_analyzer(client); + if ( ! analyzer ) goto err; + + node = idmef_analyzer_get_node(analyzer); + if ( ! node ) goto err; + idmef_target_set_node(target, node); + node = idmef_node_ref(node); + if ( ! node ) goto err; + /* END */ + /* BEGIN: Sets the user doing authentication stuff */ + ret = idmef_target_new_user(target, &user); + if ( ret < 0 ) + goto err; + idmef_user_set_category(user, IDMEF_USER_CATEGORY_APPLICATION); + + ret = idmef_user_new_user_id(user, &user_id, 0); + if ( ret < 0 ) + goto err; + idmef_user_id_set_type(user_id, IDMEF_USER_ID_TYPE_TARGET_USER); + + if ( pam_get_item_user(pamh) ) { + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_get_item_user(pamh)); + if ( ret < 0 ) + goto err; + + idmef_user_id_set_name(user_id, str); + } + /* END */ + /* BEGIN: Short description of the alert */ + ret = idmef_alert_new_classification(alert, &classification); + if ( ret < 0 ) + goto err; + + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, + authval == PAM_SUCCESS ? + "Authentication Success" : "Authentication Failure"); + if ( ret < 0 ) + goto err; + + idmef_classification_set_text(classification, str); + /* END */ + /* BEGIN: Long description of the alert */ + ret = idmef_alert_new_assessment(alert, &assessment); + if ( ret < 0 ) + goto err; + + ret = idmef_assessment_new_impact(assessment, &impact); + if ( ret < 0 ) + goto err; + + ret = prelude_string_new(&str); + if ( ret < 0 ) + goto err; + + ret = prelude_string_set_ref(str, pam_strerror (pamh, authval)); + if ( ret < 0 ) + goto err; + + idmef_impact_set_description(impact, str); + /* END */ + /* BEGIN: Adding additional data */ + if ( pam_get_item_user_prompt(pamh) ) { + ret = generate_additional_data(alert, "Local User Prompt", + pam_get_item_user_prompt(pamh)); + if ( ret < 0 ) + goto err; + } + /* END */ + + prelude_client_send_idmef(client, idmef); + + if ( idmef ) + idmef_message_destroy(idmef); + + return; + err: + pam_syslog(pamh, LOG_WARNING, "%s: IDMEF error: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + + if ( idmef ) + idmef_message_destroy(idmef); + +} + +static int +pam_alert_prelude_init(pam_handle_t *pamh, int authval) +{ + + int ret; + prelude_client_t *client = NULL; + + ret = prelude_init(NULL, NULL); + if ( ret < 0 ) { + pam_syslog(pamh, LOG_WARNING, + "%s: Unable to initialize the Prelude library: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + return -1; + } + + ret = prelude_client_new(&client, DEFAULT_ANALYZER_NAME); + if ( ! client ) { + pam_syslog(pamh, LOG_WARNING, + "%s: Unable to create a prelude client object: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + + return -1; + } + + + ret = setup_analyzer(pamh, prelude_client_get_analyzer(client)); + if ( ret < 0 ) { + pam_syslog(pamh, LOG_WARNING, + "%s: Unable to setup analyzer: %s\n", + prelude_strsource(ret), prelude_strerror(ret)); + + prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_FAILURE); + + return -1; + } + + ret = prelude_client_start(client); + if ( ret < 0 ) { + pam_syslog(pamh, LOG_WARNING, + "%s: Unable to initialize prelude client: %s.\n", + prelude_strsource(ret), prelude_strerror(ret)); + + prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_FAILURE); + + return -1; + } + + pam_alert_prelude("libpam alert" , client, pamh, authval); + + prelude_client_destroy(client, PRELUDE_CLIENT_EXIT_STATUS_SUCCESS); + + return 0; +} + +void +prelude_send_alert(pam_handle_t *pamh, int authval) +{ + + int ret; + + prelude_log_set_flags(PRELUDE_LOG_FLAGS_SYSLOG); + + ret = pam_alert_prelude_init(pamh, authval); + if ( ret < 0 ) + pam_syslog(pamh, LOG_WARNING, "No prelude alert sent"); + + prelude_deinit(); + +} + +#endif /* PRELUDE */ diff --git a/libpam/pam_prelude.h b/libpam/pam_prelude.h new file mode 100644 index 0000000..196b141 --- /dev/null +++ b/libpam/pam_prelude.h @@ -0,0 +1,15 @@ +/* + * pam_prelude.h -- prelude ids reporting + * http://www.prelude-ids.org + * + * (C) Sebastien Tricaud 2005 + */ + +#ifndef _SECURITY_PAM_PRELUDE_H +#define _SECURITY_PAM_PRELUDE_H + +#include + +void prelude_send_alert(pam_handle_t *pamh, int authval); + +#endif /* _SECURITY_PAM_PRELUDE_H */ diff --git a/libpam/pam_private.h b/libpam/pam_private.h new file mode 100644 index 0000000..77dc5a6 --- /dev/null +++ b/libpam/pam_private.h @@ -0,0 +1,361 @@ +/* + * pam_private.h + * + * This is the Linux-PAM Library Private Header. It contains things + * internal to the Linux-PAM library. Things not needed by either an + * application or module. + * + * Please see end of file for copyright. + * + * Creator: Marc Ewing. + * Maintained: CVS + */ + +#ifndef _PAM_PRIVATE_H +#define _PAM_PRIVATE_H + +#include "config.h" + +#include +#include + +#include +#include +#include + +/* the Linux-PAM configuration file */ + +#define PAM_CONFIG "/etc/pam.conf" +#define PAM_CONFIG_D "/etc/pam.d" +#define PAM_CONFIG_DF "/etc/pam.d/%s" +#define PAM_CONFIG_DIST_D "/usr/lib/pam.d" +#define PAM_CONFIG_DIST_DF "/usr/lib/pam.d/%s" +#ifdef VENDORDIR +#define PAM_CONFIG_DIST2_D VENDORDIR"/pam.d" +#define PAM_CONFIG_DIST2_DF VENDORDIR"/pam.d/%s" +#endif + + +#define PAM_DEFAULT_SERVICE "other" /* lower case */ + +#ifdef PAM_LOCKING +/* + * the Linux-PAM lock file. If it exists Linux-PAM will abort. Use it + * to block access to libpam + */ +#define PAM_LOCK_FILE "/var/lock/subsys/PAM" +#endif + +/* components of the pam_handle structure */ + +#define _PAM_INVALID_RETVAL (-1) /* default value for cached_retval */ + +struct handler { + int handler_type; + int (*func)(pam_handle_t *pamh, int flags, int argc, char **argv); + int actions[_PAM_RETURN_VALUES]; + /* set by authenticate, open_session, chauthtok(1st) + consumed by setcred, close_session, chauthtok(2nd) */ + int cached_retval; int *cached_retval_p; + int argc; + char **argv; + struct handler *next; + char *mod_name; + int stack_level; + int grantor; +}; + +#define PAM_HT_MODULE 0 +#define PAM_HT_MUST_FAIL 1 +#define PAM_HT_SUBSTACK 2 +#define PAM_HT_SILENT_MODULE 3 + +struct loaded_module { + char *name; + int type; /* PAM_STATIC_MOD or PAM_DYNAMIC_MOD */ + void *dl_handle; +}; + +#define PAM_MT_DYNAMIC_MOD 0 +#define PAM_MT_STATIC_MOD 1 +#define PAM_MT_FAULTY_MOD 2 + +struct handlers { + struct handler *authenticate; + struct handler *setcred; + struct handler *acct_mgmt; + struct handler *open_session; + struct handler *close_session; + struct handler *chauthtok; +}; + +struct service { + struct loaded_module *module; /* Array of modules */ + int modules_allocated; + int modules_used; + int handlers_loaded; + + struct handlers conf; /* the configured handlers */ + struct handlers other; /* the default handlers */ +}; + +/* + * Environment helper functions + */ + +#define PAM_ENV_CHUNK 10 /* chunks of memory calloc()'d * + * at once */ + +struct pam_environ { + int entries; /* the number of pointers available */ + int requested; /* the number of pointers used: * + * 1 <= requested <= entries */ + char **list; /* the environment storage (a list * + * of pointers to malloc() memory) */ +}; + +#include + +typedef enum { PAM_FALSE, PAM_TRUE } _pam_boolean; + +struct _pam_fail_delay { + _pam_boolean set; + unsigned int delay; + time_t begin; + const void *delay_fn_ptr; +}; + +/* initial state in substack */ +struct _pam_substack_state { + int impression; + int status; +}; + +struct _pam_former_state { +/* this is known and set by _pam_dispatch() */ + int choice; /* which flavor of module function did we call? */ + +/* state info for the _pam_dispatch_aux() function */ + int depth; /* how deep in the stack were we? */ + int impression; /* the impression at that time */ + int status; /* the status before returning incomplete */ + struct _pam_substack_state *substates; /* array of initial substack states */ + +/* state info used by pam_get_user() function */ + int fail_user; + int want_user; + char *prompt; /* saved prompt information */ + +/* state info for the pam_chauthtok() function */ + _pam_boolean update; +}; + +struct pam_handle { + char *authtok; + unsigned caller_is; + struct pam_conv *pam_conversation; + char *oldauthtok; + char *prompt; /* for use by pam_get_user() */ + char *service_name; + char *user; + char *rhost; + char *ruser; + char *tty; + char *xdisplay; + char *authtok_type; /* PAM_AUTHTOK_TYPE */ + struct pam_data *data; + struct pam_environ *env; /* structure to maintain environment list */ + struct _pam_fail_delay fail_delay; /* helper function for easy delays */ + struct pam_xauth_data xauth; /* auth info for X display */ + struct service handlers; + struct _pam_former_state former; /* library state - support for + event driven applications */ + const char *mod_name; /* Name of the module currently executed */ + int mod_argc; /* Number of module arguments */ + char **mod_argv; /* module arguments */ + int choice; /* Which function we call from the module */ + +#ifdef HAVE_LIBAUDIT + int audit_state; /* keep track of reported audit messages */ +#endif + int authtok_verified; + char *confdir; +}; + +/* Values for select arg to _pam_dispatch() */ +#define PAM_NOT_STACKED 0 +#define PAM_AUTHENTICATE 1 +#define PAM_SETCRED 2 +#define PAM_ACCOUNT 3 +#define PAM_OPEN_SESSION 4 +#define PAM_CLOSE_SESSION 5 +#define PAM_CHAUTHTOK 6 + +#define _PAM_ACTION_IS_JUMP(x) ((x) > 0) +#define _PAM_ACTION_IGNORE 0 +#define _PAM_ACTION_OK (-1) +#define _PAM_ACTION_DONE (-2) +#define _PAM_ACTION_BAD (-3) +#define _PAM_ACTION_DIE (-4) +#define _PAM_ACTION_RESET (-5) +/* Add any new entries here. Will need to change ..._UNDEF and then + * need to change pam_tokens.h */ +#define _PAM_ACTION_UNDEF (-6) /* this is treated as an error + ( = _PAM_ACTION_BAD) */ + +#define PAM_SUBSTACK_MAX_LEVEL 16 /* maximum level of substacks */ + +/* character tables for parsing config files */ +extern const char * const _pam_token_actions[-_PAM_ACTION_UNDEF]; +extern const char * const _pam_token_returns[_PAM_RETURN_VALUES+1]; + +/* + * internally defined functions --- these should not be directly + * called by applications or modules + */ +int _pam_dispatch(pam_handle_t *pamh, int flags, int choice); + +/* Free various allocated structures and dlclose() the libs */ +int _pam_free_handlers(pam_handle_t *pamh); + +/* Parse config file, allocate handler structures, dlopen() */ +int _pam_init_handlers(pam_handle_t *pamh); + +/* Set all handler stuff to 0/NULL - called once from pam_start() */ +void _pam_start_handlers(pam_handle_t *pamh); + +/* environment helper functions */ + +/* create the environment structure */ +int _pam_make_env(pam_handle_t *pamh); + +/* delete the environment structure */ +void _pam_drop_env(pam_handle_t *pamh); + +/* these functions deal with failure delays as required by the + authentication modules and application. Their *interface* is likely + to remain the same although their function is hopefully going to + improve */ + +/* reset the timer to no-delay */ +void _pam_reset_timer(pam_handle_t *pamh); + +/* this sets the clock ticking */ +void _pam_start_timer(pam_handle_t *pamh); + +/* this waits for the clock to stop ticking if status != PAM_SUCCESS */ +void _pam_await_timer(pam_handle_t *pamh, int status); + +typedef void (*voidfunc(void))(void); +typedef int (*servicefn)(pam_handle_t *, int, int, char **); + +void *_pam_dlopen (const char *mod_path); +servicefn _pam_dlsym (void *handle, const char *symbol); +void _pam_dlclose (void *handle); +const char *_pam_dlerror (void); + +/* For now we just use a stack and linear search for module data. */ +/* If it becomes apparent that there is a lot of data, it should */ +/* be changed to either a sorted list or a hash table. */ + +struct pam_data { + char *name; + void *data; + void (*cleanup)(pam_handle_t *pamh, void *data, int error_status); + struct pam_data *next; +}; + +void _pam_free_data(pam_handle_t *pamh, int status); + +char *_pam_tokenize(char *from, char **next); + +char *_pam_strdup(const char *s); + +char *_pam_memdup(const char *s, int len); + +size_t _pam_mkargv(const char *s, char ***argv, int *argc); + +void _pam_sanitize(pam_handle_t *pamh); + +void _pam_set_default_control(int *control_array, int default_action); + +void _pam_parse_control(int *control_array, char *tok); + +#define _PAM_SYSTEM_LOG_PREFIX "PAM" + +/* + * XXX - Take care with this. It could confuse the logic of a trailing + * else + */ + +#define IF_NO_PAMH(pamh,ERR) \ +do { \ + if ((pamh) == NULL) { \ + syslog(LOG_ERR, _PAM_SYSTEM_LOG_PREFIX " %s: NULL pam handle passed", __FUNCTION__); \ + return ERR; \ + } \ +} while(0) + +/* + * include some helpful macros + */ + +#include + +/* used to work out where control currently resides (in an application + or in a module) */ + +#define _PAM_CALLED_FROM_MODULE 1 +#define _PAM_CALLED_FROM_APP 2 + +#define __PAM_FROM_MODULE(pamh) ((pamh)->caller_is == _PAM_CALLED_FROM_MODULE) +#define __PAM_FROM_APP(pamh) ((pamh)->caller_is == _PAM_CALLED_FROM_APP) +#define __PAM_TO_MODULE(pamh) \ + do { (pamh)->caller_is = _PAM_CALLED_FROM_MODULE; } while (0) +#define __PAM_TO_APP(pamh) \ + do { (pamh)->caller_is = _PAM_CALLED_FROM_APP; } while (0) + +#ifdef HAVE_LIBAUDIT +extern int _pam_auditlog(pam_handle_t *pamh, int action, int retval, int flags, struct handler *h); +extern int _pam_audit_end(pam_handle_t *pamh, int pam_status); +#endif + +/* + * Copyright (C) 1995 by Red Hat Software, Marc Ewing + * Copyright (c) 1996-8,2001 by Andrew G. Morgan + * + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#endif /* _PAM_PRIVATE_H_ */ diff --git a/libpam/pam_session.c b/libpam/pam_session.c new file mode 100644 index 0000000..a6c54f5 --- /dev/null +++ b/libpam/pam_session.c @@ -0,0 +1,45 @@ +/* pam_session.c - PAM Session Management */ + +/* + * $Id$ + */ + +#include "pam_private.h" + +#include + +int pam_open_session(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("called")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + retval = _pam_dispatch(pamh, flags, PAM_OPEN_SESSION); + + return retval; +} + +int pam_close_session(pam_handle_t *pamh, int flags) +{ + int retval; + + D(("called")); + + IF_NO_PAMH(pamh, PAM_SYSTEM_ERR); + + if (__PAM_FROM_MODULE(pamh)) { + D(("called from module!?")); + return PAM_SYSTEM_ERR; + } + + retval = _pam_dispatch(pamh, flags, PAM_CLOSE_SESSION); + + return retval; + +} diff --git a/libpam/pam_start.c b/libpam/pam_start.c new file mode 100644 index 0000000..97bc35b --- /dev/null +++ b/libpam/pam_start.c @@ -0,0 +1,196 @@ +/* pam_start.c */ + +/* Creator Marc Ewing + * Maintained by AGM + * + * $Id$ + * + */ + +#include "pam_private.h" +#include "pam_i18n.h" + +#include +#include +#include +#include +#include + +static int _pam_start_internal ( + const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + const char *confdir, + pam_handle_t **pamh) +{ + D(("called pam_start: [%s] [%s] [%p] [%p]" + ,service_name, user, pam_conversation, pamh)); + +#if defined HAVE_BINDTEXTDOMAIN && defined ENABLE_NLS + /* Bind text domain to pull in PAM translations for a case where + linux-pam is installed to non-default prefix. + + It is safe to call bindtextdomain() from multiple threads, but it + has a chance to have some overhead. Let's try to do it once (or a + small number of times as `bound_text_domain` is not protected by + a lock. */ + static int bound_text_domain = 0; + if (!bound_text_domain) { + bound_text_domain = 1; + bindtextdomain(PACKAGE, LOCALEDIR); + } +#endif + + if (pamh == NULL) { + pam_syslog(NULL, LOG_CRIT, + "pam_start: invalid argument: pamh == NULL"); + return (PAM_SYSTEM_ERR); + } + + if (service_name == NULL) { + pam_syslog(NULL, LOG_CRIT, + "pam_start: invalid argument: service == NULL"); + return (PAM_SYSTEM_ERR); + } + + if (pam_conversation == NULL) { + pam_syslog(NULL, LOG_CRIT, + "pam_start: invalid argument: conv == NULL"); + return (PAM_SYSTEM_ERR); + } + + if ((*pamh = calloc(1, sizeof(**pamh))) == NULL) { + pam_syslog(NULL, LOG_CRIT, "pam_start: calloc failed for *pamh"); + return (PAM_BUF_ERR); + } + + /* All service names should be files below /etc/pam.d and nothing + else. Forbid paths. */ + if (strrchr(service_name, '/') != NULL) + service_name = strrchr(service_name, '/') + 1; + + /* Mark the caller as the application - permission to do certain + things is limited to a module or an application */ + + __PAM_TO_APP(*pamh); + + if (((*pamh)->service_name = _pam_strdup(service_name)) == NULL) { + pam_syslog(*pamh, LOG_CRIT, + "pam_start: _pam_strdup failed for service name"); + _pam_drop(*pamh); + return (PAM_BUF_ERR); + } else { + char *tmp; + + for (tmp=(*pamh)->service_name; *tmp; ++tmp) + *tmp = tolower((unsigned char)*tmp); /* require lower case */ + } + + if (user) { + if (((*pamh)->user = _pam_strdup(user)) == NULL) { + pam_syslog(*pamh, LOG_CRIT, + "pam_start: _pam_strdup failed for user"); + _pam_drop((*pamh)->service_name); + _pam_drop(*pamh); + return (PAM_BUF_ERR); + } + } else + (*pamh)->user = NULL; + + if (confdir) { + if (((*pamh)->confdir = _pam_strdup(confdir)) == NULL) { + pam_syslog(*pamh, LOG_CRIT, + "pam_start: _pam_strdup failed for confdir"); + _pam_drop((*pamh)->service_name); + _pam_drop((*pamh)->user); + _pam_drop(*pamh); + return (PAM_BUF_ERR); + } + } else + (*pamh)->confdir = NULL; + + (*pamh)->tty = NULL; + (*pamh)->prompt = NULL; /* prompt for pam_get_user() */ + (*pamh)->ruser = NULL; + (*pamh)->rhost = NULL; + (*pamh)->authtok = NULL; + (*pamh)->oldauthtok = NULL; + (*pamh)->fail_delay.delay_fn_ptr = NULL; + (*pamh)->former.choice = PAM_NOT_STACKED; + (*pamh)->former.substates = NULL; +#ifdef HAVE_LIBAUDIT + (*pamh)->audit_state = 0; +#endif + (*pamh)->xdisplay = NULL; + (*pamh)->authtok_type = NULL; + (*pamh)->authtok_verified = 0; + memset (&((*pamh)->xauth), 0, sizeof ((*pamh)->xauth)); + + if (((*pamh)->pam_conversation = (struct pam_conv *) + malloc(sizeof(struct pam_conv))) == NULL) { + pam_syslog(*pamh, LOG_CRIT, "pam_start: malloc failed for pam_conv"); + _pam_drop((*pamh)->service_name); + _pam_drop((*pamh)->user); + _pam_drop((*pamh)->confdir); + _pam_drop(*pamh); + return (PAM_BUF_ERR); + } else { + memcpy((*pamh)->pam_conversation, pam_conversation, + sizeof(struct pam_conv)); + } + + (*pamh)->data = NULL; + if ( _pam_make_env(*pamh) != PAM_SUCCESS ) { + pam_syslog(*pamh,LOG_ERR,"pam_start: failed to initialize environment"); + _pam_drop((*pamh)->pam_conversation); + _pam_drop((*pamh)->service_name); + _pam_drop((*pamh)->user); + _pam_drop((*pamh)->confdir); + _pam_drop(*pamh); + return PAM_ABORT; + } + + _pam_reset_timer(*pamh); /* initialize timer support */ + + _pam_start_handlers(*pamh); /* cannot fail */ + + /* According to the SunOS man pages, loading modules and resolving + * symbols happens on the first call from the application. */ + + if ( _pam_init_handlers(*pamh) != PAM_SUCCESS ) { + pam_syslog(*pamh, LOG_ERR, "pam_start: failed to initialize handlers"); + _pam_free_handlers(*pamh); + _pam_drop_env(*pamh); /* purge the environment */ + _pam_drop((*pamh)->pam_conversation); + _pam_drop((*pamh)->service_name); + _pam_drop((*pamh)->user); + _pam_drop((*pamh)->confdir); + _pam_drop(*pamh); + return PAM_ABORT; + } + + D(("exiting successfully")); + + return PAM_SUCCESS; +} + +int pam_start_confdir ( + const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + const char *confdir, + pam_handle_t **pamh) +{ + return _pam_start_internal(service_name, user, pam_conversation, + confdir, pamh); +} + +int pam_start ( + const char *service_name, + const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh) +{ + return _pam_start_internal(service_name, user, pam_conversation, + NULL, pamh); +} diff --git a/libpam/pam_strerror.c b/libpam/pam_strerror.c new file mode 100644 index 0000000..37f1e3b --- /dev/null +++ b/libpam/pam_strerror.c @@ -0,0 +1,107 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pam_private.h" +#include "pam_i18n.h" + +const char *pam_strerror(pam_handle_t *pamh UNUSED, int errnum) +{ + switch (errnum) { + case PAM_SUCCESS: + return _("Success"); + case PAM_ABORT: + return _("Critical error - immediate abort"); + case PAM_OPEN_ERR: + return _("Failed to load module"); + case PAM_SYMBOL_ERR: + return _("Symbol not found"); + case PAM_SERVICE_ERR: + return _("Error in service module"); + case PAM_SYSTEM_ERR: + return _("System error"); + case PAM_BUF_ERR: + return _("Memory buffer error"); + case PAM_PERM_DENIED: + return _("Permission denied"); + case PAM_AUTH_ERR: + return _("Authentication failure"); + case PAM_CRED_INSUFFICIENT: + return _("Insufficient credentials to access authentication data"); + case PAM_AUTHINFO_UNAVAIL: + return _("Authentication service cannot retrieve authentication info"); + case PAM_USER_UNKNOWN: + return _("User not known to the underlying authentication module"); + case PAM_MAXTRIES: + return _("Have exhausted maximum number of retries for service"); + case PAM_NEW_AUTHTOK_REQD: + return _("Authentication token is no longer valid; new one required"); + case PAM_ACCT_EXPIRED: + return _("User account has expired"); + case PAM_SESSION_ERR: + return _("Cannot make/remove an entry for the specified session"); + case PAM_CRED_UNAVAIL: + return _("Authentication service cannot retrieve user credentials"); + case PAM_CRED_EXPIRED: + return _("User credentials expired"); + case PAM_CRED_ERR: + return _("Failure setting user credentials"); + case PAM_NO_MODULE_DATA: + return _("No module specific data is present"); + case PAM_BAD_ITEM: + return _("Bad item passed to pam_*_item()"); + case PAM_CONV_ERR: + return _("Conversation error"); + case PAM_AUTHTOK_ERR: + return _("Authentication token manipulation error"); + case PAM_AUTHTOK_RECOVERY_ERR: + return _("Authentication information cannot be recovered"); + case PAM_AUTHTOK_LOCK_BUSY: + return _("Authentication token lock busy"); + case PAM_AUTHTOK_DISABLE_AGING: + return _("Authentication token aging disabled"); + case PAM_TRY_AGAIN: + return _("Failed preliminary check by password service"); + case PAM_IGNORE: + return _("The return value should be ignored by PAM dispatch"); + case PAM_MODULE_UNKNOWN: + return _("Module is unknown"); + case PAM_AUTHTOK_EXPIRED: + return _("Authentication token expired"); + case PAM_CONV_AGAIN: + return _("Conversation is waiting for event"); + case PAM_INCOMPLETE: + return _("Application needs to call libpam again"); + } + + return _("Unknown PAM error"); +} diff --git a/libpam/pam_syslog.c b/libpam/pam_syslog.c new file mode 100644 index 0000000..e67f713 --- /dev/null +++ b/libpam/pam_syslog.c @@ -0,0 +1,113 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pam_private.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef LOG_AUTHPRIV +#define LOG_AUTHPRIV LOG_AUTH +#endif + +static const char * +_pam_choice2str (int choice) +{ + switch (choice) + { + case PAM_AUTHENTICATE: + return "auth"; + case PAM_SETCRED: + return "setcred"; + case PAM_ACCOUNT: + return "account"; + case PAM_OPEN_SESSION: + case PAM_CLOSE_SESSION: + return "session"; + case PAM_CHAUTHTOK: + return "chauthtok"; + } + return ""; +} + +void +pam_vsyslog (const pam_handle_t *pamh, int priority, + const char *fmt, va_list args) +{ + char *msgbuf1 = NULL, *msgbuf2 = NULL; + int save_errno = errno; + + if (pamh && pamh->mod_name) + { + if (asprintf (&msgbuf1, "%s(%s:%s):", pamh->mod_name, + pamh->service_name?pamh->service_name:"", + _pam_choice2str (pamh->choice)) < 0) + { + syslog (LOG_AUTHPRIV|LOG_ERR, "asprintf: %m"); + return; + } + } + + errno = save_errno; + if (vasprintf (&msgbuf2, fmt, args) < 0) + { + syslog (LOG_AUTHPRIV|LOG_ERR, "vasprintf: %m"); + _pam_drop (msgbuf1); + return; + } + + errno = save_errno; + syslog (LOG_AUTHPRIV|priority, "%s %s", + (msgbuf1 ? msgbuf1 : _PAM_SYSTEM_LOG_PREFIX), msgbuf2); + + _pam_drop (msgbuf1); + _pam_drop (msgbuf2); +} + +void +pam_syslog (const pam_handle_t *pamh, int priority, + const char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + pam_vsyslog (pamh, priority, fmt, args); + va_end (args); +} diff --git a/libpam/pam_tokens.h b/libpam/pam_tokens.h new file mode 100644 index 0000000..1412fa3 --- /dev/null +++ b/libpam/pam_tokens.h @@ -0,0 +1,112 @@ +/* + * pam_tokens.h + * + * $Id$ + * + * This is a Linux-PAM Library Private Header file. It contains tokens + * that are used when we parse the configuration file(s). + * + * Please see end of file for copyright. + * + * Creator: Andrew Morgan. + * + */ + +#ifndef _PAM_TOKENS_H +#define _PAM_TOKENS_H + +/* an array of actions */ + +#ifndef LIBPAM_COMPILE +static +#endif +const char * const _pam_token_actions[-_PAM_ACTION_UNDEF] = { + "ignore", /* 0 */ + "ok", /* -1 */ + "done", /* -2 */ + "bad", /* -3 */ + "die", /* -4 */ + "reset", /* -5 */ +}; + +/* an array of possible return values */ + +#ifndef LIBPAM_COMPILE +static +#endif +const char * const _pam_token_returns[_PAM_RETURN_VALUES+1] = { + "success", /* 0 */ + "open_err", /* 1 */ + "symbol_err", /* 2 */ + "service_err", /* 3 */ + "system_err", /* 4 */ + "buf_err", /* 5 */ + "perm_denied", /* 6 */ + "auth_err", /* 7 */ + "cred_insufficient", /* 8 */ + "authinfo_unavail", /* 9 */ + "user_unknown", /* 10 */ + "maxtries", /* 11 */ + "new_authtok_reqd", /* 12 */ + "acct_expired", /* 13 */ + "session_err", /* 14 */ + "cred_unavail", /* 15 */ + "cred_expired", /* 16 */ + "cred_err", /* 17 */ + "no_module_data", /* 18 */ + "conv_err", /* 19 */ + "authtok_err", /* 20 */ + "authtok_recover_err", /* 21 */ + "authtok_lock_busy", /* 22 */ + "authtok_disable_aging", /* 23 */ + "try_again", /* 24 */ + "ignore", /* 25 */ + "abort", /* 26 */ + "authtok_expired", /* 27 */ + "module_unknown", /* 28 */ + "bad_item", /* 29 */ + "conv_again", /* 30 */ + "incomplete", /* 31 */ +/* add new return codes here */ + "default" /* this is _PAM_RETURN_VALUES and indicates + the default return action */ +}; + +/* + * Copyright (C) 1998,2001 Andrew G. Morgan + * + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#endif /* _PAM_PRIVATE_H_ */ diff --git a/libpam/pam_vprompt.c b/libpam/pam_vprompt.c new file mode 100644 index 0000000..a16f600 --- /dev/null +++ b/libpam/pam_vprompt.c @@ -0,0 +1,113 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pam_private.h" +#include "pam_inline.h" + +#include +#include +#include +#include +#include + +#include +#include + +int +pam_vprompt (pam_handle_t *pamh, int style, char **response, + const char *fmt, va_list args) +{ + struct pam_message msg; + struct pam_response *pam_resp = NULL; + const struct pam_message *pmsg; + const struct pam_conv *conv; + const void *convp; + char *msgbuf; + int retval; + + if (response) + *response = NULL; + + retval = pam_get_item (pamh, PAM_CONV, &convp); + if (retval != PAM_SUCCESS) + return retval; + conv = convp; + if (conv == NULL || conv->conv == NULL) + { + pam_syslog (pamh, LOG_ERR, "no conversation function"); + return PAM_SYSTEM_ERR; + } + + if (vasprintf (&msgbuf, fmt, args) < 0) + { + pam_syslog (pamh, LOG_ERR, "vasprintf: %m"); + return PAM_BUF_ERR; + } + + msg.msg_style = style; + msg.msg = msgbuf; + pmsg = &msg; + + retval = conv->conv (1, &pmsg, &pam_resp, conv->appdata_ptr); + if (retval != PAM_SUCCESS && pam_resp != NULL) + pam_syslog(pamh, LOG_WARNING, + "unexpected response from failed conversation function"); + if (response) + *response = pam_resp == NULL ? NULL : pam_resp->resp; + else if (pam_resp && pam_resp->resp) + { + pam_overwrite_string (pam_resp->resp); + _pam_drop (pam_resp->resp); + } + pam_overwrite_string (msgbuf); + _pam_drop (pam_resp); + free (msgbuf); + if (retval != PAM_SUCCESS) + pam_syslog (pamh, LOG_ERR, "conversation failed"); + + return retval; +} + +int +pam_prompt (pam_handle_t *pamh, int style, char **response, + const char *fmt, ...) +{ + va_list args; + int retval; + + va_start (args, fmt); + retval = pam_vprompt (pamh, style, response, fmt, args); + va_end (args); + + return retval; +} diff --git a/libpam_internal/include/pam_econf.h b/libpam_internal/include/pam_econf.h new file mode 100644 index 0000000..ebba659 --- /dev/null +++ b/libpam_internal/include/pam_econf.h @@ -0,0 +1,22 @@ +/* pam_econf.h -- routines to parse configuration files with libeconf */ + +#ifndef PAM_ECONF_H +#define PAM_ECONF_H + +#ifdef USE_ECONF + +#include + +econf_err pam_econf_readconfig(econf_file **key_file, + const char *usr_conf_dir, + const char *etc_conf_dir, + const char *config_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename, const void *data), + const void *callback_data); + +#endif /* USE_ECONF */ + +#endif /* PAM_ECONF_H */ diff --git a/libpam_internal/include/pam_line.h b/libpam_internal/include/pam_line.h new file mode 100644 index 0000000..70a5c48 --- /dev/null +++ b/libpam_internal/include/pam_line.h @@ -0,0 +1,26 @@ +/* pam_line.h -- routine to parse configuration lines */ + +#ifndef PAM_LINE_H +#define PAM_LINE_H + +#include "pam_inline.h" + +#include +#include +#include + +struct pam_line_buffer { + char *assembled; + char *chunk; + size_t chunk_size; + size_t len; + size_t size; +}; + +void _pam_line_buffer_clear(struct pam_line_buffer *buffer); + +void _pam_line_buffer_init(struct pam_line_buffer *buffer); + +int _pam_line_assemble(FILE *f, struct pam_line_buffer *buffer, char repl); + +#endif /* PAM_LINE_H */ diff --git a/libpam_internal/meson.build b/libpam_internal/meson.build new file mode 100644 index 0000000..5d03877 --- /dev/null +++ b/libpam_internal/meson.build @@ -0,0 +1,19 @@ +libpam_internal_src = [ + 'pam_debug.c', + 'pam_econf.c', + 'pam_line.c', +] + +libpam_internal_inc = include_directories('include') + +libpam_internal = static_library( + 'pam_internal', + sources: libpam_internal_src, + include_directories: [libpam_internal_inc, libpam_inc], + dependencies: libeconf, +) + +libpam_internal_dep = declare_dependency( + include_directories: [libpam_internal_inc], + link_with: [libpam_internal], +) diff --git a/libpam_internal/pam_debug.c b/libpam_internal/pam_debug.c new file mode 100644 index 0000000..e4608f4 --- /dev/null +++ b/libpam_internal/pam_debug.c @@ -0,0 +1,21 @@ +/* + * This provides the necessary functions to do debugging in PAM. + * Cristian Gafton + */ + +#include "config.h" + +#ifdef PAM_DEBUG + +#include "security/_pam_macros.h" + +#undef PAM_MACROS_H +#undef PAM_NO_HEADER_FUNCTIONS +#define PAM_DEBUG_C 1 +#include "security/_pam_macros.h" + +#else + +extern int ISO_C_forbids_an_empty_translation_unit; + +#endif /* PAM_DEBUG */ diff --git a/libpam_internal/pam_econf.c b/libpam_internal/pam_econf.c new file mode 100644 index 0000000..b3b9874 --- /dev/null +++ b/libpam_internal/pam_econf.c @@ -0,0 +1,60 @@ +/* pam_econf.c -- routines to parse configuration files with libeconf */ + +#include "config.h" + +#ifdef USE_ECONF + +#include +#include +#include "pam_econf.h" + +econf_err pam_econf_readconfig(econf_file **key_file, + const char *usr_conf_dir, + const char *etc_conf_dir, + const char *config_name, + const char *config_suffix, + const char *delim, + const char *comment, + bool (*callback)(const char *filename, const void *data), + const void *callback_data) +{ + econf_err ret = ECONF_SUCCESS; + D(("Read configuration from directory %s and %s", etc_conf_dir, usr_conf_dir)); + +#ifdef HAVE_ECONF_READCONFIG + + char *parsing_dirs = NULL; + if (asprintf(&parsing_dirs, "PARSING_DIRS=%s:%s", usr_conf_dir, etc_conf_dir) < 0) { + ret = ECONF_NOMEM; + parsing_dirs = NULL; + } + if (ret == ECONF_SUCCESS) + ret = econf_newKeyFile_with_options(key_file, parsing_dirs); + if (ret == ECONF_SUCCESS) + ret = econf_readConfigWithCallback(key_file, + NULL, + usr_conf_dir, + config_name, + config_suffix, + delim, + comment, + callback, callback_data); + free(parsing_dirs); + +#else + + ret = econf_readDirsWithCallback(key_file, + usr_conf_dir, + etc_conf_dir, + config_name, + config_suffix, + delim, + comment, + callback, callback_data); + +#endif + + return ret; +} + +#endif /* USE_ECONF */ diff --git a/libpam_internal/pam_line.c b/libpam_internal/pam_line.c new file mode 100644 index 0000000..044b4d7 --- /dev/null +++ b/libpam_internal/pam_line.c @@ -0,0 +1,253 @@ +/* pam_line.c -- routine to parse configuration lines */ + +#include "config.h" + +#include "security/_pam_macros.h" +#include "pam_line.h" + +static int _pam_line_buffer_add(struct pam_line_buffer *buffer, char *start, + char *end) +{ + size_t len = end - start; + + D(("assembled: [%zu/%zu] '%s', adding [%zu] '%s'", + buffer->len, buffer->size, + buffer->assembled == NULL ? "" : buffer->assembled, len, start)); + + if (start == end) + return 0; + + if (buffer->assembled == NULL && buffer->chunk == start) { + /* no extra allocation needed, just move chunk to assembled */ + buffer->assembled = buffer->chunk; + buffer->len = len; + buffer->size = buffer->chunk_size; + + buffer->chunk = NULL; + buffer->chunk_size = 0; + + D(("exiting with quick exchange")); + return 0; + } + + if (buffer->len + len + 1 > buffer->size) { + size_t size; + char *p; + + size = buffer->len + len + 1; + if ((p = realloc(buffer->assembled, size)) == NULL) + return -1; + + buffer->assembled = p; + buffer->size = size; + } + + memcpy(buffer->assembled + buffer->len, start, len); + buffer->len += len; + buffer->assembled[buffer->len] = '\0'; + + D(("exiting")); + return 0; +} + +static inline int _pam_line_buffer_add_eol(struct pam_line_buffer *buffer, + char *start, char *end) +{ + if (buffer->assembled != NULL || (*start != '\0' && *start != '\n')) + return _pam_line_buffer_add(buffer, start, end); + return 0; +} + +void _pam_line_buffer_clear(struct pam_line_buffer *buffer) +{ + pam_overwrite_n(buffer->assembled, buffer->size); + _pam_drop(buffer->assembled); + pam_overwrite_n(buffer->chunk, buffer->chunk_size); + _pam_drop(buffer->chunk); + buffer->chunk_size = 0; + buffer->len = 0; + buffer->size = 0; +} + +void _pam_line_buffer_init(struct pam_line_buffer *buffer) +{ + buffer->assembled = NULL; + buffer->chunk = NULL; + _pam_line_buffer_clear(buffer); +} + +static void _pam_line_buffer_purge(struct pam_line_buffer *buffer) +{ + pam_overwrite_n(buffer->chunk, buffer->chunk_size); + _pam_drop(buffer->chunk); + buffer->chunk_size = 0; +} + +static void _pam_line_buffer_shift(struct pam_line_buffer *buffer) +{ + if (buffer->assembled == NULL) + return; + + _pam_line_buffer_purge(buffer); + buffer->chunk = buffer->assembled; + buffer->chunk_size = buffer->size; + + buffer->assembled = NULL; + buffer->size = 0; + buffer->len = 0; +} + +static inline int _pam_line_buffer_valid(struct pam_line_buffer *buffer) +{ + return buffer->assembled != NULL && *buffer->assembled != '\0'; +} + +/* + * Trim string to relevant parts of a configuration line. + * + * Preceding whitespaces are skipped and comment (#) marks the end of + * configuration line. + * + * Returns start of configuration line. + */ +static inline char *_pam_str_trim(char *str) +{ + /* skip leading spaces */ + str += strspn(str, " \t"); + /* + * we are only interested in characters before the first '#' + * character + */ + str[strcspn(str, "#")] = '\0'; + + return str; +} + +/* + * Remove escaped newline from end of string. + * + * Configuration lines may span across multiple lines in a file + * by ending a line with a backslash (\). + * + * If an escaped newline is encountered, the backslash will be + * replaced with "repl" and the newline itself removed. + * Then the variable "end" will point to the new end of line. + * + * Returns 0 if escaped newline was found and replaced, 1 otherwise. + */ +static inline int _pam_str_unescnl(char *start, char **end, char repl) +{ + int ret = 1; + char *p = *end; + + /* + * Check for backslash by scanning back from the end of + * the entered line, the '\n' should be included since + * normally a line is terminated with this character. + */ + while (p > start && ((*--p == ' ') || (*p == '\t') || (*p == '\n'))) + ; + if (*p == '\\') { + *p = repl; /* replace backslash with replacement char */ + if (repl != '\0') { + *++p = '\0'; /* truncate the line here if repl is not NUL */ + } + *end = p; + ret = 0; + } + + return ret; +} + +/* + * Prepare line from file for configuration line parsing. + * + * A configuration line may span across multiple lines in a file. + * Remove comments and skip preceding whitespaces. + * + * Returns 0 if line spans across multiple lines, 1 if + * end of line is encountered. + */ +static inline int _pam_str_prepare(char *line, ssize_t len, + char **start, char **end, char repl) +{ + int ret; + + *start = line; + *end = line + len; + + ret = _pam_str_unescnl(*start, end, repl) || strchr(*start, '#') != NULL; + + *start = _pam_str_trim(*start); + + return ret; +} + +/* + * This is where we read a line of the PAM config file. The line may be + * preceded by lines of comments and also extended with "\\\n" + * + * The "repl" argument is used as replacement char for the backslash used + * in newline escaping, i.e. in "\\\n". + * + * Returns 0 on EOF, 1 on successful line parsing, or -1 on error. + */ +int _pam_line_assemble(FILE *f, struct pam_line_buffer *buffer, char repl) +{ + int ret = 0; + + /* loop broken with a 'break' when a non-'\\n' ended line is read */ + + D(("called.")); + + _pam_line_buffer_shift(buffer); + + for (;;) { + char *start, *end; + ssize_t n; + int eol; + + if ((n = getline(&buffer->chunk, &buffer->chunk_size, f)) == -1) { + if (ret) { + /* Incomplete read */ + ret = -1; + } else { + /* EOF */ + ret = 0; + } + break; + } + + eol = _pam_str_prepare(buffer->chunk, n, &start, &end, repl); + + if (eol) { + if (_pam_line_buffer_add_eol(buffer, start, end)) { + ret = -1; + break; + } + if (_pam_line_buffer_valid(buffer)) { + /* Successfully parsed a line */ + ret = 1; + break; + } + /* Start parsing next line */ + _pam_line_buffer_shift(buffer); + ret = 0; + } else { + /* Configuration line spans across multiple lines in file */ + if (_pam_line_buffer_add(buffer, start, end)) { + ret = -1; + break; + } + /* Keep parsing line */ + ret = 1; + } + } + + if (ret == 1) + _pam_line_buffer_purge(buffer); + else + _pam_line_buffer_clear(buffer); + + return ret; +} diff --git a/libpam_misc/help_env.c b/libpam_misc/help_env.c new file mode 100644 index 0000000..ad7d519 --- /dev/null +++ b/libpam_misc/help_env.c @@ -0,0 +1,89 @@ +/* + * $Id$ + * + * This file was written by Andrew G. Morgan + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include "pam_inline.h" + +/* + * This function should be used to carefully dispose of the copied + * environment. + * + * usage: env = pam_misc_drop_env(env); + */ + +char **pam_misc_drop_env(char **dump) +{ + size_t i; + + for (i=0; dump[i] != NULL; ++i) { + D(("dump[%zu]=`%s'", i, dump[i])); + pam_overwrite_string(dump[i]); + _pam_drop(dump[i]); + } + _pam_drop(dump); + + return NULL; +} + +/* + * This function takes the supplied environment and uploads it to be + * the PAM one. + */ + +int pam_misc_paste_env(pam_handle_t *pamh, const char * const * user_env) +{ + for (; user_env && *user_env; ++user_env) { + int retval; + + D(("uploading: %s", *user_env)); + retval = pam_putenv(pamh, *user_env); + if (retval != PAM_SUCCESS) { + D(("error setting %s: %s", *user_env, pam_strerror(pamh,retval))); + return retval; + } + } + D(("done.")); + return PAM_SUCCESS; +} + +/* + * This is a wrapper to make pam behave in the way that setenv() does. + */ + +int pam_misc_setenv(pam_handle_t *pamh, const char *name + , const char *value, int readonly) +{ + char *tmp; + int retval; + + if (readonly) { + const char *etmp; + + /* we check if the variable is there already */ + etmp = pam_getenv(pamh, name); + if (etmp != NULL) { + D(("failed to set readonly variable: %s", name)); + return PAM_PERM_DENIED; /* not allowed to overwrite */ + } + } + if (asprintf(&tmp, "%s=%s", name, value) >= 0) { + D(("pam_putt()ing: %s", tmp)); + retval = pam_putenv(pamh, tmp); + pam_overwrite_string(tmp); /* purge */ + _pam_drop(tmp); /* forget */ + } else { + D(("malloc failure")); + retval = PAM_BUF_ERR; + } + + return retval; +} diff --git a/libpam_misc/include/security/meson.build b/libpam_misc/include/security/meson.build new file mode 100644 index 0000000..4330915 --- /dev/null +++ b/libpam_misc/include/security/meson.build @@ -0,0 +1 @@ +install_headers('pam_misc.h', install_dir: includedir) diff --git a/libpam_misc/include/security/pam_misc.h b/libpam_misc/include/security/pam_misc.h new file mode 100644 index 0000000..fca2422 --- /dev/null +++ b/libpam_misc/include/security/pam_misc.h @@ -0,0 +1,52 @@ +/* $Id$ */ + +#ifndef __PAMMISC_H +#define __PAMMISC_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* include some useful macros */ + +#include + +/* functions defined in pam_misc.* libraries */ + +extern int misc_conv(int num_msg, const struct pam_message **msgm, + struct pam_response **response, void *appdata_ptr); + +#include + +extern time_t pam_misc_conv_warn_time; /* time that we should warn user */ +extern time_t pam_misc_conv_die_time; /* cut-off time for input */ +extern const char *pam_misc_conv_warn_line; /* warning notice */ +extern const char *pam_misc_conv_die_line; /* cut-off remark */ +extern int pam_misc_conv_died; /* 1 = cut-off time reached (0 not) */ +extern int (*pam_binary_handler_fn)(void *appdata, pamc_bp_t *prompt_p); +extern void (*pam_binary_handler_free)(void *appdata, pamc_bp_t *prompt_p); +/* + * Environment helper functions + */ + +/* transcribe given environment (to pam) */ +extern int pam_misc_paste_env(pam_handle_t *pamh + , const char * const * user_env); + +/* delete environment as obtained from (pam_getenvlist) */ +extern char **pam_misc_drop_env(char **env); + +/* provide something like the POSIX setenv function for the (Linux-)PAM + * environment. */ + +extern int pam_misc_setenv(pam_handle_t *pamh, const char *name + , const char *value, int readonly); + +#ifdef __cplusplus +} +#endif /* def __cplusplus */ + +#endif /* ndef __PAMMISC_H */ diff --git a/libpam_misc/libpam_misc.map b/libpam_misc/libpam_misc.map new file mode 100644 index 0000000..2f28d5c --- /dev/null +++ b/libpam_misc/libpam_misc.map @@ -0,0 +1,17 @@ +LIBPAM_MISC_1.0 { + global: + misc_conv; + pam_misc_conv_warn_time; + pam_misc_conv_die_time; + pam_misc_conv_warn_line; + pam_misc_conv_die_line; + pam_misc_conv_died; + pam_binary_handler_fn; + pam_binary_handler_free; + pam_misc_paste_env; + pam_misc_drop_env; + pam_misc_setenv; + + local: + *; +}; diff --git a/libpam_misc/meson.build b/libpam_misc/meson.build new file mode 100644 index 0000000..e0bb30f --- /dev/null +++ b/libpam_misc/meson.build @@ -0,0 +1,40 @@ +subdir('include/security') + +libpam_misc_src = [ + 'help_env.c', + 'misc_conv.c', +] + +libpam_misc_inc = include_directories('include') + +libpam_misc_version = '0.82.1' +libpam_misc_map = 'libpam_misc.map' +libpam_misc_map_path = meson.current_source_dir() / libpam_misc_map + +libpam_misc_link_deps = [libpam_misc_map] +libpam_misc_link_args = ['-Wl,--version-script=' + libpam_misc_map_path] + +libpam_misc = shared_library( + 'pam_misc', + sources: libpam_misc_src, + include_directories: [libpam_misc_inc, libpamc_inc], + dependencies: [libpam_internal_dep, libpam_dep], + link_depends: libpam_misc_link_deps, + link_args: libpam_misc_link_args, + version: libpam_misc_version, + install: true, +) + +libpam_misc_dep = declare_dependency( + include_directories: [libpam_misc_inc], + link_with: [libpam_misc], +) + +pkgconfig.generate( + libpam_misc, + description: 'Miscellaneous functions that make the job of writing PAM-aware applications easier.', + name: 'pam_misc', + filebase: 'pam_misc', + version: meson.project_version(), + url: 'http://www.linux-pam.org/' +) diff --git a/libpam_misc/misc_conv.c b/libpam_misc/misc_conv.c new file mode 100644 index 0000000..fa3848e --- /dev/null +++ b/libpam_misc/misc_conv.c @@ -0,0 +1,404 @@ +/* + * A generic conversation function for text based applications + * + * Written by Andrew Morgan + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pam_inline.h" +#include "pam_i18n.h" + +#define INPUTSIZE PAM_MISC_CONV_BUFSIZE /* maximum length of input+1 */ +#define CONV_ECHO_ON 1 /* types of echo state */ +#define CONV_ECHO_OFF 0 + +/* + * external timeout definitions - these can be overridden by the + * application. + */ + +time_t pam_misc_conv_warn_time = 0; /* time when we warn */ +time_t pam_misc_conv_die_time = 0; /* time when we timeout */ + +const char *pam_misc_conv_warn_line = N_("...Time is running out...\n"); +const char *pam_misc_conv_die_line = N_("...Sorry, your time is up!\n"); + +int pam_misc_conv_died=0; /* application can probe this for timeout */ + +/* + * These functions are for binary prompt manipulation. + * The manner in which a binary prompt is processed is application + * specific, so these function pointers are provided and can be + * initialized by the application prior to the conversation function + * being used. + */ + +static void pam_misc_conv_delete_binary(void *appdata UNUSED, + pamc_bp_t *delete_me) +{ + PAM_BP_RENEW(delete_me, 0, 0); +} + +int (*pam_binary_handler_fn)(void *appdata, pamc_bp_t *prompt_p) = NULL; +void (*pam_binary_handler_free)(void *appdata, pamc_bp_t *prompt_p) + = pam_misc_conv_delete_binary; + +/* the following code is used to get text input */ + +static volatile sig_atomic_t expired=0; + +/* return to the previous signal handling */ +static void reset_alarm(struct sigaction *o_ptr) +{ + (void) alarm(0); /* stop alarm clock - if still ticking */ + (void) sigaction(SIGALRM, o_ptr, NULL); +} + +/* this is where we intercept the alarm signal */ +static void time_is_up(int ignore UNUSED) +{ + expired = 1; +} + +/* set the new alarm to hit the time_is_up() function */ +static int set_alarm(int delay, struct sigaction *o_ptr) +{ + struct sigaction new_sig; + + sigemptyset(&new_sig.sa_mask); + new_sig.sa_flags = 0; + new_sig.sa_handler = time_is_up; + if ( sigaction(SIGALRM, &new_sig, o_ptr) ) { + return 1; /* setting signal failed */ + } + if ( alarm(delay) ) { + (void) sigaction(SIGALRM, o_ptr, NULL); + return 1; /* failed to set alarm */ + } + return 0; /* all seems to have worked */ +} + +/* return the number of seconds to next alarm. 0 = no delay, -1 = expired */ +static int get_delay(void) +{ + time_t now; + + expired = 0; /* reset flag */ + (void) time(&now); + + /* has the quit time passed? */ + if (pam_misc_conv_die_time && now >= pam_misc_conv_die_time) { + fprintf(stderr,"%s",pam_misc_conv_die_line); + + pam_misc_conv_died = 1; /* note we do not reset the die_time */ + return -1; /* time is up */ + } + + /* has the warning time passed? */ + if (pam_misc_conv_warn_time && now >= pam_misc_conv_warn_time) { + fprintf(stderr, "%s", pam_misc_conv_warn_line); + pam_misc_conv_warn_time = 0; /* reset warn_time */ + + /* indicate remaining delay - if any */ + + return (pam_misc_conv_die_time ? pam_misc_conv_die_time - now:0 ); + } + + /* indicate possible warning delay */ + + if (pam_misc_conv_warn_time) + return (pam_misc_conv_warn_time - now); + else if (pam_misc_conv_die_time) + return (pam_misc_conv_die_time - now); + else + return 0; +} + +/* read a line of input string, giving prompt when appropriate */ +static int read_string(int echo, const char *prompt, char **retstr) +{ + struct termios term_before, term_tmp; + char line[INPUTSIZE]; + struct sigaction old_sig; + int delay, nc = -1, have_term = 0; + sigset_t oset, nset; + + D(("called with echo='%s', prompt='%s'.", echo ? "ON":"OFF" , prompt)); + + if (isatty(STDIN_FILENO)) { /* terminal state */ + + /* is a terminal so record settings and flush it */ + if ( tcgetattr(STDIN_FILENO, &term_before) != 0 ) { + D(("")); + *retstr = NULL; + return -1; + } + memcpy(&term_tmp, &term_before, sizeof(term_tmp)); + if (echo) + term_tmp.c_lflag |= ICANON | ECHOCTL; + else + term_tmp.c_lflag &= ~(ECHO); + have_term = 1; + + /* + * We make a simple attempt to block TTY signals from suspending + * the conversation without giving PAM a chance to clean up. + */ + + sigemptyset(&nset); + sigaddset(&nset, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &nset, &oset); + + } else if (!echo) { + D(("")); + } + + /* set up the signal handling */ + delay = get_delay(); + + /* reading the line */ + while (delay >= 0) { + /* this may, or may not set echo off -- drop pending input */ + if (have_term) + (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_tmp); + + fprintf(stderr, "%s", prompt); + + if ( delay > 0 && set_alarm(delay, &old_sig) ) { + D(("")); + break; + } else { + if (have_term) + nc = read(STDIN_FILENO, line, INPUTSIZE-1); + else /* we must read one line only */ + for (nc = 0; nc < INPUTSIZE-1 && (nc?line[nc-1]:0) != '\n'; + nc++) { + int rv; + if ((rv=read(STDIN_FILENO, line+nc, 1)) != 1) { + if (rv < 0) { + pam_overwrite_n(line, (unsigned int) nc); + nc = rv; + } + break; + } + } + if (have_term) { + (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &term_before); + if (!echo || expired) /* do we need a newline? */ + fprintf(stderr, "\n"); + } + if ( delay > 0 ) { + reset_alarm(&old_sig); + } + if (expired) { + delay = get_delay(); + } else if (nc > 0) { /* we got some user input */ + D(("we got some user input")); + + if (line[nc-1] == '\n') { /* terminate */ + line[--nc] = '\0'; + } else { + if (echo) { + fprintf(stderr, "\n"); + } + line[nc] = '\0'; + } + *retstr = strdup(line); + pam_overwrite_array(line); + if (!*retstr) { + D(("no memory for response string")); + nc = -1; + } + + goto cleanexit; /* return malloc()ed string */ + + } else if (nc == 0) { /* Ctrl-D */ + D(("user did not want to type anything")); + + *retstr = NULL; + if (echo) { + fprintf(stderr, "\n"); + } + goto cleanexit; /* return malloc()ed "" */ + } else if (nc == -1) { + /* Don't loop forever if read() returns -1. */ + D(("error reading input from the user: %m")); + if (echo) { + fprintf(stderr, "\n"); + } + *retstr = NULL; + goto cleanexit; /* return NULL */ + } + } + } + + /* getting here implies that the timer expired */ + + D(("the timer appears to have expired")); + + *retstr = NULL; + pam_overwrite_array(line); + + cleanexit: + + if (have_term) { + (void) sigprocmask(SIG_SETMASK, &oset, NULL); + (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term_before); + } + + return nc; +} + +/* end of read_string functions */ + +/* + * This conversation function is supposed to be a generic PAM one. + * Unfortunately, it is _not_ completely compatible with the Solaris PAM + * codebase. + * + * Namely, for msgm's that contain multiple prompts, this function + * interprets "const struct pam_message **msgm" as equivalent to + * "const struct pam_message *msgm[]". The Solaris module + * implementation interprets the **msgm object as a pointer to a + * pointer to an array of "struct pam_message" objects (that is, a + * confusing amount of pointer indirection). + */ + +int misc_conv(int num_msg, const struct pam_message **msgm, + struct pam_response **response, void *appdata_ptr) +{ + int count=0; + struct pam_response *reply; + + if (num_msg <= 0) + return PAM_CONV_ERR; + + D(("allocating empty response structure array.")); + + reply = calloc(num_msg, sizeof(struct pam_response)); + if (reply == NULL) { + D(("no memory for responses")); + return PAM_CONV_ERR; + } + + D(("entering conversation function.")); + + for (count=0; count < num_msg; ++count) { + char *string=NULL; + int nc; + + switch (msgm[count]->msg_style) { + case PAM_PROMPT_ECHO_OFF: + nc = read_string(CONV_ECHO_OFF,msgm[count]->msg, &string); + if (nc < 0) { + goto failed_conversation; + } + break; + case PAM_PROMPT_ECHO_ON: + nc = read_string(CONV_ECHO_ON,msgm[count]->msg, &string); + if (nc < 0) { + goto failed_conversation; + } + break; + case PAM_ERROR_MSG: + if (fprintf(stderr,"%s\n",msgm[count]->msg) < 0) { + goto failed_conversation; + } + break; + case PAM_TEXT_INFO: + if (fprintf(stdout,"%s\n",msgm[count]->msg) < 0) { + goto failed_conversation; + } + break; + case PAM_BINARY_PROMPT: + { + pamc_bp_t binary_prompt = NULL; + + if (!msgm[count]->msg || !pam_binary_handler_fn) { + goto failed_conversation; + } + + PAM_BP_RENEW(&binary_prompt, + PAM_BP_RCONTROL(msgm[count]->msg), + PAM_BP_LENGTH(msgm[count]->msg)); + PAM_BP_FILL(binary_prompt, 0, PAM_BP_LENGTH(msgm[count]->msg), + PAM_BP_RDATA(msgm[count]->msg)); + + if (pam_binary_handler_fn(appdata_ptr, + &binary_prompt) != PAM_SUCCESS + || (binary_prompt == NULL)) { + goto failed_conversation; + } + string = (char *) binary_prompt; + binary_prompt = NULL; + + break; + } + default: + fprintf(stderr, _("erroneous conversation (%d)\n"), + msgm[count]->msg_style); + goto failed_conversation; + } + + if (string) { /* must add to reply array */ + /* add string to list of responses */ + + reply[count].resp_retcode = 0; + reply[count].resp = string; + string = NULL; + } + } + + *response = reply; + reply = NULL; + + return PAM_SUCCESS; + +failed_conversation: + + D(("the conversation failed")); + + if (reply) { + for (count=0; countmsg_style) { + case PAM_PROMPT_ECHO_ON: + case PAM_PROMPT_ECHO_OFF: + pam_overwrite_string(reply[count].resp); + free(reply[count].resp); + break; + case PAM_BINARY_PROMPT: + { + void *bt_ptr = reply[count].resp; + pam_binary_handler_free(appdata_ptr, bt_ptr); + break; + } + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + /* should not actually be able to get here... */ + free(reply[count].resp); + } + reply[count].resp = NULL; + } + /* forget reply too */ + free(reply); + reply = NULL; + } + + return PAM_CONV_ERR; +} diff --git a/libpam_misc/pam_misc.pc.in b/libpam_misc/pam_misc.pc.in new file mode 100644 index 0000000..c1e69d2 --- /dev/null +++ b/libpam_misc/pam_misc.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@pkgconfig_includedir@ + +Name: pam_misc +Description: Miscellaneous functions that make the job of writing PAM-aware applications easier. +URL: http://www.linux-pam.org/ +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lpam_misc diff --git a/libpamc/License b/libpamc/License new file mode 100644 index 0000000..9316078 --- /dev/null +++ b/libpamc/License @@ -0,0 +1,41 @@ +Unless otherwise *explicitly* stated the following text describes the +licensed conditions under which the contents of this libpamc release +may be distributed: + +------------------------------------------------------------------------- +Redistribution and use in source and binary forms of libpamc, +with or without modification, are permitted provided that the +following conditions are met: + +1. Redistributions of source code must retain any existing copyright + notice, and this entire permission notice in its entirety, + including the disclaimer of warranties. + +2. Redistributions in binary form must reproduce all prior and current + copyright notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +3. The name of any author may not be used to endorse or promote + products derived from this software without their specific prior + written permission. + +ALTERNATIVELY, this product may be distributed under the terms of the +GNU Library General Public License (LGPL), in which case the +provisions of the GNU LGPL are required INSTEAD OF the above +restrictions. (This clause is necessary due to a potential conflict +between the GNU LGPL and the restrictions contained in a BSD-style +copyright.) + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. +------------------------------------------------------------------------- diff --git a/libpamc/include/security/meson.build b/libpamc/include/security/meson.build new file mode 100644 index 0000000..f82fea0 --- /dev/null +++ b/libpamc/include/security/meson.build @@ -0,0 +1 @@ +install_headers('pam_client.h', install_dir: includedir) diff --git a/libpamc/include/security/pam_client.h b/libpamc/include/security/pam_client.h new file mode 100644 index 0000000..f382ac7 --- /dev/null +++ b/libpamc/include/security/pam_client.h @@ -0,0 +1,195 @@ +/* + * $Id$ + * + * Copyright (c) 1999 Andrew G. Morgan + * + * This header file provides the prototypes for the PAM client API + */ + +#ifndef PAM_CLIENT_H +#define PAM_CLIENT_H + +#ifdef __cplusplus +extern "C" { +#endif /* def __cplusplus */ + +#include +#include +#include +#include +#include +#include + +/* opaque agent handling structure */ + +typedef struct pamc_handle_s *pamc_handle_t; + +/* binary prompt structure pointer */ +typedef struct { uint32_t length; uint8_t control; } + __attribute__ ((__packed__)) *pamc_bp_t; + +/* + * functions provided by libpamc + */ + +/* + * Initialize the agent abstraction library + */ + +pamc_handle_t pamc_start(void); + +/* + * Terminate the authentication process + */ + +int pamc_end(pamc_handle_t *pch); + +/* + * force the loading of a specified agent + */ + +int pamc_load(pamc_handle_t pch, const char *agent_id); + +/* + * Single conversation interface for binary prompts + */ + +int pamc_converse(pamc_handle_t pch, pamc_bp_t *prompt_p); + +/* + * disable an agent + */ + +int pamc_disable(pamc_handle_t pch, const char *agent_id); + +/* + * obtain a list of available agents + */ + +char **pamc_list_agents(pamc_handle_t pch); + +/* + * PAM_BP_ MACROS for creating, destroying and manipulating binary prompts + */ + +#ifndef PAM_BP_ASSERT +# ifdef NDEBUG +# define PAM_BP_ASSERT(x) do {} while (0) +# else +# define PAM_BP_ASSERT(x) do { printf(__FILE__ "(%d): %s\n", \ + __LINE__, x) ; exit(1); } while (0) +# endif /* NDEBUG */ +#endif /* PAM_BP_ASSERT */ + +#ifndef PAM_BP_CALLOC +# define PAM_BP_CALLOC calloc +#endif /* PAM_BP_CALLOC */ + +#ifndef PAM_BP_FREE +# define PAM_BP_FREE free +#endif /* PAM_BP_FREE */ + +#define __PAM_BP_WOCTET(x,y) (*((y) + (uint8_t *)(x))) +#define __PAM_BP_ROCTET(x,y) (*((y) + (const uint8_t *)(x))) + +#define PAM_BP_MIN_SIZE (sizeof(uint32_t) + sizeof(uint8_t)) +#define PAM_BP_MAX_LENGTH 0x20000 /* an advisory limit */ +#define PAM_BP_WCONTROL(x) (__PAM_BP_WOCTET(x,4)) +#define PAM_BP_RCONTROL(x) (__PAM_BP_ROCTET(x,4)) +#define PAM_BP_SIZE(x) ((__PAM_BP_ROCTET(x,0)<<24)+ \ + (__PAM_BP_ROCTET(x,1)<<16)+ \ + (__PAM_BP_ROCTET(x,2)<< 8)+ \ + (__PAM_BP_ROCTET(x,3) )) +#define PAM_BP_LENGTH(x) (PAM_BP_SIZE(x) - PAM_BP_MIN_SIZE) +#define PAM_BP_WDATA(x) (PAM_BP_MIN_SIZE + (uint8_t *) (x)) +#define PAM_BP_RDATA(x) (PAM_BP_MIN_SIZE + (const uint8_t *) (x)) + +/* Note, this macro always '\0' terminates renewed packets */ + +#define PAM_BP_RENEW(old_p, cntrl, data_length) \ +do { \ + if ((old_p) != NULL) { \ + if (*(old_p)) { \ + uint32_t __size; \ + __size = PAM_BP_SIZE(*(old_p)); \ + memset(*(old_p), 0, __size); \ + PAM_BP_FREE(*(old_p)); \ + } \ + if (cntrl) { \ + uint32_t __size; \ + \ + __size = PAM_BP_MIN_SIZE + (data_length); \ + if ((*(old_p) = PAM_BP_CALLOC(1, 1+__size))) { \ + __PAM_BP_WOCTET(*(old_p), 3) = __size & 0xFF; \ + __PAM_BP_WOCTET(*(old_p), 2) = (__size>>=8) & 0xFF; \ + __PAM_BP_WOCTET(*(old_p), 1) = (__size>>=8) & 0xFF; \ + __PAM_BP_WOCTET(*(old_p), 0) = (__size>>=8) & 0xFF; \ + (*(old_p))->control = cntrl; \ + } else { \ + PAM_BP_ASSERT("out of memory for binary prompt"); \ + } \ + } else { \ + *(old_p) = NULL; \ + } \ + } else { \ + PAM_BP_ASSERT("programming error, invalid binary prompt pointer"); \ + } \ +} while (0) + +#define PAM_BP_FILL(prmpt, offset, length, data) \ +do { \ + size_t bp_length; \ + uint8_t *prompt = (uint8_t *) (prmpt); \ + bp_length = PAM_BP_LENGTH(prompt); \ + if (bp_length < ((length)+(offset))) { \ + PAM_BP_ASSERT("attempt to write over end of prompt"); \ + } \ + memcpy((offset) + PAM_BP_WDATA(prompt), (data), (length)); \ +} while (0) + +#define PAM_BP_EXTRACT(prmpt, offset, length, data) \ +do { \ + size_t __bp_length; \ + const uint8_t *__prompt = (const uint8_t *) (prmpt); \ + __bp_length = PAM_BP_LENGTH(__prompt); \ + if (((offset) < 0) || (__bp_length < ((length)+(offset))) \ + || ((length) < 0)) { \ + PAM_BP_ASSERT("invalid extraction from prompt"); \ + } \ + memcpy((data), (offset) + PAM_BP_RDATA(__prompt), (length)); \ +} while (0) + + +/* Control types */ + +#define PAM_BPC_FALSE 0 +#define PAM_BPC_TRUE 1 + +#define PAM_BPC_OK 0x01 /* continuation packet */ +#define PAM_BPC_SELECT 0x02 /* initialization packet */ +#define PAM_BPC_DONE 0x03 /* termination packet */ +#define PAM_BPC_FAIL 0x04 /* unable to execute */ + +/* The following control characters are only legal for echanges + between an agent and a client (it is the responsibility of the + client to enforce this rule in the face of a rogue server): */ + +#define PAM_BPC_GETENV 0x41 /* obtain client env.var */ +#define PAM_BPC_PUTENV 0x42 /* set client env.var */ +#define PAM_BPC_TEXT 0x43 /* display message */ +#define PAM_BPC_ERROR 0x44 /* display error message */ +#define PAM_BPC_PROMPT 0x45 /* echo'd text prompt */ +#define PAM_BPC_PASS 0x46 /* non-echo'd text prompt*/ + +/* quick check for prompts that are legal for the client (by + implication the server too) to send to libpamc */ + +#define PAM_BPC_FOR_CLIENT(/* pamc_bp_t */ prompt) \ + (((prompt)->control <= PAM_BPC_FAIL && (prompt)->control >= PAM_BPC_OK) \ + ? PAM_BPC_TRUE:PAM_BPC_FALSE) + +#ifdef __cplusplus +} +#endif /* def __cplusplus */ + +#endif /* PAM_CLIENT_H */ diff --git a/libpamc/libpamc.h b/libpamc/libpamc.h new file mode 100644 index 0000000..cdc7724 --- /dev/null +++ b/libpamc/libpamc.h @@ -0,0 +1,66 @@ +/* + * $Id$ + * + * Copyright (c) Andrew G. Morgan + * + */ + +#ifndef LIBPAMC_H +#define LIBPAMC_H + +#include "config.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _PAMC_DEFAULT_TOP_FD 10 + +struct pamc_handle_s { + struct pamc_agent_s *current; + struct pamc_agent_s *chain; + struct pamc_blocked_s *blocked_agents; + int max_path; + char **agent_paths; + int combined_status; + int highest_fd_to_close; +}; + +typedef struct pamc_blocked_s { + char *id; /* terminated */ + struct pamc_blocked_s *next; +} pamc_blocked_t; + +typedef struct pamc_agent_s { + char *id; + int id_length; + struct pamc_agent_s *next; + int writer; /* write to agent */ + int reader; /* read from agent */ + pid_t pid; /* agent process id */ +} pamc_agent_t; + +/* used to build a tree of unique, sorted agent ids */ + +typedef struct pamc_id_node { + struct pamc_id_node *left, *right; + int child_count; + char *agent_id; +} pamc_id_node_t; + +/* internal function */ +int __pamc_valid_agent_id(int id_length, const char *id); + +#define PAMC_SYSTEM_AGENT_PATH "/lib/pamc:/usr/lib/pamc" +#define PAMC_SYSTEM_AGENT_SEPARATOR ':' + +#endif /* LIBPAMC_H */ diff --git a/libpamc/libpamc.map b/libpamc/libpamc.map new file mode 100644 index 0000000..cc61a33 --- /dev/null +++ b/libpamc/libpamc.map @@ -0,0 +1,12 @@ +LIBPAMC_1.0 { + global: + pamc_start; + pamc_end; + pamc_load; + pamc_converse; + pamc_disable; + pamc_list_agents; + + local: + *; +}; diff --git a/libpamc/meson.build b/libpamc/meson.build new file mode 100644 index 0000000..f5da067 --- /dev/null +++ b/libpamc/meson.build @@ -0,0 +1,36 @@ +subdir('include/security') + +libpamc_src = [ + 'pamc_client.c', + 'pamc_converse.c', + 'pamc_load.c', +] + +libpamc_inc = include_directories('include') + +libpamc_version = '0.82.1' +libpamc_map = 'libpamc.map' +libpamc_map_path = meson.current_source_dir() / libpamc_map + +libpamc_link_deps = [libpamc_map] +libpamc_link_args = ['-Wl,--version-script=' + libpamc_map_path] + +libpamc = shared_library( + 'pamc', + sources: libpamc_src, + include_directories: [libpam_inc, libpamc_inc], + dependencies: [libpam_internal_dep], + link_depends: libpamc_link_deps, + link_args: libpamc_link_args, + version: libpamc_version, + install: true, +) + +pkgconfig.generate( + libpamc, + description: 'The PAM client API library and binary prompt support. Rarely used.', + name: 'libpamc', + filebase: 'pamc', + version: meson.project_version(), + url: 'http://www.linux-pam.org/' +) diff --git a/libpamc/pamc.pc.in b/libpamc/pamc.pc.in new file mode 100644 index 0000000..6e7ec29 --- /dev/null +++ b/libpamc/pamc.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@pkgconfig_includedir@ + +Name: libpamc +URL: http://www.linux-pam.org/ +Description: The PAM client API library and binary prompt support. Rarely used. +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lpamc diff --git a/libpamc/pamc_client.c b/libpamc/pamc_client.c new file mode 100644 index 0000000..b7be821 --- /dev/null +++ b/libpamc/pamc_client.c @@ -0,0 +1,190 @@ +/* + * $Id$ + * + * Copyright (c) Andrew G. Morgan + * + * pamc_start and pamc_end + */ + +#include "libpamc.h" +#include "pam_inline.h" + +/* + * liberate path list + */ + +static void __pamc_delete_path_list(pamc_handle_t pch) +{ + int i; + + for (i=0; pch->agent_paths[i]; ++i) { + free(pch->agent_paths[i]); + pch->agent_paths[i] = NULL; + } + + free(pch->agent_paths); + pch->agent_paths = NULL; +} + +/* + * open the pamc library + */ + +pamc_handle_t pamc_start(void) +{ + int i, count, last, this; + const char *default_path; + pamc_handle_t pch; + + pch = calloc(1, sizeof(struct pamc_handle_s)); + if (pch == NULL) { + D(("no memory for *pch")); + return NULL; + } + + pch->highest_fd_to_close = _PAMC_DEFAULT_TOP_FD; + + default_path = getenv("PAMC_AGENT_PATH"); + if (default_path == NULL) { + default_path = PAMC_SYSTEM_AGENT_PATH; + } + + /* number of individual paths */ + for (count=1, i=0; default_path[i]; ++i) { + if (default_path[i] == PAMC_SYSTEM_AGENT_SEPARATOR) { + ++count; + } + } + + pch->agent_paths = calloc(count+1, sizeof(char *)); + if (pch->agent_paths == NULL) { + D(("no memory for path list")); + goto drop_pch; + } + + this = last = i = 0; + while ( default_path[i] || (i != last) ) { + if ( default_path[i] == PAMC_SYSTEM_AGENT_SEPARATOR + || !default_path[i] ) { + int length; + + pch->agent_paths[this] = malloc(length = 1+i-last); + + if (pch->agent_paths[this] == NULL) { + D(("no memory for next path")); + goto drop_list; + } + + memcpy(pch->agent_paths[this], default_path + last, i-last); + pch->agent_paths[this][i-last] = '\0'; + if (length > pch->max_path) { + pch->max_path = length; + } + + if (++this == count) { + break; + } + + last = ++i; + } else { + ++i; + } + } + + return pch; + +drop_list: + __pamc_delete_path_list(pch); + +drop_pch: + free(pch); + + return NULL; +} + +/* + * shutdown each of the loaded agents and + */ + +static int __pamc_shutdown_agents(pamc_handle_t pch) +{ + int retval = PAM_BPC_TRUE; + + D(("called")); + + while (pch->chain) { + pid_t pid; + int status; + pamc_agent_t *this; + + this = pch->chain; + D(("cleaning up agent %p", this)); + pch->chain = pch->chain->next; + this->next = NULL; + D(("cleaning up agent: %s", this->id)); + + /* close off contact with agent and wait for it to shutdown */ + + close(this->writer); + this->writer = -1; + close(this->reader); + this->reader = -1; + + pid = waitpid(this->pid, &status, 0); + if (pid == this->pid) { + + D(("is exit:%d, exit val:%d", + WIFEXITED(status), WEXITSTATUS(status))); + + if (!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) { + retval = PAM_BPC_FALSE; + } + } else { + D(("problem shutting down agent (%s): pid(%d) != waitpid(%d)!?", + this->id, this->pid, pid)); + retval = PAM_BPC_FALSE; + } + pid = this->pid = 0; + + pam_overwrite_n(this->id, this->id_length); + free(this->id); + this->id = NULL; + this->id_length = 0; + + free(this); + this = NULL; + } + + return retval; +} + +/* + * close the pamc library + */ + +int pamc_end(pamc_handle_t *pch_p) +{ + int retval; + + if (pch_p == NULL) { + D(("called with no pch_p")); + return PAM_BPC_FALSE; + } + + if (*pch_p == NULL) { + D(("called with no *pch_p")); + return PAM_BPC_FALSE; + } + + D(("removing path_list")); + __pamc_delete_path_list(*pch_p); + + D(("shutting down agents")); + retval = __pamc_shutdown_agents(*pch_p); + + D(("freeing *pch_p")); + free(*pch_p); + *pch_p = NULL; + + return retval; +} diff --git a/libpamc/pamc_converse.c b/libpamc/pamc_converse.c new file mode 100644 index 0000000..4da11cf --- /dev/null +++ b/libpamc/pamc_converse.c @@ -0,0 +1,212 @@ +/* + * $Id$ + * + * Copyright (c) Andrew G. Morgan + * + * pamc_converse + */ + +#include "libpamc.h" +#include "pam_inline.h" + +/* + * select agent + */ + +static int __pamc_select_agent(pamc_handle_t pch, char *agent_id) +{ + pamc_agent_t *agent; + + for (agent = pch->chain; agent; agent = agent->next) { + if (!strcmp(agent->id, agent_id)) { + pch->current = agent; + return PAM_BPC_TRUE; + } + } + + D(("failed to locate agent")); + pch->current = NULL; + return PAM_BPC_FALSE; +} + +/* + * pass a binary prompt to the active agent and wait for a reply prompt + */ + +int pamc_converse(pamc_handle_t pch, pamc_bp_t *prompt_p) +{ + uint32_t size, offset=0; + uint8_t control, raw[PAM_BP_MIN_SIZE]; + + D(("called")); + + if (pch == NULL) { + D(("null pch")); + goto pamc_converse_failure; + } + + if (prompt_p == NULL) { + D(("null prompt_p")); + goto pamc_converse_failure; + } + + if (*prompt_p == NULL) { + D(("null *prompt_p")); + goto pamc_converse_failure; + } + + /* from here on, failures are interoperability problems.. */ + + size = PAM_BP_SIZE(*prompt_p); + if (size < PAM_BP_MIN_SIZE) { + D(("problem with size being too short (%u)", size)); + goto pamc_unknown_prompt; + } + + if (PAM_BPC_FOR_CLIENT(*prompt_p) != PAM_BPC_TRUE) { + D(("*prompt_p is not legal for the client to use")); + goto pamc_unknown_prompt; + } + + /* do we need to select the agent? */ + if ((*prompt_p)->control == PAM_BPC_SELECT) { + char *rawh; + size_t i; + int retval; + + D(("selecting a specified agent")); + + rawh = (char *) *prompt_p; + for (i = PAM_BP_MIN_SIZE; i= size) + || !__pamc_valid_agent_id(i-PAM_BP_MIN_SIZE, + rawh + PAM_BP_MIN_SIZE) ) { + goto pamc_unknown_prompt; + } + + rawh[i] = '\0'; + retval = pamc_load(pch, PAM_BP_MIN_SIZE + rawh); + if (retval == PAM_BPC_TRUE) { + retval = __pamc_select_agent(pch, PAM_BP_MIN_SIZE + rawh); + } + rawh[i] = '/'; + + if (retval != PAM_BPC_TRUE) { + goto pamc_unknown_prompt; + } + + D(("agent is loaded")); + } + + if (pch->current == NULL) { + D(("unable to address agent")); + goto pamc_unknown_prompt; + } + + /* pump all of the prompt into the agent */ + do { + int rval = write(pch->current->writer, + offset + (const uint8_t *) (*prompt_p), + size - offset); + if (rval == -1) { + switch (errno) { + case EINTR: + break; + default: + D(("problem writing to agent: %m")); + goto pamc_unknown_prompt; + } + } else { + offset += rval; + } + } while (offset < size); + + D(("whole prompt sent to agent")); + + /* read size and control for response prompt */ + + offset = 0; + memset(raw, 0, sizeof(raw)); + do { + int rval; + + rval = read(pch->current->reader, raw + offset, + PAM_BP_MIN_SIZE - offset); + + if (rval == -1) { + switch (errno) { + case EINTR: + break; + default: + D(("problem reading from agent: %m")); + goto pamc_unknown_prompt; + } + } else if (rval) { + offset += rval; + } else { + D(("agent has closed its output pipe - nothing more to read")); + goto pamc_converse_failure; + } + } while (offset < PAM_BP_MIN_SIZE); + + /* construct the whole reply prompt */ + + size = PAM_BP_SIZE(raw); + control = PAM_BP_RCONTROL(raw); + pam_overwrite_array(raw); + + D(("agent replied with prompt of size %d and control %u", + size, control)); + + PAM_BP_RENEW(prompt_p, control, size - PAM_BP_MIN_SIZE); + if (*prompt_p == NULL) { + D(("problem making a new prompt for reply")); + goto pamc_unknown_prompt; + } + + /* read the rest of the reply prompt -- note offset has the correct + value from the previous loop */ + + while (offset < size) { + int rval = read(pch->current->reader, offset + (uint8_t *) *prompt_p, + size-offset); + + if (rval == -1) { + switch (errno) { + case EINTR: + break; + default: + D(("problem reading from agent: %m")); + goto pamc_unknown_prompt; + } + } else if (rval) { + offset += rval; + } else { + D(("problem reading prompt (%d) with %d to go", + size, size-offset)); + goto pamc_converse_failure; + } + } + + D(("returning success")); + + return PAM_BPC_TRUE; + +pamc_converse_failure: + + D(("conversation failure")); + PAM_BP_RENEW(prompt_p, 0, 0); + return PAM_BPC_FALSE; + +pamc_unknown_prompt: + + /* the server is trying something that the client does not support */ + D(("unknown prompt")); + PAM_BP_RENEW(prompt_p, PAM_BPC_FAIL, 0); + return PAM_BPC_TRUE; +} diff --git a/libpamc/pamc_load.c b/libpamc/pamc_load.c new file mode 100644 index 0000000..7efd572 --- /dev/null +++ b/libpamc/pamc_load.c @@ -0,0 +1,473 @@ +/* + * $Id$ + * + * Copyright (c) 1999 Andrew G. Morgan + * + * pamc_load + */ + +#include "libpamc.h" +#include "pam_inline.h" + +static int __pamc_exec_agent(pamc_handle_t pch, pamc_agent_t *agent) +{ + char *full_path; + int found_agent, length, reset_length, to_agent[2], from_agent[2]; + int return_code = PAM_BPC_FAIL; + + if (agent->id[agent->id_length] != '\0') { + PAM_BP_ASSERT("libpamc: internal error agent_id not terminated"); + } + + for (length=0; (length < agent->id_length); ++length) { + switch (agent->id[length]) { + case '/': + D(("ill formed agent id")); + return PAM_BPC_FAIL; + } + } + + /* enough memory for any path + this agent */ + reset_length = 3 + pch->max_path + agent->id_length; + D(("reset_length = %d (3+%d+%d)", + reset_length, pch->max_path, agent->id_length)); + full_path = malloc(reset_length); + if (full_path == NULL) { + D(("no memory for agent path")); + return PAM_BPC_FAIL; + } + + found_agent = 0; + for (length=0; pch->agent_paths[length]; ++length) { + struct stat buf; + + D(("path: [%s]", pch->agent_paths[length])); + D(("agent id: [%s]", agent->id)); + + sprintf(full_path, "%s/%s", pch->agent_paths[length], agent->id); + + D(("looking for agent here: [%s]\n", full_path)); + if (stat(full_path, &buf) == 0) { + D(("file existis")); + found_agent = 1; + break; + } + } + + if (! found_agent) { + D(("no agent was found")); + goto free_and_return; + } + + if (pipe(to_agent)) { + D(("failed to open pipe to agent")); + goto free_and_return; + } + + if (pipe(from_agent)) { + D(("failed to open pipe from agent")); + goto close_the_agent; + } + + agent->pid = fork(); + if (agent->pid == -1) { + + D(("failed to fork for agent")); + goto close_both_pipes; + + } else if (agent->pid == 0) { + + int i; + + dup2(from_agent[1], STDOUT_FILENO); + dup2(to_agent[0], STDIN_FILENO); + + /* we close all of the files that have filedescriptors lower + and equal to twice the highest we have seen, The idea is + that we don't want to leak filedescriptors to agents from a + privileged client application. + + XXX - this is a heuristic at this point. There is a growing + need for an extra 'set param' libpamc function, that could + be used to supply info like the highest fd to close etc.. + */ + + if (from_agent[1] > pch->highest_fd_to_close) { + pch->highest_fd_to_close = 2*from_agent[1]; + } + + for (i=0; i <= pch->highest_fd_to_close; ++i) { + switch (i) { + case STDOUT_FILENO: + case STDERR_FILENO: + case STDIN_FILENO: + /* only these three remain open */ + break; + default: + (void) close(i); /* don't care if its not open */ + } + } + + /* we make no attempt to drop other privileges - this library + has no idea how that would be done in the general case. It + is up to the client application (when calling + pamc_converse) to make sure no privilege will leak into an + (untrusted) agent. */ + + /* we propagate no environment - future versions of this + library may have the ability to audit all agent + transactions. */ + + D(("exec'ing agent %s", full_path)); + execle(full_path, "pam-agent", NULL, NULL); + + D(("exec failed")); + _exit(1); + + } + + close(to_agent[0]); + close(from_agent[1]); + + agent->writer = to_agent[1]; + agent->reader = from_agent[0]; + + return_code = PAM_BPC_TRUE; + goto free_and_return; + +close_both_pipes: + close(from_agent[0]); + close(from_agent[1]); + +close_the_agent: + close(to_agent[0]); + close(to_agent[1]); + +free_and_return: + pam_overwrite_n(full_path, reset_length); + free(full_path); + + D(("returning %d", return_code)); + + return return_code; +} + +/* + * has the named agent been loaded? + */ + +static int __pamc_agent_is_enabled(pamc_handle_t pch, const char *agent_id) +{ + pamc_agent_t *agent; + + for (agent = pch->chain; agent; agent = agent->next) { + if (!strcmp(agent->id, agent_id)) { + D(("agent already loaded")); + return PAM_BPC_TRUE; + } + } + + D(("agent is not loaded")); + return PAM_BPC_FALSE; +} + +/* + * has the named agent been disabled? + */ + +static int __pamc_agent_is_disabled(pamc_handle_t pch, const char *agent_id) +{ + pamc_blocked_t *blocked; + + for (blocked=pch->blocked_agents; blocked; blocked = blocked->next) { + if (!strcmp(agent_id, blocked->id)) { + D(("agent is disabled")); + return PAM_BPC_TRUE; + } + } + + D(("agent is not disabled")); + return PAM_BPC_FALSE; +} + +/* + * disable an agent + */ + +int pamc_disable(pamc_handle_t pch, const char *agent_id) +{ + pamc_blocked_t *block; + + if (pch == NULL) { + D(("pch is NULL")); + return PAM_BPC_FALSE; + } + + if (agent_id == NULL) { + D(("agent_id is NULL")); + return PAM_BPC_FALSE; + } + + if (__pamc_agent_is_enabled(pch, agent_id) != PAM_BPC_FALSE) { + D(("agent is already loaded")); + return PAM_BPC_FALSE; + } + + if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) { + D(("agent is already disabled")); + return PAM_BPC_TRUE; + } + + block = calloc(1, sizeof(pamc_blocked_t)); + if (block == NULL) { + D(("no memory for new blocking structure")); + return PAM_BPC_FALSE; + } + + block->id = strdup(agent_id); + if (block->id == NULL) { + D(("no memory for agent id")); + free(block); + return PAM_BPC_FALSE; + } + + block->next = pch->blocked_agents; + pch->blocked_agents = block; + + return PAM_BPC_TRUE; +} + +/* + * force the loading of a particular agent + */ + +int pamc_load(pamc_handle_t pch, const char *agent_id) +{ + pamc_agent_t *agent; + size_t length; + + /* sanity checking */ + + if (pch == NULL) { + D(("pch is NULL")); + return PAM_BPC_FALSE; + } + + if (agent_id == NULL) { + D(("agent_id is NULL")); + return PAM_BPC_FALSE; + } + + if (__pamc_agent_is_disabled(pch, agent_id) != PAM_BPC_FALSE) { + D(("sorry agent is disabled")); + return PAM_BPC_FALSE; + } + + length = strlen(agent_id); + + /* scan list to see if agent is loaded */ + + if (__pamc_agent_is_enabled(pch, agent_id) == PAM_BPC_TRUE) { + D(("no need to load an already loaded agent (%s)", agent_id)); + return PAM_BPC_TRUE; + } + + /* not in the list, so we need to load it and add it to the head + of the chain */ + + agent = calloc(1, sizeof(pamc_agent_t)); + if (agent == NULL) { + D(("no memory for new agent")); + return PAM_BPC_FALSE; + } + agent->id = calloc(1, 1+length); + if (agent->id == NULL) { + D(("no memory for new agent's id")); + goto fail_free_agent; + } + memcpy(agent->id, agent_id, length); + agent->id[length] = '\0'; + agent->id_length = length; + + if (__pamc_exec_agent(pch, agent) != PAM_BPC_TRUE) { + D(("unable to exec agent")); + goto fail_free_agent_id; + } + + agent->next = pch->chain; + pch->chain = agent; + + return PAM_BPC_TRUE; + +fail_free_agent_id: + + pam_overwrite_n(agent->id, agent->id_length); + free(agent->id); + + pam_overwrite_object(agent); + +fail_free_agent: + + free(agent); + return PAM_BPC_FALSE; +} + +/* + * what's a valid agent name? + */ + +int __pamc_valid_agent_id(int id_length, const char *id) +{ + int post, i; + + for (i=post=0 ; i < id_length; ++i) { + int ch = id[i++]; + + if (isalpha((unsigned char)ch) || isdigit((unsigned char)ch) || (ch == '_')) { + continue; + } else if (post && (ch == '.')) { + continue; + } else if ((i > 1) && (!post) && (ch == '@')) { + post = 1; + } else { + D(("id=%s contains '%c' which is illegal", id, ch)); + return 0; + } + } + + if (!i) { + D(("length of id is 0")); + return 0; + } else { + return 1; /* id is valid */ + } +} + +/* + * building a tree of available agent names + */ + +static pamc_id_node_t *__pamc_add_node(pamc_id_node_t *root, const char *id, + int *counter) +{ + if (root) { + + int cmp; + + if ((cmp = strcmp(id, root->agent_id))) { + if (cmp > 0) { + root->right = __pamc_add_node(root->right, id, + &(root->child_count)); + } else { + root->left = __pamc_add_node(root->left, id, + &(root->child_count)); + } + } + + return root; + + } else { + + pamc_id_node_t *node = calloc(1, sizeof(pamc_id_node_t)); + + if (node) { + node->agent_id = strdup(id); + if (node->agent_id == NULL) { + free(node); + node = NULL; + } + } + + (*counter)++; + return node; + } +} + +/* + * drop all of the tree and any remaining ids + */ + +static pamc_id_node_t *__pamc_liberate_nodes(pamc_id_node_t *tree) +{ + if (tree) { + free(tree->agent_id); + tree->agent_id = NULL; + + tree->left = __pamc_liberate_nodes(tree->left); + tree->right = __pamc_liberate_nodes(tree->right); + + tree->child_count = 0; + free(tree); + } + + return NULL; +} + +/* + * fill a list with the contents of the tree (in ascii order) + */ + +static void __pamc_fill_list_from_tree(pamc_id_node_t *tree, char **agent_list, + int *counter) +{ + if (tree) { + __pamc_fill_list_from_tree(tree->left, agent_list, counter); + agent_list[(*counter)++] = tree->agent_id; + tree->agent_id = NULL; + __pamc_fill_list_from_tree(tree->right, agent_list, counter); + } +} + +/* + * get a list of the available agents + */ + +char **pamc_list_agents(pamc_handle_t pch) +{ + int i, total_agent_count=0; + pamc_id_node_t *tree = NULL; + char **agent_list; + + /* loop over agent paths */ + + for (i=0; pch->agent_paths[i]; ++i) { + DIR *dir; + + dir = opendir(pch->agent_paths[i]); + if (dir) { + struct dirent *item; + + while ((item = readdir(dir))) { + + /* this is a cheat on recognizing agent_ids */ + if (!__pamc_valid_agent_id(strlen(item->d_name), + item->d_name)) { + continue; + } + + tree = __pamc_add_node(tree, item->d_name, &total_agent_count); + } + + closedir(dir); + } + } + + /* now, we build a list of ids */ + D(("total of %d available agents\n", total_agent_count)); + + agent_list = calloc(total_agent_count+1, sizeof(char *)); + if (agent_list) { + int counter=0; + + __pamc_fill_list_from_tree(tree, agent_list, &counter); + if (counter != total_agent_count) { + PAM_BP_ASSERT("libpamc: internal error transcribing tree"); + } + } else { + D(("no memory for agent list")); + } + + __pamc_liberate_nodes(tree); + + return agent_list; +} diff --git a/libpamc/test/agents/secret@here b/libpamc/test/agents/secret@here new file mode 100755 index 0000000..2bef647 --- /dev/null +++ b/libpamc/test/agents/secret@here @@ -0,0 +1,307 @@ +#!/usr/bin/perl +# +# This is a simple example PAM authentication agent, it implements a +# simple shared secret authentication scheme. The PAM module pam_secret.so +# is its counter part. Both the agent and the remote server are able to +# authenticate one another, but the server is given the opportunity to +# ignore a failed authentication. +# + +$^W = 1; +use strict; +use IPC::Open2; +$| = 1; + +# display extra information to STDERR +my $debug = 0; +if (scalar @ARGV) { + $debug = 1; +} + +# Globals + +my %state; +my $default_key; + +my $next_key = $$; + +# loop over binary prompts +for (;;) { + my ($control, $data) = ReadBinaryPrompt(); + my ($reply_control, $reply_data); + + if ($control == 0) { + if ($debug) { + print STDERR "agent: no packet to read\n"; + } + last; + } elsif ($control == 0x02) { + ($reply_control, $reply_data) = HandleAgentSelection($data); + } elsif ($control == 0x01) { + ($reply_control, $reply_data) = HandleContinuation($data); + } else { + if ($debug) { + print STDERR + "agent: unrecognized packet $control {$data} to read\n"; + } + ($reply_control, $reply_data) = (0x04, ""); + } + + WriteBinaryPrompt($reply_control, $reply_data); +} + +# Only willing to exit well if we've completed our authentication exchange + +if (scalar keys %state) { + if ($debug) { + print STDERR "The following sessions are still active:\n "; + print STDERR join ', ', keys %state; + print STDERR "\n"; + } + exit 1; +} else { + exit 0; +} + +sub HandleAgentSelection ($) { + my ($data) = @_; + + unless ( $data =~ /^([a-zA-Z0-9_]+\@?[a-zA-Z0-9_.]*)\/(.*)$/ ) { + return (0x04, ""); + } + + my ($agent_name, $payload) = ($1, $2); + if ($debug) { + print STDERR "agent: ". "agent=$agent_name, payload=$payload\n"; + } + + # this agent has a defined name + if ($agent_name ne "secret\@here") { + if ($debug) { + print STDERR "bad agent name: [$agent_name]\n"; + } + return (0x04, ""); + } + + # the selection request is accompanied with a hexadecimal cookie + my @tokens = split '\|', $payload; + + unless ((scalar @tokens) == 2) { + if ($debug) { + print STDERR "bad payload\n"; + } + return (0x04, ""); + } + + unless ($tokens[1] =~ /^[a-z0-9]+$/) { + if ($debug) { + print STDERR "bad server cookie\n"; + } + return (0x04, ""); + } + + my $shared_secret = IdentifyLocalSecret($tokens[0]); + + unless (defined $shared_secret) { + # make a secret up + if ($debug) { + print STDERR "agent: cannot authenticate user\n"; + } + $shared_secret = GetRandom(); + } + + my $local_cookie = GetRandom(); + $default_key = $next_key++; + + $state{$default_key} = $local_cookie ."|". $tokens[1] ."|". $shared_secret; + + if ($debug) { + print STDERR "agent: \$state{$default_key} = $state{$default_key}\n"; + } + + return (0x01, $default_key ."|". $local_cookie); +} + +sub HandleContinuation ($) { + my ($data) = @_; + + my ($key, $server_digest) = split '\|', $data; + + unless (defined $state{$key}) { + # retries and out of sequence prompts are not permitted + return (0x04, ""); + } + + my $expected_digest = CreateDigest($state{$key}); + my ($local_cookie, $remote_cookie, $shared_secret) + = split '\|', $state{$key}; + delete $state{$key}; + + unless ($expected_digest eq $server_digest) { + if ($debug) { + print STDERR "agent: don't trust server - faking reply\n"; + print STDERR "agent: got ($server_digest)\n"; + print STDERR "agent: expected ($expected_digest)\n"; + } + + ## FIXME: Agent should exchange a prompt with the client warning + ## that the server is faking us out. + + return (0x03, CreateDigest($expected_digest . $data . GetRandom())); + } + + if ($debug) { + print STDERR "agent: server appears to know the secret\n"; + } + + my $session_authenticated_ticket = + CreateDigest($remote_cookie."|".$shared_secret."|".$local_cookie); + + # FIXME: Agent should set a derived session key environment + # variable (available for the client (and its children) to sign + # future data exchanges. + + if ($debug) { + print STDERR "agent: should putenv(" + ."\"AUTH_SESSION_TICKET=$session_authenticated_ticket\")\n"; + } + + # return agent's authenticating digest + return (0x03, CreateDigest($shared_secret."|".$remote_cookie + ."|".$local_cookie)); +} + +sub ReadBinaryPrompt { + my $buffer = " "; + my $count = read(STDIN, $buffer, 5); + if ($count == 0) { + # no more packets to read + return (0, ""); + } + + if ($count != 5) { + # broken packet header + return (-1, ""); + } + + my ($length, $control) = unpack("N C", $buffer); + if ($length < 5) { + # broken packet length + return (-1, ""); + } + + my $data = ""; + $length -= 5; + while ($count = read(STDIN, $buffer, $length)) { + $data .= $buffer; + if ($count != $length) { + $length -= $count; + next; + } + + if ($debug) { + print STDERR "agent: ". "data is [$data]\n"; + } + + return ($control, $data); + } + + # broken packet data + return (-1, ""); +} + +sub WriteBinaryPrompt ($$) { + my ($control, $data) = @_; + + my $length = 5 + length($data); + if ($debug) { + printf STDERR "agent: ". "{%d|0x%.2x|%s}\n", $length, $control, $data; + } + my $bp = pack("N C a*", $length, $control, $data); + print STDOUT $bp; + if ($debug) { + printf STDERR "agent: ". "agent has replied\n"; + } +} + +## +## Here is where we parse the simple secret file +## The format of this file is a list of lines of the following form: +## +## user@client0.host.name secret_string1 +## user@client1.host.name secret_string2 +## user@client2.host.name secret_string3 +## + +sub IdentifyLocalSecret ($) { + my ($identifier) = @_; + my $secret; + + if (open SECRETS, "< ". (getpwuid($<))[7] ."/.secret\@here") { + my $line; + while (defined ($line = )) { + my ($id, $sec) = split /[\s]+/, $line; + if ((defined $id) && ($id eq $identifier)) { + $secret = $sec; + last; + } + } + close SECRETS; + } + + return $secret; +} + +## Here is where we generate a message digest + +sub CreateDigest ($) { + my ($data) = @_; + + my $pid = open2(\*MD5out, \*MD5in, "/usr/bin/md5sum -") + or die "you'll need /usr/bin/md5sum installed"; + + my $oldfd = select MD5in; $|=1; select $oldfd; + if ($debug) { + print STDERR "agent: ". "telling md5: <$data>\n"; + } + print MD5in "$data"; + close MD5in; + my $reply = ; + ($reply) = split /\s/, $reply; + if ($debug) { + print STDERR "agent: ". "md5 said: <$reply>\n"; + } + close MD5out; + + return $reply; +} + +## get a random number + +sub GetRandom { + + if ( -r "/dev/urandom" ) { + open RANDOM, "< /dev/urandom" or die "crazy"; + + my $i; + my $reply = ""; + + for ($i=0; $i<4; ++$i) { + my $buffer = " "; + while (read(RANDOM, $buffer, 4) != 4) { + ; + } + $reply .= sprintf "%.8x", unpack("N", $buffer); + if ($debug) { + print STDERR "growing reply: [$reply]\n"; + } + } + close RANDOM; + + return $reply; + } else { + print STDERR "agent: ". "[got linux?]\n"; + return "%.8x%.8x%.8x%.8x", time, time, time, time; + } + +} diff --git a/libpamc/test/modules/pam_secret.c b/libpamc/test/modules/pam_secret.c new file mode 100644 index 0000000..da607b1 --- /dev/null +++ b/libpamc/test/modules/pam_secret.c @@ -0,0 +1,669 @@ +/* + * $Id$ + * + * Copyright (c) 1999 Andrew G. Morgan + */ + +/* + * WARNING: AS WRITTEN THIS CODE IS NOT SECURE. THE MD5 IMPLEMENTATION + * NEEDS TO BE INTEGRATED MORE NATIVELY. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * This is a sample module that demonstrates the use of binary prompts + * and how they can be used to implement sophisticated authentication + * schemes. + */ + +struct ps_state_s { + int retval; /* last retval returned by the authentication fn */ + int state; /* what state the module was in when it + returned incomplete */ + + char *username; /* the name of the local user */ + + char server_cookie[33]; /* storage for 32 bytes of server cookie */ + char client_cookie[33]; /* storage for 32 bytes of client cookie */ + + char *secret_data; /* pointer to terminated secret_data */ + int invalid_secret; /* indication of whether the secret is valid */ + + pamc_bp_t current_prompt; /* place to store the current prompt */ + pamc_bp_t current_reply; /* place to receive the reply prompt */ +}; + +#define PS_STATE_ID "PAM_SECRET__STATE" +#define PS_AGENT_ID "secret@here" +#define PS_STATE_DEAD 0 +#define PS_STATE_INIT 1 +#define PS_STATE_PROMPT1 2 +#define PS_STATE_PROMPT2 3 + +#define MAX_LEN_HOSTNAME 512 +#define MAX_FILE_LINE_LEN 1024 + +/* + * Routine for generating 16*8 bits of random data represented in ASCII hex + */ + +static int generate_cookie(unsigned char *buffer_33) +{ + static const char hexarray[] = "0123456789abcdef"; + int i, fd; + + /* fill buffer_33 with 32 hex characters (lower case) + '\0' */ + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + D(("failed to open /dev/urandom")); + return 0; + } + read(fd, buffer_33 + 16, 16); + close(fd); + + /* expand top 16 bytes into 32 nibbles */ + for (i=0; i<16; ++i) { + buffer_33[2*i ] = hexarray[(buffer_33[16+i] & 0xf0)>>4]; + buffer_33[2*i+1] = hexarray[(buffer_33[16+i] & 0x0f)]; + } + + buffer_33[32] = '\0'; + + return 1; +} + +/* + * XXX - This is a hack, and is fundamentally insecure. Its subject to + * all sorts of attacks not to mention the fact that all our secrets + * will be displayed on the command line for someone doing 'ps' to + * see. This is just for programming convenience in this instance, it + * needs to be replaced with the md5 code. Although I am loath to + * add yet another instance of md5 code to the Linux-PAM source code. + * [Need to think of a cleaner way to do this for the distribution as + * a whole...] + */ + +#define COMMAND_FORMAT "/bin/echo -n '%s|%s|%s'|/usr/bin/md5sum -" + +int create_digest(const char *d1, const char *d2, const char *d3, + char *buffer_33) +{ + int length; + char *buffer; + FILE *pipe; + + length = strlen(d1)+strlen(d2)+strlen(d3)+sizeof(COMMAND_FORMAT); + buffer = malloc(length); + if (buffer == NULL) { + D(("out of memory")); + return 0; + } + + sprintf(buffer, COMMAND_FORMAT, d1,d2,d3); + + D(("executing pipe [%s]", buffer)); + pipe = popen(buffer, "r"); + memset(buffer, 0, length); + free(buffer); + + if (pipe == NULL) { + D(("failed to launch pipe")); + return 0; + } + + if (fgets(buffer_33, 33, pipe) == NULL) { + D(("failed to read digest")); + pclose(pipe); + return 0; + } + + if (strlen(buffer_33) != 32) { + D(("digest was not 32 chars")); + pclose(pipe); + return 0; + } + + pclose(pipe); + + D(("done [%s]", buffer_33)); + + return 1; +} + +/* + * method to attempt to instruct the application's conversation function + */ + +static int converse(pam_handle_t *pamh, struct ps_state_s *new) +{ + int retval; + struct pam_conv *conv; + + D(("called")); + + retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv); + if (retval == PAM_SUCCESS) { + struct pam_message msg; + struct pam_response *single_reply; + const struct pam_message *msg_ptr; + + memset(&msg, 0, sizeof(msg)); + msg.msg_style = PAM_BINARY_PROMPT; + msg.msg = (const char *) new->current_prompt; + msg_ptr = &msg; + + single_reply = NULL; + retval = conv->conv(1, &msg_ptr, &single_reply, conv->appdata_ptr); + if (retval == PAM_SUCCESS) { + if ((single_reply == NULL) || (single_reply->resp == NULL)) { + retval = PAM_CONV_ERR; + } else { + new->current_reply = (pamc_bp_t) single_reply->resp; + single_reply->resp = NULL; + } + } + + free(single_reply); + } + +#ifdef PAM_DEBUG + if (retval == PAM_SUCCESS) { + D(("reply has length=%d and control=%u", + PAM_BP_LENGTH(new->current_reply), + PAM_BP_CONTROL(new->current_reply))); + } + D(("returning %s", pam_strerror(pamh, retval))); +#endif + + return retval; +} + +/* + * identify the secret in question + */ + +#define SECRET_FILE_FORMAT "%s/.secret@here" + +char *identify_secret(char *identity, const char *user) +{ + struct passwd *pwd; + char *temp; + FILE *secrets; + int length_id; + + pwd = getpwnam(user); + if ((pwd == NULL) || (pwd->pw_dir == NULL)) { + D(("user [%s] is not known", user)); + return NULL; + } + + length_id = strlen(pwd->pw_dir) + sizeof(SECRET_FILE_FORMAT); + temp = malloc(length_id); + if (temp == NULL) { + D(("out of memory")); + pwd = NULL; + return NULL; + } + + sprintf(temp, SECRET_FILE_FORMAT, pwd->pw_dir); + pwd = NULL; + + D(("opening key file [%s]", temp)); + secrets = fopen(temp, "r"); + memset(temp, 0, length_id); + + if (secrets == NULL) { + D(("failed to open key file")); + return NULL; + } + + length_id = strlen(identity); + temp = malloc(MAX_FILE_LINE_LEN); + + for (;;) { + char *secret = NULL; + + if (fgets(temp, MAX_FILE_LINE_LEN, secrets) == NULL) { + fclose(secrets); + return NULL; + } + + D(("cf[%s][%s]", identity, temp)); + if (memcmp(temp, identity, length_id)) { + continue; + } + + D(("found entry")); + fclose(secrets); + + for (secret=temp+length_id; *secret; ++secret) { + if (!(*secret == ' ' || *secret == '\n' || *secret == '\t')) { + break; + } + } + + memmove(temp, secret, MAX_FILE_LINE_LEN-(secret-(temp+length_id))); + secret = temp; + + for (; *secret; ++secret) { + if (*secret == ' ' || *secret == '\n' || *secret == '\t') { + break; + } + } + + if (*secret) { + *secret = '\0'; + } + + D(("secret found [%s]", temp)); + + return temp; + } + + /* NOT REACHED */ +} + +/* + * function to perform the two message authentication process + * (with support for event driven conversation functions) + */ + +static int auth_sequence(pam_handle_t *pamh, + const struct ps_state_s *old, struct ps_state_s *new) +{ + const char *rhostname; + const char *rusername; + int retval; + + retval = pam_get_item(pamh, PAM_RUSER, (const void **) &rusername); + if ((retval != PAM_SUCCESS) || (rusername == NULL)) { + D(("failed to obtain an rusername")); + new->state = PS_STATE_DEAD; + return PAM_AUTH_ERR; + } + + retval = pam_get_item(pamh, PAM_RHOST, (const void **) &rhostname); + if ((retval != PAM_SUCCESS) || (rhostname == NULL)) { + D(("failed to identify local hostname: ", pam_strerror(pamh, retval))); + new->state = PS_STATE_DEAD; + return PAM_AUTH_ERR; + } + + D(("switch on new->state=%d [%s@%s]", new->state, rusername, rhostname)); + switch (new->state) { + + case PS_STATE_INIT: + { + const char *user = NULL; + + retval = pam_get_user(pamh, &user, NULL); + + if ((retval == PAM_SUCCESS) && (user == NULL)) { + D(("success but no username?")); + new->state = PS_STATE_DEAD; + retval = PAM_USER_UNKNOWN; + } + + if (retval != PAM_SUCCESS) { + if (retval == PAM_CONV_AGAIN) { + retval = PAM_INCOMPLETE; + } else { + new->state = PS_STATE_DEAD; + } + D(("state init failed: %s", pam_strerror(pamh, retval))); + return retval; + } + + /* nothing else in this 'case' can be retried */ + + new->username = strdup(user); + if (new->username == NULL) { + D(("out of memory")); + new->state = PS_STATE_DEAD; + return PAM_BUF_ERR; + } + + if (! generate_cookie(new->server_cookie)) { + D(("problem generating server cookie")); + new->state = PS_STATE_DEAD; + return PAM_ABORT; + } + + new->current_prompt = NULL; + PAM_BP_RENEW(&new->current_prompt, PAM_BPC_SELECT, + sizeof(PS_AGENT_ID) + strlen(rusername) + 1 + + strlen(rhostname) + 1 + 32); + sprintf(PAM_BP_WDATA(new->current_prompt), + PS_AGENT_ID "/%s@%s|%.32s", rusername, rhostname, + new->server_cookie); + + /* note, the BP is guaranteed by the spec to be terminated */ + D(("initialization packet [%s]", PAM_BP_DATA(new->current_prompt))); + + /* fall through */ + new->state = PS_STATE_PROMPT1; + + D(("fall through to state_prompt1")); + } + + case PS_STATE_PROMPT1: + { + int i, length; + + /* send {secret@here/jdoe@client.host|} */ + retval = converse(pamh, new); + if (retval != PAM_SUCCESS) { + if (retval == PAM_CONV_AGAIN) { + D(("conversation failed to complete")); + return PAM_INCOMPLETE; + } else { + new->state = PS_STATE_DEAD; + return retval; + } + } + + if (retval != PAM_SUCCESS) { + D(("failed to read ruser@rhost")); + new->state = PS_STATE_DEAD; + return PAM_AUTH_ERR; + } + + /* expect to receive the following {|} */ + if (new->current_reply == NULL) { + D(("conversation returned [%s] but gave no reply", + pam_strerror(pamh, retval))); + new->state = PS_STATE_DEAD; + return PAM_CONV_ERR; + } + + /* find | */ + length = PAM_BP_LENGTH(new->current_reply); + for (i=0; icurrent_reply)[i] == '|') { + break; + } + } + if (i >= length) { + D(("malformed response (no |) of length %d", length)); + new->state = PS_STATE_DEAD; + return PAM_CONV_ERR; + } + if ((length - ++i) != 32) { + D(("cookie is incorrect length (%d,%d) %d != 32", + length, i, length-i)); + new->state = PS_STATE_DEAD; + return PAM_CONV_ERR; + } + + /* copy client cookie */ + memcpy(new->client_cookie, PAM_BP_RDATA(new->current_reply)+i, 32); + + /* generate a prompt that is length(seqid) + length(|) + 32 long */ + PAM_BP_RENEW(&new->current_prompt, PAM_BPC_OK, i+32); + /* copy the head of the response prompt */ + memcpy(PAM_BP_WDATA(new->current_prompt), + PAM_BP_RDATA(new->current_reply), i); + PAM_BP_RENEW(&new->current_reply, 0, 0); + + /* look up the secret */ + new->invalid_secret = 0; + + if (new->secret_data == NULL) { + char *ruser_rhost; + + ruser_rhost = malloc(strlen(rusername)+2+strlen(rhostname)); + if (ruser_rhost == NULL) { + D(("out of memory")); + new->state = PS_STATE_DEAD; + return PAM_BUF_ERR; + } + sprintf(ruser_rhost, "%s@%s", rusername, rhostname); + new->secret_data = identify_secret(ruser_rhost, new->username); + + memset(ruser_rhost, 0, strlen(ruser_rhost)); + free(ruser_rhost); + } + + if (new->secret_data == NULL) { + D(("secret not found for user")); + new->invalid_secret = 1; + + /* need to make up a secret */ + new->secret_data = malloc(32 + 1); + if (new->secret_data == NULL) { + D(("out of memory")); + new->state = PS_STATE_DEAD; + return PAM_BUF_ERR; + } + if (! generate_cookie(new->secret_data)) { + D(("what's up - no fake cookie generated?")); + new->state = PS_STATE_DEAD; + return PAM_ABORT; + } + } + + /* construct md5[||] */ + if (! create_digest(new->client_cookie, new->server_cookie, + new->secret_data, + PAM_BP_WDATA(new->current_prompt)+i)) { + D(("md5 digesting failed")); + new->state = PS_STATE_DEAD; + return PAM_ABORT; + } + + /* prompt2 is now constructed - fall through to send it */ + } + + case PS_STATE_PROMPT2: + { + /* send {|md5[||]} */ + retval = converse(pamh, new); + if (retval != PAM_SUCCESS) { + if (retval == PAM_CONV_AGAIN) { + D(("conversation failed to complete")); + return PAM_INCOMPLETE; + } else { + new->state = PS_STATE_DEAD; + return retval; + } + } + + /* After we complete this section, we should not be able to + recall this authentication function. So, we force all + future calls into the weeds. */ + + new->state = PS_STATE_DEAD; + + /* expect reply:{md5[||]} */ + + { + int cf; + char expectation[33]; + + if (!create_digest(new->secret_data, new->server_cookie, + new->client_cookie, expectation)) { + new->state = PS_STATE_DEAD; + return PAM_ABORT; + } + + cf = strcmp(expectation, PAM_BP_RDATA(new->current_reply)); + memset(expectation, 0, sizeof(expectation)); + if (cf || new->invalid_secret) { + D(("failed to authenticate")); + return PAM_AUTH_ERR; + } + } + + D(("correctly authenticated :)")); + return PAM_SUCCESS; + } + + default: + new->state = PS_STATE_DEAD; + + case PS_STATE_DEAD: + + D(("state is currently dead/unknown")); + return PAM_AUTH_ERR; + } + + fprintf(stderr, "pam_secret: this should not be reached\n"); + return PAM_ABORT; +} + +static void clean_data(pam_handle_t *pamh, void *datum, int error_status) +{ + struct ps_state_s *data = datum; + + D(("liberating datum=%p", datum)); + + if (data) { + D(("renew prompt")); + PAM_BP_RENEW(&data->current_prompt, 0, 0); + D(("renew reply")); + PAM_BP_RENEW(&data->current_reply, 0, 0); + D(("overwrite datum")); + memset(data, 0, sizeof(struct ps_state_s)); + D(("liberate datum")); + free(data); + } + + D(("done.")); +} + +/* + * front end for the authentication function + */ + +int pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int retval; + struct ps_state_s *new_data; + const struct ps_state_s *old_data; + + D(("called")); + + new_data = calloc(1, sizeof(struct ps_state_s)); + if (new_data == NULL) { + D(("out of memory")); + return PAM_BUF_ERR; + } + new_data->retval = PAM_SUCCESS; + + retval = pam_get_data(pamh, PS_STATE_ID, (const void **) &old_data); + if (retval == PAM_SUCCESS) { + new_data->state = old_data->state; + memcpy(new_data->server_cookie, old_data->server_cookie, 32); + memcpy(new_data->client_cookie, old_data->client_cookie, 32); + if (old_data->username) { + new_data->username = strdup(old_data->username); + } + if (old_data->secret_data) { + new_data->secret_data = strdup(old_data->secret_data); + } + if (old_data->current_prompt) { + int length; + + length = PAM_BP_LENGTH(old_data->current_prompt); + PAM_BP_RENEW(&new_data->current_prompt, + PAM_BP_CONTROL(old_data->current_prompt), length); + PAM_BP_FILL(new_data->current_prompt, 0, length, + PAM_BP_RDATA(old_data->current_prompt)); + } + /* don't need to duplicate current_reply */ + } else { + old_data = NULL; + new_data->state = PS_STATE_INIT; + } + + D(("call auth_sequence")); + new_data->retval = auth_sequence(pamh, old_data, new_data); + D(("returned from auth_sequence")); + + retval = pam_set_data(pamh, PS_STATE_ID, new_data, clean_data); + if (retval != PAM_SUCCESS) { + D(("unable to store new_data")); + } else { + retval = new_data->retval; + } + + old_data = new_data = NULL; + + D(("done (%d)", retval)); + return retval; +} + +/* + * front end for the credential setting function + */ + +#define AUTH_SESSION_TICKET_ENV_FORMAT "AUTH_SESSION_TICKET=" + +int pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int retval; + const struct ps_state_s *old_data; + + D(("called")); + + /* XXX - need to pay attention to the various flavors of call */ + + /* XXX - need provide an option to turn this feature on/off: if + other modules want to supply an AUTH_SESSION_TICKET, we should + leave it up to the admin which module dominiates. */ + + retval = pam_get_data(pamh, PS_STATE_ID, (const void **) &old_data); + if (retval != PAM_SUCCESS) { + D(("no data to base decision on")); + return PAM_AUTH_ERR; + } + + /* + * If ok, export a derived shared secret session ticket to the + * client's PAM environment - the ticket has the form + * + * AUTH_SESSION_TICKET = + * md5[||] + * + * This is a precursor to supporting a spoof resistant trusted + * path mechanism. This shared secret ticket can be used to add + * a hard-to-guess checksum to further authentication data. + */ + + retval = old_data->retval; + if (retval == PAM_SUCCESS) { + char envticket[sizeof(AUTH_SESSION_TICKET_ENV_FORMAT)+32]; + + memcpy(envticket, AUTH_SESSION_TICKET_ENV_FORMAT, + sizeof(AUTH_SESSION_TICKET_ENV_FORMAT)); + + if (! create_digest(old_data->server_cookie, old_data->secret_data, + old_data->client_cookie, + envticket+sizeof(AUTH_SESSION_TICKET_ENV_FORMAT)-1 + )) { + D(("unable to generate a digest for session ticket")); + return PAM_ABORT; + } + + D(("putenv[%s]", envticket)); + retval = pam_putenv(pamh, envticket); + memset(envticket, 0, sizeof(envticket)); + } + + old_data = NULL; + D(("done (%d)", retval)); + + return retval; +} diff --git a/libpamc/test/regress/run_test.sh b/libpamc/test/regress/run_test.sh new file mode 100755 index 0000000..6922f03 --- /dev/null +++ b/libpamc/test/regress/run_test.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +LD_LIBRARY_PATH=../.. ; export LD_LIBRARY_PATH +PAMC_AGENT_PATH="../agents" ; export PAMC_AGENT_PATH + +./test.libpamc diff --git a/libpamc/test/regress/test.libpamc.c b/libpamc/test/regress/test.libpamc.c new file mode 100644 index 0000000..e6d6bb3 --- /dev/null +++ b/libpamc/test/regress/test.libpamc.c @@ -0,0 +1,343 @@ +/* + * This is a small test program for testing libpamc against the + * secret@here agent. It does the same as the test.secret@here perl + * script in this directory, but via the libpamc API. + */ + +#include +#include +#include +#include +#include + +struct internal_packet { + int length; + int at; + char *buffer; +}; + + +void append_data(struct internal_packet *packet, int extra, const char *data) +{ + if ((extra + packet->at) >= packet->length) { + if (packet->length == 0) { + packet->length = 1000; + } + /* make sure we have at least a char extra space available */ + while (packet->length <= (extra + packet->at)) { + packet->length <<= 1; + } + packet->buffer = realloc(packet->buffer, packet->length); + if (packet->buffer == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + } + + if (data != NULL) { + memcpy(packet->at + packet->buffer, data, extra); + } + packet->at += extra; + + /* assisting string manipulation */ + packet->buffer[packet->at] = '\0'; +} + +void append_string(struct internal_packet *packet, const char *string, + int with_nul) +{ + append_data(packet, strlen(string) + (with_nul ? 1:0), string); +} + +char *identify_secret(char *identity) +{ + struct internal_packet temp_packet; + FILE *secrets; + int length_id; + + temp_packet.length = temp_packet.at = 0; + temp_packet.buffer = NULL; + + append_string(&temp_packet, "/home/", 0); + append_string(&temp_packet, getlogin(), 0); + append_string(&temp_packet, "/.secret@here", 1); + + secrets = fopen(temp_packet.buffer, "r"); + if (secrets == NULL) { + fprintf(stderr, "server: failed to open\n [%s]\n", + temp_packet.buffer); + exit(1); + } + + length_id = strlen(identity); + for (;;) { + char *secret = NULL; + temp_packet.at = 0; + + if (fgets(temp_packet.buffer, temp_packet.length, secrets) == NULL) { + fclose(secrets); + return NULL; + } + + if (memcmp(temp_packet.buffer, identity, length_id)) { + continue; + } + + fclose(secrets); + for (secret=temp_packet.buffer; *secret; ++secret) { + if (*secret == ' ' || *secret == '\n' || *secret == '\t') { + break; + } + } + for (; *secret; ++secret) { + if (!(*secret == ' ' || *secret == '\n' || *secret == '\t')) { + break; + } + } + + for (temp_packet.buffer=secret; *temp_packet.buffer; + ++temp_packet.buffer) { + if (*temp_packet.buffer == ' ' || *temp_packet.buffer == '\n' + || *temp_packet.buffer == '\t') { + break; + } + } + if (*temp_packet.buffer) { + *temp_packet.buffer = '\0'; + } + + return secret; + } + + /* NOT REACHED */ +} + +/* + * This is a hack, and is fundamentally insecure. All our secrets will be + * displayed on the command line for someone doing 'ps' to see. This + * is just for programming convenience in this instance, since this + * program is simply a regression test. The pam_secret module should + * not do this, but make use of md5 routines directly. + */ + +char *create_digest(int length, const char *raw) +{ + struct internal_packet temp_packet; + FILE *pipe; + + temp_packet.length = temp_packet.at = 0; + temp_packet.buffer = NULL; + + append_string(&temp_packet, "echo -n '", 0); + append_string(&temp_packet, raw, 0); + append_string(&temp_packet, "'|/usr/bin/md5sum -", 1); + + fprintf(stderr, "am attempting to run [%s]\n", temp_packet.buffer); + + pipe = popen(temp_packet.buffer, "r"); + if (pipe == NULL) { + fprintf(stderr, "server: failed to run\n [%s]\n", temp_packet.buffer); + exit(1); + } + + temp_packet.at = 0; + append_data(&temp_packet, 32, NULL); + + if (fgets(temp_packet.buffer, 33, pipe) == NULL) { + fprintf(stderr, "server: failed to read digest\n"); + exit(1); + } + if (strlen(temp_packet.buffer) != 32) { + fprintf(stderr, "server: digest was not 32 chars?? [%s]\n", + temp_packet.buffer); + exit(1); + } + + pclose(pipe); + + return temp_packet.buffer; +} + +void packet_to_prompt(pamc_bp_t *prompt_p, uint8_t control, + struct internal_packet *packet) +{ + PAM_BP_RENEW(prompt_p, control, packet->at); + PAM_BP_FILL(*prompt_p, 0, packet->at, packet->buffer); + packet->at = 0; +} + +void prompt_to_packet(pamc_bp_t prompt, struct internal_packet *packet) +{ + int data_length; + + data_length = PAM_BP_LENGTH(prompt); + packet->at = 0; + append_data(packet, data_length, NULL); + + PAM_BP_EXTRACT(prompt, 0, data_length, packet->buffer); + + fprintf(stderr, "server received[%d]: {%d|0x%.2x|%s}\n", + data_length, + PAM_BP_SIZE(prompt), PAM_BP_RCONTROL(prompt), + PAM_BP_RDATA(prompt)); +} + +int main(int argc, char **argv) +{ + pamc_handle_t pch; + pamc_bp_t prompt = NULL; + struct internal_packet packet_data, *packet; + char *temp_string, *secret, *user, *a_cookie, *seqid, *digest; + const char *cookie = "123451234512345"; + int retval; + + packet = &packet_data; + packet->length = 0; + packet->at = 0; + packet->buffer = NULL; + + pch = pamc_start(); + if (pch == NULL) { + fprintf(stderr, "server: unable to get a handle from libpamc\n"); + exit(1); + } + + temp_string = getlogin(); + if (temp_string == NULL) { + fprintf(stderr, "server: who are you?\n"); + exit(1); + } +#define DOMAIN "@local.host" + user = malloc(1+strlen(temp_string)+strlen(DOMAIN)); + if (user == NULL) { + fprintf(stderr, "server: out of memory for user id\n"); + exit(1); + } + sprintf(user, "%s%s", temp_string, DOMAIN); + + append_string(packet, "secret@here/", 0); + append_string(packet, user, 0); + append_string(packet, "|", 0); + append_string(packet, cookie, 0); + packet_to_prompt(&prompt, PAM_BPC_SELECT, packet); + + /* get the library to accept the first packet (which should load + the secret@here agent) */ + + retval = pamc_converse(pch, &prompt); + fprintf(stderr, "server: after conversation\n"); + if (PAM_BP_RCONTROL(prompt) != PAM_BPC_OK) { + fprintf(stderr, "server: prompt had unexpected control type: %u\n", + PAM_BP_RCONTROL(prompt)); + exit(1); + } + + fprintf(stderr, "server: got a prompt back\n"); + + prompt_to_packet(prompt, packet); + + temp_string = strtok(packet->buffer, "|"); + if (temp_string == NULL) { + fprintf(stderr, "server: prompt does not contain anything"); + exit(1); + } + seqid = strdup(temp_string); + if (seqid == NULL) { + fprintf(stderr, "server: unable to store sequence id\n"); + } + + temp_string = strtok(NULL, "|"); + if (temp_string == NULL) { + fprintf(stderr, "server: no cookie from agent\n"); + exit(1); + } + a_cookie = strdup(temp_string); + if (a_cookie == NULL) { + fprintf(stderr, "server: no memory to store agent cookie\n"); + exit(1); + } + + fprintf(stderr, "server: agent responded with {%s|%s}\n", seqid, a_cookie); + secret = identify_secret(user); + fprintf(stderr, "server: secret=%s\n", secret); + + /* now, we construct the response */ + packet->at = 0; + append_string(packet, a_cookie, 0); + append_string(packet, "|", 0); + append_string(packet, cookie, 0); + append_string(packet, "|", 0); + append_string(packet, secret, 0); + + fprintf(stderr, "server: get digest of %s\n", packet->buffer); + + digest = create_digest(packet->at, packet->buffer); + + fprintf(stderr, "server: secret=%s, digest=%s\n", secret, digest); + + packet->at = 0; + append_string(packet, seqid, 0); + append_string(packet, "|", 0); + append_string(packet, digest, 0); + packet_to_prompt(&prompt, PAM_BPC_OK, packet); + + retval = pamc_converse(pch, &prompt); + fprintf(stderr, "server: after 2nd conversation\n"); + if (PAM_BP_RCONTROL(prompt) != PAM_BPC_DONE) { + fprintf(stderr, "server: 2nd prompt had unexpected control type: %u\n", + PAM_BP_RCONTROL(prompt)); + exit(1); + } + + prompt_to_packet(prompt, packet); + PAM_BP_RENEW(&prompt, 0, 0); + + temp_string = strtok(packet->buffer, "|"); + if (temp_string == NULL) { + fprintf(stderr, "no digest from agent\n"); + exit(1); + } + temp_string = strdup(temp_string); + + packet->at = 0; + append_string(packet, secret, 0); + append_string(packet, "|", 0); + append_string(packet, cookie, 0); + append_string(packet, "|", 0); + append_string(packet, a_cookie, 0); + + fprintf(stderr, "server: get digest of %s\n", packet->buffer); + + digest = create_digest(packet->at, packet->buffer); + + fprintf(stderr, "server: digest=%s\n", digest); + + if (strcmp(digest, temp_string)) { + fprintf(stderr, "server: agent doesn't know the secret\n"); + fprintf(stderr, "server: agent says: [%s]\n" + "server: server says: [%s]\n", temp_string, digest); + exit(1); + } else { + fprintf(stderr, "server: agent seems to know the secret\n"); + + packet->at = 0; + append_string(packet, cookie, 0); + append_string(packet, "|", 0); + append_string(packet, secret, 0); + append_string(packet, "|", 0); + append_string(packet, a_cookie, 0); + + digest = create_digest(packet->at, packet->buffer); + + fprintf(stderr, "server: putenv(\"AUTH_SESSION_TICKET=%s\")\n", + digest); + } + + + retval = pamc_end(&pch); + + fprintf(stderr, "server: agent(s) were %shappy to terminate\n", + retval == PAM_BPC_TRUE ? "":"un"); + + exit(!retval); +} diff --git a/libpamc/test/regress/test.secret@here b/libpamc/test/regress/test.secret@here new file mode 100755 index 0000000..67fe22e --- /dev/null +++ b/libpamc/test/regress/test.secret@here @@ -0,0 +1,151 @@ +#!/usr/bin/perl + +## +## this is a test script for regressing changes to the secret@here PAM +## agent +## + +$^W = 1; +use strict; +use IPC::Open2; + +$| = 1; + +my $whoami = `/usr/bin/whoami`; chomp $whoami; +my $cookie = "12345"; +my $user_domain = "$whoami\@local.host"; + +my $pid = open2(\*Reader, \*Writer, "../agents/secret\@here blah") + or die "failed to load secret\@here agent"; + +unless (-f (getpwuid($<))[7]."/.secret\@here") { + print STDERR "server: ". "no " .(getpwuid($<))[7]. "/.secret\@here file\n"; + die "no config file"; +} + +WriteBinaryPrompt(\*Writer, 0x02, "secret\@here/$user_domain|$cookie"); + +my ($control, $data) = ReadBinaryPrompt(\*Reader); + +print STDERR "server: ". "reply: control=$control, data=$data\n"; +if ($control != 1) { + die "expected 1 (OK) for the first agent reply; got $control"; +} +my ($seqid, $a_cookie) = split '\|', $data; + +# server needs to convince agent that it knows the secret before +# agent will give a valid response +my $secret = IdentifyLocalSecret($user_domain); +my $digest = CreateDigest($a_cookie."|".$cookie."|".$secret); + +print STDERR "server: ". "digest = $digest\n"; +WriteBinaryPrompt(\*Writer, 0x01, "$seqid|$digest"); + +# The agent will authenticate us and then reply with its +# authenticating digest. we check that before we're done. + +($control, $data) = ReadBinaryPrompt(\*Reader); +if ($control != 0x03) { + die "server: agent did not reply with a 'done' prompt ($control)\n"; +} + +unless ($data eq CreateDigest($secret."|".$cookie."|".$a_cookie)) { + die "server: agent is not authenticated\n"; +} + +print STDERR "server: agent appears to know secret\n"; + +my $session_authenticated_ticket + = CreateDigest($cookie."|".$secret."|".$a_cookie); + +print STDERR "server: should putenv(" + ."\"AUTH_SESSION_TICKET=$session_authenticated_ticket\")\n"; + +exit 0; + +sub CreateDigest ($) { + my ($data) = @_; + + my $pid = open2(\*MD5out, \*MD5in, "/usr/bin/md5sum -") + or die "you'll need /usr/bin/md5sum installed"; + + my $oldfd = select MD5in; $|=1; select $oldfd; + print MD5in "$data"; + close MD5in; + my $reply = ; + ($reply) = split /\s/, $reply; + print STDERR "server: ". "md5 said: <$reply>\n"; + close MD5out; + + return $reply; +} + +sub ReadBinaryPrompt ($) { + my ($fd) = @_; + + my $buffer = " "; + my $count = read($fd, $buffer, 5); + if ($count == 0) { + # no more packets to read + return (0, ""); + } + + if ($count != 5) { + # broken packet header + return (-1, ""); + } + + my ($length, $control) = unpack("N C", $buffer); + if ($length < 5) { + # broken packet length + return (-1, ""); + } + + my $data = ""; + $length -= 5; + while ($count = read($fd, $buffer, $length)) { + $data .= $buffer; + if ($count != $length) { + $length -= $count; + next; + } + + print STDERR "server: ". "data is [$data]\n"; + + return ($control, $data); + } + + # broken packet data + return (-1, ""); +} + +sub WriteBinaryPrompt ($$$) { + my ($fd, $control, $data) = @_; + + my $length = 5 + length($data); + printf STDERR "server: ". "{%d|0x%.2x|%s}\n", $length, $control, $data; + my $bp = pack("N C a*", $length, $control, $data); + print $fd $bp; + + print STDERR "server: ". "control passed to agent\@here\n"; +} + +sub IdentifyLocalSecret ($) { + my ($identifier) = @_; + my $secret; + + my $whoami = `/usr/bin/whoami` ; chomp $whoami; + if (open SECRETS, "< " .(getpwuid($<))[7]. "/.secret\@here") { + my $line; + while (defined ($line = )) { + my ($id, $sec) = split /[\s]/, $line; + if ((defined $id) && ($id eq $identifier)) { + $secret = $sec; + last; + } + } + close SECRETS; + } + + return $secret; +} diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..2be2128 --- /dev/null +++ b/meson.build @@ -0,0 +1,635 @@ +project('Linux-PAM', 'c', + version: '1.7.0', + license: 'BSD-3-Clause OR GPL-2.0-or-later', + default_options: [ + 'b_pie=true', + 'prefix=/usr', + 'warning_level=2', + ], + meson_version: '>= 0.62.0', +) + +fs = import('fs') +pkgconfig = import('pkgconfig') + + +prefixdir = get_option('prefix') +if not fs.is_absolute(prefixdir) + error('Prefix is not absolute: "@0@"'.format(prefixdir)) +endif +datadir = prefixdir / get_option('datadir') +includedir = prefixdir / get_option('includedir') / 'security' +libdir = prefixdir / get_option('libdir') +localedir = prefixdir / get_option('localedir') +localstatedir = '/' / get_option('localstatedir') +mandir = prefixdir / get_option('mandir') +sbindir = prefixdir / get_option('sbindir') +sysconfdir = prefixdir / get_option('sysconfdir') +securedir = get_option('securedir') +if securedir == '' + securedir = libdir / 'security' +endif + + +cc = meson.get_compiler('c') +cdata = configuration_data() + +null_dep = dependency('', required: false) + +cdata.set('_GNU_SOURCE', 1) +cdata.set_quoted('PACKAGE', meson.project_name()) +cdata.set_quoted('PAM_VERSION', meson.project_version()) +cdata.set('UNUSED', '__attribute__((__unused__))') +cdata.set('PAM_NO_HEADER_FUNCTIONS', 1, description: 'Disable function bodies in headers') +cdata.set_quoted('SYSCONFDIR', sysconfdir) +cdata.set_quoted('LTDIR', '') +# used in configure_file() only, so not quoted +cdata.set('sbindir', sbindir) + +cdata.set('PAM_DEBUG', get_option('pam-debug') ? 1 : false) +cdata.set('PAM_LOCKING', get_option('pamlocking') ? 1 : false) +cdata.set('PAM_READ_BOTH_CONFS', get_option('read-both-confs') ? 1 : false) +cdata.set10('DEFAULT_USERGROUPS_SETTING', get_option('usergroups')) +cdata.set('PAM_USERTYPE_UIDMIN', get_option('uidmin')) +cdata.set('PAM_USERTYPE_OVERFLOW_UID', get_option('kernel-overflow-uid')) +cdata.set('PAM_MISC_CONV_BUFSIZE', get_option('misc-conv-bufsize')) + +cdata.set_quoted('_PAM_ISA', + get_option('isadir') != '' ? get_option('isadir') : '../..' / fs.name(libdir) / 'security') + +docdir = get_option('docdir') +if docdir == '' + docdir = datadir / 'doc' / meson.project_name() +endif + +htmldir = get_option('htmldir') +if htmldir == '' + htmldir = docdir +endif + +pdfdir = get_option('pdfdir') +if pdfdir == '' + pdfdir = docdir +endif + +sconfigdir = get_option('sconfigdir') +if sconfigdir == '' + sconfigdir = sysconfdir / 'security' +endif +cdata.set_quoted('SCONFIG_DIR', sconfigdir) +# used in configure_file() only, so not quoted +cdata.set('SCONFIGDIR', sconfigdir) + +vendordir = get_option('vendordir') +if get_option('vendordir') == '' + vendor_sconfigdir = sconfigdir + vendor_sysconfdir = sysconfdir + stringparam_vendordir = [] + stringparam_profileconditions = 'without_vendordir' +else + vendor_sconfigdir = vendordir / 'security' + vendor_sysconfdir = vendordir + cdata.set_quoted('VENDORDIR', vendordir) + cdata.set_quoted('VENDOR_SCONFIG_DIR', vendor_sconfigdir) + stringparam_vendordir = ['--stringparam', 'vendordir', vendordir] + stringparam_profileconditions = 'with_vendordir' +endif + +randomdev_option = get_option('randomdev') +if randomdev_option == '' or randomdev_option == 'yes' + cdata.set_quoted('PAM_PATH_RANDOMDEV', '/dev/urandom') +elif randomdev_option != 'no' + cdata.set_quoted('PAM_PATH_RANDOMDEV', randomdev_option) +endif + +xauth_option = get_option('xauth') +if xauth_option != '' and xauth_option != '/usr/X11R6/bin/xauth' + cdata.set_quoted('PAM_PATH_XAUTH', xauth_option) +endif + + +# meson defines -D_STRIP_FILE_OFFSET_BITS=64 even for 64-bit systems, this +# forces glibc to use function names with the same semantics but with "64" +# suffix, and that leads to discrepancies in the build artefacts produced +# by different build systems. +if cc.sizeof('long') >= 8 + add_project_arguments( + '-U_FILE_OFFSET_BITS', + language: 'c') +endif + + +try_cc_flags = [ + '-Wbad-function-cast', + '-Wcast-align', + '-Wcast-align=strict', + '-Wcast-qual', + '-Wdeprecated', + '-Wformat=2', + '-Winit-self', + '-Wmain', + '-Wmissing-declarations', + '-Wmissing-format-attribute', + '-Wmissing-prototypes', + '-Wnull-dereference', + '-Wpointer-arith', + '-Wreturn-type', + '-Wshadow', + '-Wstrict-prototypes', + '-Wundef', + '-Wuninitialized', + '-Wunused', + '-Wwrite-strings', +] +add_project_arguments( + cc.get_supported_arguments(try_cc_flags), + language: 'c') + +add_project_link_arguments( + # --as-needed and --no-undefined are enabled by default + cc.get_supported_link_arguments([ + '-Wl,--fatal-warnings', + '-Wl,-O1', + ]), + language: 'c') + +exe_link_args = cc.get_supported_link_arguments([ + '-Wl,-z,relro', + '-Wl,-z,now', +]) + + +check_headers = [ + 'crypt.h', + 'paths.h', + 'sys/random.h', +] +foreach h: check_headers + if cc.has_header(h) + cdata.set('HAVE_' + h.underscorify().to_upper(), 1) + endif +endforeach + + +check_functions = [ + ['close_range', '#include '], + ['explicit_bzero', '#include '], + ['getdomainname', '#include '], + ['getgrgid_r', '#include '], + ['getgrnam_r', '#include '], + ['getgrouplist', '#include '], + ['getmntent_r', '#include '], + ['getpwnam', '#include '], + ['getpwnam_r', '#include '], + ['getpwuid_r', '#include '], + ['getrandom', '#include '], + ['getspnam_r', '#include '], + ['getutent_r', '#include '], + ['innetgr', '#include '], + ['memset_explicit', '#include '], + ['quotactl', '#include '], + ['ruserok', '#include '], + ['ruserok_af', '#include '], + ['unshare', '#include '], +] +foreach ident: check_functions + if cc.has_function(ident[0], prefix: ident[1], args: '-D_GNU_SOURCE') + cdata.set('HAVE_' + ident[0].to_upper(), 1) + endif +endforeach + +enable_pam_keyinit = cc.sizeof('__NR_keyctl', prefix: '#include ') > 0 + +if get_option('mailspool') != '' + cdata.set_quoted('PAM_PATH_MAILDIR', get_option('mailspool')) +else + have = cc.sizeof('_PATH_MAILDIR', prefix: '#include ') > 0 + cdata.set('PAM_PATH_MAILDIR', have ? '_PATH_MAILDIR' : '"/var/spool/mail"') +endif + + +libdl = dependency('dl') + + +if not get_option('i18n').disabled() + libintl = dependency('intl', required: get_option('i18n')) + if libintl.found() + cdata.set('ENABLE_NLS', 1) + cdata.set_quoted('LOCALEDIR', localedir) + foreach f: ['bindtextdomain', + 'dngettext', + ] + if cc.has_function(f, prefix: '#include ', + args: '-D_GNU_SOURCE', + dependencies: libintl) + cdata.set('HAVE_' + f.to_upper(), 1) + endif + endforeach + endif +endif + + +libaudit = dependency('audit', required: get_option('audit')) +if libaudit.found() + cdata.set('HAVE_LIBAUDIT', 1) + foreach ident: ['struct audit_tty_status'] + if cc.sizeof(ident, prefix: '#include ') > 0 + cdata.set('HAVE_' + ident.underscorify().to_upper(), 1) + endif + endforeach + foreach ident: [['struct audit_tty_status', 'log_passwd']] + if cc.has_member(ident[0], ident[1], + prefix: '#include ', + args: '-D_GNU_SOURCE') + cdata.set('HAVE_@0@_@1@'.format(ident[0], ident[1]).underscorify().to_upper(), 1) + endif + endforeach +endif + + +libcrypt = dependency('libcrypt', 'libxcrypt', required: false) +if not libcrypt.found() + libcrypt = cc.find_library('crypt') +endif +crypt_h = cdata.get('HAVE_CRYPT_H') == 1 ? '''#include ''' : '''#include ''' +foreach f: ['crypt_r'] + if cc.has_function(f, prefix: crypt_h, + args: '-D_GNU_SOURCE', + dependencies: libcrypt) + cdata.set('HAVE_' + f.to_upper(), 1) + endif +endforeach + + +libeconf = dependency('libeconf', version: '>= 0.5.0', required: get_option('econf')) +if libeconf.found() + cdata.set('USE_ECONF', 1) + have_econf_readconfig = true + if stringparam_vendordir != [] + stringparam_profileconditions += ';with_vendordir_and_with_econf' + endif + + foreach f: ['econf_newKeyFile_with_options', + 'econf_readConfigWithCallback', + ] + if cc.has_function(f, prefix: '#include ', + args: '-D_GNU_SOURCE', + dependencies: libeconf) + cdata.set('HAVE_' + f.to_upper(), 1) + else + have_econf_readconfig = false + endif + if have_econf_readconfig + cdata.set('HAVE_ECONF_READCONFIG', 1) + endif + endforeach +else + if stringparam_vendordir != [] + stringparam_profileconditions += ';with_vendordir_and_without_econf' + endif +endif + + +libselinux = dependency('libselinux', required: get_option('selinux')) +if libselinux.found() + cdata.set('WITH_SELINUX', 1) + foreach f: ['getseuser', 'setkeycreatecon'] + if cc.has_function(f, prefix: '#include ', + dependencies: libselinux) + cdata.set('HAVE_' + f.to_upper(), 1) + endif + endforeach +endif + + +if get_option('openssl').disabled() + libcrypto = null_dep +else + libcrypto = dependency('libcrypto', required: false) +endif +if libcrypto.found() and cc.has_function('EVP_MAC_CTX_new', + prefix: '#include ', + dependencies: libcrypto) + cdata.set('WITH_OPENSSL', 1) + stringparam_profileconditions += ';openssl_hmac' +else + stringparam_profileconditions += ';no_openssl_hmac' +endif + + +libsystemd = dependency('libsystemd', version: '>= 254', required: get_option('logind')) +cdata.set('USE_LOGIND', libsystemd.found() ? 1 : false) + +systemdunitdir = get_option('systemdunitdir') +if systemdunitdir == '' + systemdunitdir = prefixdir / 'lib' / 'systemd' / 'system' + systemd = dependency('systemd', required: false) + if systemd.found() + systemdunitdir = systemd.get_variable( + pkgconfig: 'systemdsystemunitdir', + default_value: systemdunitdir, + ) + endif +endif + + +feature_pam_userdb = get_option('pam_userdb') +if feature_pam_userdb.disabled() + enable_pam_userdb = false +else + use_db = get_option('db') + have = false + if use_db == 'db' or use_db == 'auto' + db_uniquename = get_option('db-uniquename') + name = 'db' + db_uniquename + libdb = cc.find_library(name, required: false) + if libdb.found() + foreach f: ['dbm_open' + db_uniquename, 'dbm_open'] + have = cc.links(''' + #define _GNU_SOURCE + #define DB_DBM_HSEARCH 1 + #define HAVE_DBM 1 + #include + int main(void) {return !@0@("", 0, 0);} + '''.format(f), + args: '-l' + name, + name: '@0@ in @1@'.format(f, 'lib' + name)) + if have + break + endif + endforeach + if have + use_db = 'db' + cdata.set('HAVE_DB_H', 1) + endif + endif + endif + if use_db == 'gdbm' or use_db == 'auto' + libdb = cc.find_library('gdbm', required: false) + if libdb.found() + have = cc.has_function('gdbm_store', prefix: '#include ', + args: '-D_GNU_SOURCE', dependencies: libdb) + if have + use_db = 'gdbm' + cdata.set('HAVE_GDBM_H', 1) + if cc.compiles('''#include + int db_close(void *dbm) {return gdbm_close(dbm);}''', + args: '-D_GNU_SOURCE', + name: 'gdbm_close returns int') + cdata.set('GDBM_CLOSE_RETURNS_INT', 1) + endif + endif + endif + endif + if use_db == 'ndbm' or use_db == 'auto' + libdb = cc.find_library('ndbm', required: false) + if libdb.found() + have = cc.has_function('dbm_store', prefix: '#include ', + args: '-D_GNU_SOURCE', dependencies: libdb) + if have + use_db = 'ndbm' + cdata.set('HAVE_NDBM_H', 1) + endif + endif + endif + if not have and feature_pam_userdb.enabled() + error('pam_userdb module is enabled but the db library is not available') + endif + enable_pam_userdb = have +endif + + +feature_pam_lastlog = get_option('pam_lastlog') +if feature_pam_lastlog.disabled() + enable_pam_lastlog = false +else + libutil = cc.find_library('util', required: false) + have = cc.has_function('logwtmp', prefix: '#include ', + dependencies: libutil) + if not have and feature_pam_lastlog.enabled() + error('pam_lastlog module is enabled but logwtmp() is not available') + endif + enable_pam_lastlog = have +endif + + +enable_pam_unix = not get_option('pam_unix').disabled() +if enable_pam_unix + if get_option('lckpwdf') + cdata.set('USE_LCKPWDF', 1) + foreach f: ['lckpwdf'] + if cc.has_function(f, prefix: '#include ') + cdata.set('HAVE_' + f.to_upper(), 1) + endif + endforeach + endif + + feature_nis = get_option('nis') + if feature_nis.disabled() + enable_nis = false + libtirpc = null_dep + libnsl = null_dep + else + # If libtirpc is available, prefer that over the system implementation. + libtirpc = dependency('libtirpc', required: false) + enable_nis = true + foreach f: ['getrpcport', + 'rpcb_getaddr', + ] + if cc.has_function(f, prefix: '#include ', + dependencies: libtirpc) + cdata.set('HAVE_' + f.to_upper(), 1) + cdata.set('HAVE_DECL_' + f.to_upper(), 1) + endif + endforeach + + libnsl = dependency('libnsl', required: feature_nis) + enable_nis = libnsl.found() + + if enable_nis + foreach f: ['yp_bind', + 'yp_get_default_domain', + 'yp_match', + 'yp_unbind', + ] + if not cc.has_function(f, prefix: '''#include + #include ''', + dependencies: [libnsl, libtirpc]) + enable_nis = false + libnsl = null_dep + break + endif + endforeach + endif + + if enable_nis + cdata.set('HAVE_NIS', 1) + elif feature_nis.enabled() + error('NIS is enabled but required interfaces are not available') + endif + endif +endif + + +feature_docs = get_option('docs') +enable_docs = not feature_docs.disabled() +if enable_docs + prog_xsltproc = find_program( + 'xsltproc', + required: feature_docs, + disabler: true, + ) + prog_xmllint = find_program( + 'xmllint', + required: feature_docs, + disabler: true, + ) + prog_w3m = find_program( + 'w3m', + required: false, + ) + if prog_w3m.found() + browser = [prog_w3m, '-T', 'text/html', '-dump'] + else + prog_elinks = find_program( + 'elinks', + required: feature_docs, + disabler: true, + ) + browser = [prog_elinks, '-no-numbering', '-no-references', '-dump'] + endif + prog_fop = find_program( + 'fop', + required: feature_docs, + disabler: true, + ) + + prog_xmlcatalog = find_program( + 'xmlcatalog', + required: feature_docs, + disabler: true, + ) + xml_catalog = get_option('xml-catalog') + if xml_catalog == '' + xml_catalog = '/etc/xml/catalog' + endif + + have = prog_xmlcatalog.found() and run_command( + ['test', '-f', xml_catalog], + check: feature_docs.enabled() + ).returncode() == 0 + if not have + xml_catalog = disabler() + endif + + docbook_rng = get_option('docbook-rng') + have = run_command( + [prog_xmlcatalog, '--noout', xml_catalog, docbook_rng], + check: feature_docs.enabled() + ).returncode() == 0 + if not have + docbook_rng = disabler() + endif + + html_stylesheet = get_option('html-stylesheet') + have = run_command( + [prog_xmlcatalog, '--noout', xml_catalog, html_stylesheet], + check: feature_docs.enabled() + ).returncode() == 0 + if not have + html_stylesheet = disabler() + endif + + txt_stylesheet = get_option('txt-stylesheet') + have = run_command( + [prog_xmlcatalog, '--noout', xml_catalog, txt_stylesheet], + check: feature_docs.enabled() + ).returncode() == 0 + if not have + txt_stylesheet = disabler() + endif + + man_stylesheet = get_option('man-stylesheet') + have = run_command( + [prog_xmlcatalog, '--noout', xml_catalog, man_stylesheet], + check: feature_docs.enabled() + ).returncode() == 0 + if not have + man_stylesheet = disabler() + endif + + pdf_stylesheet = get_option('pdf-stylesheet') + have = run_command( + [prog_xmlcatalog, '--noout', xml_catalog, pdf_stylesheet], + check: feature_docs.enabled() + ).returncode() == 0 + if not have + pdf_stylesheet = disabler() + endif + + stringparam_profileconditions = [ + '--stringparam', + 'profile.condition', + stringparam_profileconditions + ] +else + prog_xsltproc = disabler() + prog_xmllint = disabler() + prog_fop = disabler() + browser = disabler() + custom_man_xsl = disabler() + docbook_rng = disabler() + html_stylesheet = disabler() + man_stylesheet = disabler() + txt_stylesheet = disabler() +endif + + +prog_flex = find_program( + 'flex', + required: false, + disabler: true, +) + +yacc_cmd = [] +prog_yacc = find_program( + 'bison', + required: false, +) +if prog_yacc.found() + yacc_cmd = [prog_yacc, '-o', '@OUTPUT0@', '--defines=@OUTPUT1@', '@INPUT@'] +else + prog_yacc = find_program( + 'byacc', + required: false, + disabler: true, + ) + yacc_cmd = [prog_yacc, '-o', '@OUTPUT0@', '-H', '@OUTPUT1@', '@INPUT@'] +endif + + +redir_exe = files('aux' / 'redir_exe.sh') +chdir_meson_build_subdir = files('aux' / 'chdir_meson_build_subdir.sh') + + +libpam_private_inc = include_directories('libpam') +libpam_inc = include_directories('libpam' / 'include') + + +subdir('po') +subdir('libpam_internal') +subdir('libpam') +subdir('libpamc') +subdir('libpam_misc') +if enable_docs + subdir('doc') +endif +subdir('tests') +subdir('modules') +subdir('conf' / 'pam_conv1') +if get_option('examples') + subdir('examples') +endif +if get_option('xtests') + subdir('xtests') +endif diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..914ab46 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,99 @@ +# -*- mode: meson -*- + +option('i18n', type: 'feature', value: 'auto', + description: 'i18n support') +option('docs', type: 'feature', value: 'auto', + description: 'Generate and install documentation') +option('audit', type: 'feature', value: 'auto', + description: 'audit support') +option('econf', type: 'feature', value: 'auto', + description: 'libeconf support') +option('logind', type: 'feature', value: 'auto', + description: 'logind support in pam_issue and pam_timestamp') +option('openssl', type: 'feature', value: 'disabled', + description: 'Use OpenSSL crypto libraries in pam_timestamp') +option('selinux', type: 'feature', value: 'auto', + description: 'SELinux support') +option('nis', type: 'feature', value: 'auto', + description: 'NIS/YP support in pam_unix') + +option('examples', type: 'boolean', value: true, + description: 'Build examples') +option('lckpwdf', type: 'boolean', value: true, + description: 'Use lckpwdf function in pam_unix') +option('pam-debug', type: 'boolean', value: false, + description: 'Save debugging information into pam-debug.log') +option('pamlocking', type: 'boolean', value: false, + description: 'Observe a global authentication lock') +option('read-both-confs', type: 'boolean', value: false, + description: 'Read both /etc/pam.d and /etc/pam.conf files') +option('usergroups', type: 'boolean', value: false, + description: 'usergroups option default in pam_umask') +option('xtests', type: 'boolean', value: false, + description: 'Build xtests') + +option('misc-conv-bufsize', type: 'integer', value: 4096, + description: 'Size of input buffer for misc_conv() conversation function') +option('kernel-overflow-uid', type: 'integer', value: 65534, + description: 'Linux kernel overflow uid') +option('uidmin', type: 'integer', value: 1000, + description: 'Default value for regular user min uid') + +option('docbook-rng', + type: 'string', + value: 'http://docbook.org/xml/5.0/rng/docbookxi.rng', + description: 'RNG file for checking XML files') +option('html-stylesheet', + type: 'string', + value: 'http://docbook.sourceforge.net/release/xsl-ns/current/html/chunk.xsl', + description: 'html stylesheet path') +option('man-stylesheet', + type: 'string', + value: 'http://docbook.sourceforge.net/release/xsl-ns/current/manpages/profile-docbook.xsl', + description: 'man stylesheet path') +option('pdf-stylesheet', + type: 'string', + value: 'http://docbook.sourceforge.net/release/xsl-ns/current/fo/docbook.xsl', + description: 'pdf stylesheet path') +option('txt-stylesheet', + type: 'string', + value: 'http://docbook.sourceforge.net/release/xsl-ns/current/html/docbook.xsl', + description: 'txt stylesheet path') +option('xml-catalog', type: 'string', + description: 'xml catalog path') + +option('docdir', type: 'string', + description: 'documentation directory') +option('htmldir', type: 'string', + description: 'HTML documentation directory') +option('pdfdir', type: 'string', + description: 'PDF documentation directory') + +option('isadir', type: 'string', + description: 'Arch-specific PAM modules directory, relative to SECUREDIR') +option('securedir', type: 'string', + description: 'PAM modules directory') +option('sconfigdir', type: 'string', + description: 'PAM module configuration files directory') +option('systemdunitdir', type: 'string', + description: 'systemd service directory') +option('mailspool', type: 'string', + description: 'Mail spool directory') +option('xauth', type: 'string', + description: 'Additional path to check for xauth when it is called from pam_xauth') +option('randomdev', type: 'string', + description: 'Random device to use instead of /dev/urandom') +option('vendordir', type: 'string', + description: 'Distribution provided configuration files directory') + +option('pam_userdb', type: 'feature', value: 'auto', + description: 'pam_userdb module') +option('db', type: 'combo', choices: ['db', 'gdbm', 'ndbm', 'auto'], + value: 'auto') +option('db-uniquename', type: 'string', + description: 'Unique name for db libraries and functions') + +option('pam_lastlog', type: 'feature', value: 'disabled', + description: 'pam_lastlog module') +option('pam_unix', type: 'feature', value: 'auto', + description: 'pam_unix module') diff --git a/modules/meson.build b/modules/meson.build new file mode 100644 index 0000000..20cebdb --- /dev/null +++ b/modules/meson.build @@ -0,0 +1,51 @@ +pam_module_map = 'modules.map' +pam_module_map_path = meson.current_source_dir() / pam_module_map + +pam_module_link_deps = ['..' / pam_module_map] +pam_module_link_args = ['-Wl,--version-script=' + pam_module_map_path] + +subdir('pam_access') +subdir('pam_canonicalize_user') +subdir('pam_debug') +subdir('pam_deny') +subdir('pam_echo') +subdir('pam_env') +subdir('pam_exec') +subdir('pam_faildelay') +subdir('pam_faillock') +subdir('pam_filter') +subdir('pam_ftp') +subdir('pam_group') +subdir('pam_issue') +subdir('pam_keyinit') +subdir('pam_lastlog') +subdir('pam_limits') +subdir('pam_listfile') +subdir('pam_localuser') +subdir('pam_loginuid') +subdir('pam_mail') +subdir('pam_mkhomedir') +subdir('pam_motd') +subdir('pam_namespace') +subdir('pam_nologin') +subdir('pam_permit') +subdir('pam_pwhistory') +subdir('pam_rhosts') +subdir('pam_rootok') +subdir('pam_securetty') +subdir('pam_selinux') +subdir('pam_sepermit') +subdir('pam_setquota') +subdir('pam_shells') +subdir('pam_stress') +subdir('pam_succeed_if') +subdir('pam_time') +subdir('pam_timestamp') +subdir('pam_tty_audit') +subdir('pam_umask') +subdir('pam_unix') +subdir('pam_userdb') +subdir('pam_usertype') +subdir('pam_warn') +subdir('pam_wheel') +subdir('pam_xauth') diff --git a/modules/module-meson.build b/modules/module-meson.build new file mode 100644 index 0000000..acb7087 --- /dev/null +++ b/modules/module-meson.build @@ -0,0 +1,510 @@ +# -*- mode: meson -*- + +module = fs.name(meson.current_source_dir()) + +# pam_module.so + +pam_module_src = [module + '.c'] +pam_module_c_args = [] +pam_module_deps = [libpam_internal_dep, libpam_dep] + +if module == 'pam_env' + pam_module_deps += [libeconf] +endif +if module == 'pam_faillock' + pam_module_src += ['faillock.c', 'faillock_config.c'] + pam_module_deps += [libaudit] +endif +if module == 'pam_issue' + pam_module_deps += [libsystemd] +endif +if module == 'pam_keyinit' + if not enable_pam_keyinit + subdir_done() + endif +endif +if module == 'pam_lastlog' + if not enable_pam_lastlog + subdir_done() + endif + pam_module_deps += [libutil] +endif +if module == 'pam_limits' + limits_conf_dir = sconfigdir / 'limits.d' + pam_module_c_args += ['-DLIMITS_FILE_DIR="@0@"'.format(limits_conf_dir)] + pam_module_deps += [libsystemd] +endif +if module == 'pam_loginuid' + pam_module_deps += [libaudit] +endif +if module == 'pam_mkhomedir' + pam_module_c_args += ['-DMKHOMEDIR_HELPER="@0@"'.format(sbindir / 'mkhomedir_helper')] +endif +if module == 'pam_namespace' + if cdata.get('HAVE_UNSHARE', 0) == 0 + subdir_done() + endif + pam_module_src += ['md5.c', 'argv_parse.c'] + pam_module_deps += [libselinux] +endif +if module == 'pam_pwhistory' + pam_module_src += ['opasswd.c', 'pwhistory_config.c'] + if libselinux.found() + pam_module_c_args += ['-DPWHISTORY_HELPER="@0@"'.format(sbindir / 'pwhistory_helper')] + endif + pam_module_deps += [libcrypt, libselinux] +endif +if module == 'pam_rhosts' + if cdata.get('HAVE_RUSEROK', 0) == 0 or cdata.get('HAVE_RUSEROK_AF', 0) == 0 + subdir_done() + endif +endif +if module == 'pam_rootok' + pam_module_deps += [libselinux, libaudit] +endif +if module == 'pam_selinux' + if not libselinux.found() + subdir_done() + endif + pam_module_deps += [libselinux, libaudit] +endif +if module == 'pam_sepermit' + if not libselinux.found() + subdir_done() + endif + pam_module_deps += [libselinux] + sepermit_lockdir = localstatedir / 'run' / 'sepermit' + pam_module_c_args += ['-DSEPERMIT_LOCKDIR="@0@"'.format(sepermit_lockdir)] +endif +if module == 'pam_setquota' + if cdata.get('HAVE_QUOTACTL', 0) == 0 + subdir_done() + endif +endif +if module == 'pam_shells' + pam_module_deps += [libeconf] +endif +if module == 'pam_timestamp' + if cdata.get('WITH_OPENSSL', 0) == 1 + pam_module_src += ['hmac_openssl_wrapper.c'] + else + pam_module_src += ['hmacsha1.c', 'sha1.c'] + endif + pam_module_deps += [libcrypto, libsystemd] +endif +if module == 'pam_tty_audit' + if cdata.get('HAVE_STRUCT_AUDIT_TTY_STATUS', 0) == 0 + subdir_done() + endif +endif +if module == 'pam_unix' + if not enable_pam_unix + subdir_done() + endif + pam_module_src = ['bigcrypt.c', + 'pam_unix_acct.c', + 'pam_unix_auth.c', + 'pam_unix_passwd.c', + 'pam_unix_sess.c', + 'support.c', + 'passverify.c', + 'md5_good.c', + 'md5_broken.c', + ] + if enable_nis + pam_module_src += ['yppasswd_xdr.c'] + endif + pam_module_c_args += ['-DCHKPWD_HELPER="@0@"'.format(sbindir / 'unix_chkpwd'), + '-DUPDATE_HELPER="@0@"'.format(sbindir / 'unix_update')] + pam_module_deps += [libcrypt, libselinux, libtirpc, libnsl] +endif +if module == 'pam_userdb' + if not enable_pam_userdb + subdir_done() + endif + pam_module_deps += [libdb, libcrypt] +endif +if module == 'pam_xauth' + pam_module_deps += [libselinux] +endif + +pam_module = shared_module( + module, + name_prefix: '', + sources: pam_module_src, + dependencies: pam_module_deps, + c_args: pam_module_c_args, + link_depends: pam_module_link_deps, + link_args: pam_module_link_args, + install: true, + install_dir: securedir, +) + + +# pam_module.8 + +pam_module_mans = [[module + '.8', []]] +if module == 'pam_access' + pam_module_mans += [['access.conf.5', []]] +endif +if module == 'pam_env' + pam_module_mans += [['pam_env.conf.5', ['environment.5']]] +endif +if module == 'pam_group' + pam_module_mans += [['group.conf.5', []]] +endif +if module == 'pam_faillock' + pam_module_mans += [['faillock.8', []], + ['faillock.conf.5', []]] +endif +if module == 'pam_limits' + pam_module_mans += [['limits.conf.5', []]] +endif +if module == 'pam_mkhomedir' + pam_module_mans += [['mkhomedir_helper.8', []]] +endif +if module == 'pam_namespace' + pam_module_mans += [['namespace.conf.5', []], + [module + '_helper.8', []]] +endif +if module == 'pam_pwhistory' + pam_module_mans += [['pwhistory.conf.5', []]] + if libselinux.found() + pam_module_mans += [['pwhistory_helper.8', []]] + endif +endif +if module == 'pam_sepermit' + pam_module_mans += [['sepermit.conf.5', []]] +endif +if module == 'pam_time' + pam_module_mans += [['time.conf.5', []]] +endif +if module == 'pam_timestamp' + pam_module_mans += [['pam_timestamp_check.8', []]] +endif +if module == 'pam_unix' + pam_module_mans += [['unix_chkpwd.8', []]] + if libselinux.found() + pam_module_mans += [['unix_update.8', []]] + endif +endif + +foreach man: pam_module_mans + xml = man[0] + '.xml' + + run_command([prog_xmllint, + '--nonet', + '--noout', + '--xinclude', + '--relaxng', docbook_rng, + xml], + check: true) + + custom_target(man[0], + input: xml, + output: man, + depends: [custom_man_xsl, pam_module], + command: [prog_xsltproc, + '-o', '@OUTPUT0@', + '--nonet', + '--xinclude', + '--path', meson.current_source_dir(), + stringparam_vendordir, + stringparam_profileconditions, + custom_man_xsl, + '@INPUT@'], + install: true, + install_dir: mandir / 'man' + man[0].substring(-1), + install_tag: 'man', + ) +endforeach + + +# README + +readme_html = custom_target( + input: 'README.xml', + output: 'README.html', + depends: pam_module, + command: [prog_xsltproc, + '-o', '@OUTPUT@', + '--nonet', + '--xinclude', + '--path', meson.current_source_dir(), + '--stringparam', 'generate.toc', 'none', + stringparam_vendordir, + stringparam_profileconditions, + txt_stylesheet, + '@INPUT@'], +) + +custom_target( + input: readme_html, + output: module + '.txt', + command: [redir_exe, + '@INPUT@', + '@OUTPUT@', + browser], + install: true, + install_dir: docdir / 'modules', + install_tag: 'doc', +) + + +# module-specific configuration files and helpers + +if module == 'pam_access' + install_data( + 'access.conf', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) +endif +if module == 'pam_env' + install_data( + 'pam_env.conf', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) + install_data( + 'environment', + install_dir: vendor_sysconfdir, + install_tag: 'config', + ) +endif +if module == 'pam_group' + install_data( + 'group.conf', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) +endif +if module == 'pam_faillock' + executable( + 'faillock', + sources: ['main.c', 'faillock.c', 'faillock_config.c'], + link_args: exe_link_args, + dependencies: [libpam_internal_dep, libpam_dep, libaudit], + install: true, + install_dir: sbindir, + ) + install_data( + 'faillock.conf', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) +endif +if module == 'pam_filter' + install_data( + 'pam_filter.h', + install_dir: includedir, + install_tag: 'devel', + ) +endif +if module == 'pam_limits' + install_data( + 'limits.conf', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) + install_emptydir( + limits_conf_dir, + install_tag: 'config', + ) +endif +if module == 'pam_mkhomedir' + executable( + 'mkhomedir_helper', + sources: ['mkhomedir_helper.c'], + link_args: exe_link_args, + dependencies: [libpam_internal_dep, libpam_dep], + install: true, + install_dir: sbindir, + ) +endif +if module == 'pam_namespace' + pam_namespace_helper = configure_file( + input: 'pam_namespace_helper.in', + output: 'pam_namespace_helper', + configuration: cdata + ) + install_data( + pam_namespace_helper, + install_mode: 'rwxr-xr-x', + install_dir: sbindir, + install_tag: 'bin', + ) + pam_namespace_service = configure_file( + input: 'pam_namespace.service.in', + output: 'pam_namespace.service', + configuration: cdata + ) + install_data( + pam_namespace_service, + install_dir: systemdunitdir, + install_tag: 'config', + ) + install_data( + 'namespace.conf', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) + install_data( + 'namespace.init', + install_mode: 'rwxr-xr-x', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) + install_emptydir( + sconfigdir / 'namespace.d', + install_tag: 'config', + ) +endif +if module == 'pam_pwhistory' + executable( + 'pwhistory_helper', + sources: ['pwhistory_helper.c', 'opasswd.c'], + c_args: ['-DHELPER_COMPILE="pwhistory_helper"'], + link_args: exe_link_args, + dependencies: [libpam_internal_dep, libpam_dep, libcrypt], + install: true, + install_dir: sbindir, + ) + install_data( + 'pwhistory.conf', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) +endif +if module == 'pam_selinux' + executable( + 'pam_selinux_check', + sources: ['pam_selinux_check.c'], + include_directories: [libpamc_inc], + link_args: exe_link_args, + dependencies: [libpam_internal_dep, libpam_dep, libpam_misc_dep], + ) +endif +if module == 'pam_sepermit' + install_data( + 'sepermit.conf', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) + install_emptydir( + sepermit_lockdir, + install_tag: 'config', + ) +endif +if module == 'pam_time' + install_data( + 'time.conf', + install_dir: vendor_sconfigdir, + install_tag: 'config', + ) +endif +if module == 'pam_timestamp' + executable( + 'pam_timestamp_check', + sources: ['pam_timestamp_check.c'], + link_args: exe_link_args, + dependencies: [libpam_internal_dep, libpam_dep, libsystemd], + install: true, + install_dir: sbindir, + ) +endif +if module == 'pam_unix' + executable( + 'unix_chkpwd', + sources: ['unix_chkpwd.c', + 'audit.c', + 'bigcrypt.c', + 'md5_good.c', + 'md5_broken.c', + 'passverify.c', + ], + c_args: ['-DHELPER_COMPILE="unix_chkpwd"'], + link_args: exe_link_args, + dependencies: [libpam_internal_dep, libpam_dep, libcrypt, libselinux, libaudit], + install: true, + install_dir: sbindir, + ) + if libselinux.found() + executable( + 'unix_update', + sources: ['unix_update.c', + 'audit.c', + 'bigcrypt.c', + 'md5_good.c', + 'md5_broken.c', + 'passverify.c', + ], + c_args: ['-DHELPER_COMPILE="unix_update"'], + link_args: exe_link_args, + dependencies: [libpam_internal_dep, libpam_dep, libcrypt, libselinux, libaudit], + install: true, + install_dir: sbindir, + ) + endif + executable( + 'bigcrypt', + sources: ['bigcrypt.c', 'bigcrypt_main.c'], + dependencies: [libpam_internal_dep, libpam_dep, libcrypt], + ) +endif + + +# tests + +test( + 'dlopen ' + module, + tst_dlopen, + args: [pam_module], +) + +tst_module_retval_name = 'tst-' + module + '-retval' +tst_module_retval_src = tst_module_retval_name + '.c' + +if fs.exists(tst_module_retval_src) + tst_module_retval_link_args = '' + if module == 'pam_canonicalize_user' + tst_module_retval_link_args = '-Wl,--export-dynamic' + endif + + tst_module_retval_deps = [libpam_internal_dep, libpam_dep] + if module == 'pam_rootok' + tst_module_retval_deps += [libselinux, libaudit] + endif + + tst_module_retval = executable( + tst_module_retval_name, + sources: tst_module_retval_src, + dependencies: tst_module_retval_deps, + link_args: tst_module_retval_link_args, + ) + + test( + tst_module_retval_name, + chdir_meson_build_subdir, + args: [tst_module_retval], + env: ['MESON_BUILD_SUBDIR=' + meson.current_build_dir()], + ) +endif + + +if module == 'pam_timestamp' and cdata.get('WITH_OPENSSL', 0) != 1 + hmacfile_exe = executable( + 'hmacfile', + sources: ['hmacfile.c', 'hmacsha1.c', 'sha1.c'], + dependencies: [libpam_internal_dep, libpam_dep], + ) + test( + 'pam_timestamp hmacfile', + hmacfile_exe, + ) +endif + + +if module == 'pam_filter' + subdir('upperLOWER') +endif diff --git a/modules/modules.map b/modules/modules.map new file mode 100644 index 0000000..369b047 --- /dev/null +++ b/modules/modules.map @@ -0,0 +1,10 @@ +{ + global: + pam_sm_acct_mgmt; + pam_sm_authenticate; + pam_sm_chauthtok; + pam_sm_close_session; + pam_sm_open_session; + pam_sm_setcred; + local: *; +}; diff --git a/modules/pam_access/README.xml b/modules/pam_access/README.xml new file mode 100644 index 0000000..408aed0 --- /dev/null +++ b/modules/pam_access/README.xml @@ -0,0 +1,23 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_access.8.xml" xpointer='xpointer(id("pam_access-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_access/access.conf b/modules/pam_access/access.conf new file mode 100644 index 0000000..9c8e217 --- /dev/null +++ b/modules/pam_access/access.conf @@ -0,0 +1,125 @@ +# Login access control table. +# +# Comment line must start with "#", no space at front. +# Order of lines is important. +# +# When someone logs in, the table is scanned for the first entry that +# matches the (user, host) combination, or, in case of non-networked +# logins, the first entry that matches the (user, tty) combination. The +# permissions field of that table entry determines whether the login will +# be accepted or refused. +# +# Format of the login access control table is three fields separated by a +# ":" character: +# +# [Note, if you supply a 'fieldsep=|' argument to the pam_access.so +# module, you can change the field separation character to be +# '|'. This is useful for configurations where you are trying to use +# pam_access with X applications that provide PAM_TTY values that are +# the display variable like "host:0".] +# +# permission:users:origins +# +# The first field should be a "+" (access granted) or "-" (access denied) +# character. +# +# The second field should be a list of one or more login names, group +# names, or ALL (always matches). A pattern of the form user@host is +# matched when the login name matches the "user" part, and when the +# "host" part matches the local machine name. +# +# The third field should be a list of one or more tty names (for +# non-networked logins), host names, domain names (begin with "."), host +# addresses, internet network numbers (end with "."), ALL (always +# matches), NONE (matches no tty on non-networked logins) or +# LOCAL (matches any string that does not contain a "." character). +# +# You can use @netgroupname in host or user patterns; this even works +# for @usergroup@@hostgroup patterns. +# +# The EXCEPT operator makes it possible to write very compact rules. +# +# The group file is searched only when a name does not match that of the +# logged-in user. Both the user's primary group is matched, as well as +# groups in which users are explicitly listed. +# To avoid problems with accounts, which have the same name as a group, +# you can use brackets around group names '(group)' to differentiate. +# In this case, you should also set the "nodefgroup" option. +# +# TTY NAMES: Must be in the form returned by ttyname(3) less the initial +# "/dev" (e.g. tty1 or vc/1) +# +############################################################################## +# +# Disallow non-root logins on tty1 +# +#-:ALL EXCEPT root:tty1 +# +# Disallow console logins to all but a few accounts. +# +#-:ALL EXCEPT wheel shutdown sync:LOCAL +# +# Same, but make sure that really the group wheel and not the user +# wheel is used (use nodefgroup argument, too): +# +#-:ALL EXCEPT (wheel) shutdown sync:LOCAL +# +# Disallow non-local logins to privileged accounts (group wheel). +# +#-:wheel:ALL EXCEPT LOCAL .win.tue.nl +# +# Some accounts are not allowed to login from anywhere: +# +#-:wsbscaro wsbsecr wsbspac wsbsym wscosor wstaiwde:ALL +# +# All other accounts are allowed to login from anywhere. +# +############################################################################## +# All lines from here up to the end are building a more complex example. +############################################################################## +# +# User "root" should be allowed to get access via cron .. tty5 tty6. +#+:root:cron crond :0 tty1 tty2 tty3 tty4 tty5 tty6 +# +# User "root" should be allowed to get access from hosts with ip addresses. +#+:root:192.168.200.1 192.168.200.4 192.168.200.9 +#+:root:127.0.0.1 +# +# User "root" should get access from network 192.168.201. +# This term will be evaluated by string matching. +# comment: It might be better to use network/netmask instead. +# The same is 192.168.201.0/24 or 192.168.201.0/255.255.255.0 +#+:root:192.168.201. +# +# User "root" should be able to have access from domain. +# Uses string matching also. +#+:root:.foo.bar.org +# +# User "root" should be denied to get access from all other sources. +#-:root:ALL +# +# User "foo" and members of netgroup "nis_group" should be +# allowed to get access from all sources. +# This will only work if netgroup service is available. +#+:@nis_group foo:ALL +# +# User "john" should get access from ipv4 net/mask +#+:john:127.0.0.0/24 +# +# User "john" should get access from ipv4 as ipv6 net/mask +#+:john:::ffff:127.0.0.0/127 +# +# User "john" should get access from ipv6 host address +#+:john:2001:4ca0:0:101::1 +# +# User "john" should get access from ipv6 host address (same as above) +#+:john:2001:4ca0:0:101:0:0:0:1 +# +# User "john" should get access from ipv6 local link host address +#+:john:fe80::de95:818c:1b55:7e42%eth0 +# +# User "john" should get access from ipv6 net/mask +#+:john:2001:4ca0:0:101::/64 +# +# All other users should be denied to get access from all sources. +#-:ALL:ALL diff --git a/modules/pam_access/access.conf.5.xml b/modules/pam_access/access.conf.5.xml new file mode 100644 index 0000000..0b93db0 --- /dev/null +++ b/modules/pam_access/access.conf.5.xml @@ -0,0 +1,260 @@ + + + + access.conf + 5 + Linux-PAM Manual + + + + access.conf + the login access control table file + + + + + DESCRIPTION + + The /etc/security/access.conf file specifies + (user/group, host), + (user/group, network/netmask), + (user/group, tty), + (user/group, + X-$DISPLAY-value), or + (user/group, + pam-service-name) + combinations for which a login will be either accepted or refused. + + + When someone logs in, the file access.conf is + scanned for the first entry that matches the + (user/group, host) or + (user/group, network/netmask) + combination, or, in case of non-networked logins, the first entry + that matches the + (user/group, tty) + combination, or in the case of non-networked logins without a + tty, the first entry that matches the + (user/group, + X-$DISPLAY-value) or + (user/group, + pam-service-name/) + combination. The permissions field of that table entry + determines + whether the login will be accepted or refused. + + + + Each line of the login access control table has three fields separated + by a ":" character (colon): + + + + permission:users/groups:origins + + + + + The first field, the permission field, can be either a + "+" character (plus) for access granted or a + "-" character (minus) for access denied. + + + + The second field, the + users/group + field, should be a list of one or more login names, group names, uid, gid, or + ALL (which always matches). To differentiate + user entries from group entries, group entries should be written + with brackets, e.g. (group) or (gid). + + + + The third field, the origins + field, should be a list of one or more tty names (for non-networked + logins), X $DISPLAY values or PAM service + names (for non-networked logins without a tty), host names, + domain names (begin with "."), host addresses, + internet network numbers (end with "."), internet network addresses + with network mask (where network mask can be a decimal number or an + internet address also), ALL (which always matches) + or LOCAL. The LOCAL + keyword matches when the user connects without a network + connection (e.g., su, + login). A connection through the loopback + device (e.g., ssh user@localhost) is + considered a network connection, and thus, the + LOCAL keyword does not match. + + + + If supported by the system you can use + @netgroupname in host or user patterns. The + @@netgroupname syntax is supported in the user + pattern only and it makes the local system hostname to be passed + to the netgroup match call in addition to the user name. This might not + work correctly on some libc implementations causing the match to + always fail. + + + + The EXCEPT operator makes it possible to + write very compact rules. + + + + If the is not set, the group file + is searched when a name does not match that of the logged-in + user. Only groups are matched in which users are explicitly listed. + However the PAM module does not look at the primary group id of a user. + + + + + The "#" character at start of line (no space + at front) can be used to mark this line as a comment line. + + + + + + EXAMPLES + + These are some example lines which might be specified in + /etc/security/access.conf. + + + + User root should be allowed to get access via + cron, X11 terminal :0, + tty1, ..., tty5, + tty6. + + +:root:crond :0 tty1 tty2 tty3 tty4 tty5 tty6 + + + User root should be allowed to get access from + hosts which own the IPv4 addresses. This does not mean that the + connection have to be a IPv4 one, a IPv6 connection from a host with + one of this IPv4 addresses does work, too. + + +:root:192.168.200.1 192.168.200.4 192.168.200.9 + +:root:127.0.0.1 + + + User root should get access from network + 192.168.201. where the term will be evaluated by + string matching. But it might be better to use network/netmask instead. + The same meaning of 192.168.201. is + 192.168.201.0/24 or + 192.168.201.0/255.255.255.0. + + +:root:192.168.201. + + + User root should be able to have access from hosts + foo1.bar.org and foo2.bar.org + (uses string matching also). + + +:root:foo1.bar.org foo2.bar.org + + + User root should be able to have access from + domain foo.bar.org (uses string matching also). + + +:root:.foo.bar.org + + + User root should be denied to get access + from all other sources. + + -:root:ALL + + + A user with uid 1003 and a group with gid + 1000 should be allowed to get access + from all other sources. + + +:(1000) 1003:ALL + + User foo and members of netgroup + admins should be allowed to get access + from all sources. This will only work if netgroup service is available. + + +:@admins foo:ALL + + + User john and foo + should get access from IPv6 host address. + + +:john foo:2001:db8:0:101::1 + + + User john and foo + should get access from IPv6 link local host address. + + +:john foo:fe80::de95:818c:1b55:7e42%eth1 + + + User john should get access from IPv6 net/mask. + + +:john:2001:db8:0:101::/64 + + + Members of group wheel should be allowed to get access + from all sources. + + +:(wheel):ALL + + + Disallow console logins to all but the shutdown, sync and all + other accounts, which are a member of the wheel group. + + -:ALL EXCEPT (wheel) shutdown sync:LOCAL + + + All other users should be denied to get access from all sources. + + -:ALL:ALL + + + + + NOTES + + The default separators of list items in a field are space, ',', and tabulator + characters. Thus conveniently if spaces are put at the beginning and the end of + the fields they are ignored. However if the list separator is changed with the + listsep option, the spaces will become part of the actual + item and the line will be most probably ignored. For this reason, it is not + recommended to put spaces around the ':' characters. + + + An IPv6 link local host address must contain the interface + identifier. IPv6 link local network/netmask is not supported. + + + + + SEE ALSO + + pam_access8, + pam.d5, + pam8 + + + + + AUTHORS + + Original login.access5 + manual was provided by Guido van Rooij which was renamed to + access.conf5 + to reflect relation to default config file. + + + Network address / netmask description and example text was + introduced by Mike Becher <mike.becher@lrz-muenchen.de>. + + + diff --git a/modules/pam_access/meson.build b/modules/pam_access/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_access/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_access/pam_access.8.xml b/modules/pam_access/pam_access.8.xml new file mode 100644 index 0000000..c991d7a --- /dev/null +++ b/modules/pam_access/pam_access.8.xml @@ -0,0 +1,304 @@ + + + + pam_access + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_access + + PAM module for logdaemon style login access control + + + + + + + + pam_access.so + + debug + + + nodefgroup + + + noaudit + + + quiet_log + + + accessfile=file + + + fieldsep=sep + + + listsep=sep + + + + + + + DESCRIPTION + + The pam_access PAM module is mainly for access management. + It provides logdaemon style login access control based on login + names, host or domain names, internet addresses or network numbers, + or on terminal line names, X $DISPLAY values, + or PAM service names in case of non-networked logins. + + + By default rules for access management are taken from config file + /etc/security/access.conf if you don't specify + another file. + Then individual *.conf files from the + /etc/security/access.d/ directory are read. + The files are parsed one after another in the order of the system locale. + The effect of the individual files is the same as if all the files were + concatenated together in the order of parsing. This means that once + a pattern is matched in some file no further files are parsed. + If a config file is explicitly specified with the + option the files in the above directory are not parsed. + + + By default rules for access management are taken from config file + /etc/security/access.conf or, if that one is not + present, the file %vendordir%/security/access.conf. + These settings can be overruled by setting in a config file explicitly + specified with the option. + Then individual *.conf files from the + /etc/security/access.d/ and + %vendordir%/security/access.d directories are read. + If /etc/security/access.d/@filename@.conf exists, then + %vendordir%/security/access.d/@filename@.conf will not be used. + All access.d/*.conf files are sorted by their + @filename@.conf in lexicographic order regardless of which + of the directories they reside in. + The effect of the individual files is the same as if all the files were + concatenated together in the order of parsing. This means that once + a pattern is matched in some file no further files are parsed. + If a config file is explicitly specified with the + option the files in the above directories are not parsed. + + + If Linux PAM is compiled with audit support the module will report + when it denies access based on origin (host, tty, etc.). + + + + + OPTIONS + + + + + accessfile=/path/to/access.conf + + + + Indicate an alternative access.conf + style configuration file to override the default. This can + be useful when different services need different access lists. + + + + + + + debug + + + + A lot of debug information is printed with + syslog3. + + + + + + + noaudit + + + + Do not report logins from disallowed hosts and ttys to the audit subsystem. + + + + + + + quiet_log + + + + Do not log denials with + syslog3. + + + + + + + fieldsep=separators + + + + This option modifies the field separator character that + pam_access will recognize when parsing the access + configuration file. For example: + fieldsep=| will cause the + default `:' character to be treated as part of a field value + and `|' becomes the field separator. Doing this may be + useful in conjunction with a system that wants to use + pam_access with X based applications, since the + PAM_TTY item is likely to be + of the form "hostname:0" which includes a `:' character in + its value. But you should not need this. + + + + + + + listsep=separators + + + + This option modifies the list separator character that + pam_access will recognize when parsing the access + configuration file. For example: + listsep=, will cause the + default ` ' (space) and `\t' (tab) characters to be treated + as part of a list element value and `,' becomes the only + list element separator. Doing this may be useful on a system + with group information obtained from a Windows domain, + where the default built-in groups "Domain Users", + "Domain Admins" contain a space. + + + + + + + nodefgroup + + + + User tokens which are not enclosed in parentheses will not be + matched against the group database. The backwards compatible default is + to try the group database match even for tokens not enclosed + in parentheses. + + + + + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + Access was granted. + + + + + PAM_PERM_DENIED + + + Access was not granted. + + + + + PAM_IGNORE + + + pam_setcred was called which does nothing. + + + + + PAM_ABORT + + + Not all relevant data or options could be gotten. + + + + + PAM_USER_UNKNOWN + + + The user is not known to the system. + + + + + + + + FILES + + + /etc/security/access.conf + + Default configuration file + + + + %vendordir%/security/access.conf + + Default configuration file if + /etc/security/access.conf does not exist. + + + + + + + SEE ALSO + + + access.conf5 + , + + pam.d5 + , + + pam8 + . + + + + + AUTHORS + + The logdaemon style login access control scheme was designed and implemented by + Wietse Venema. + The pam_access PAM module was developed by + Alexei Nogin <alexei@nogin.dnttm.ru>. + The IPv6 support and the network(address) / netmask feature + was developed and provided by Mike Becher <mike.becher@lrz-muenchen.de>. + + + diff --git a/modules/pam_access/pam_access.c b/modules/pam_access/pam_access.c new file mode 100644 index 0000000..48e7c7e --- /dev/null +++ b/modules/pam_access/pam_access.c @@ -0,0 +1,1240 @@ +/* + * pam_access module + * + * Written by Alexei Nogin 1997/06/15 + * (I took login_access from logdaemon-5.6 and converted it to PAM + * using parts of pam_time code.) + * + ************************************************************************ + * Copyright message from logdaemon-5.6 (original file name DISCLAIMER) + ************************************************************************ + * Copyright 1995 by Wietse Venema. All rights reserved. Individual files + * may be covered by other copyrights (as noted in the file itself.) + * + * This material was originally written and compiled by Wietse Venema at + * Eindhoven University of Technology, The Netherlands, in 1990, 1991, + * 1992, 1993, 1994 and 1995. + * + * Redistribution and use in source and binary forms are permitted + * provided that this entire copyright notice is duplicated in all such + * copies. + * + * This software is provided "as is" and without any expressed or implied + * warranties, including, without limitation, the implied warranties of + * merchantability and fitness for any particular purpose. + ************************************************************************* + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBAUDIT +#include +#endif + +#include +#include +#include +#include +#include "pam_cc_compat.h" +#include "pam_inline.h" + +#define PAM_ACCESS_CONFIG (SCONFIG_DIR "/access.conf") +#define ACCESS_CONF_GLOB (SCONFIG_DIR "/access.d/*.conf") +#ifdef VENDOR_SCONFIG_DIR +#define VENDOR_PAM_ACCESS_CONFIG (VENDOR_SCONFIG_DIR "/access.conf") +#define VENDOR_ACCESS_CONF_GLOB (VENDOR_SCONFIG_DIR "/access.d/*.conf") +#endif + +/* login_access.c from logdaemon-5.6 with several changes by A.Nogin: */ + + /* + * This module implements a simple but effective form of login access + * control based on login names and on host (or domain) names, internet + * addresses (or network numbers), or on terminal line names in case of + * non-networked logins. Diagnostics are reported through syslog(3). + * + * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands. + */ + +#if !defined(MAXHOSTNAMELEN) || (MAXHOSTNAMELEN < 64) +#undef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif + + /* Delimiters for fields and for lists of users, ttys or hosts. */ + + +#define ALL 2 +#define YES 1 +#define NO 0 +#define NOMATCH (-1) + + /* + * A structure to bundle up all login-related information to keep the + * functional interfaces as generic as possible. + */ +struct login_info { + const struct passwd *user; + const char *from; + const char *config_file; + const char *hostname; + int debug; /* Print debugging messages. */ + int only_new_group_syntax; /* Only allow group entries of the form "(xyz)" */ + int noaudit; /* Do not audit denials */ + int quiet_log; /* Do not log denials */ + const char *fs; /* field separator */ + const char *sep; /* list-element separator */ + int from_remote_host; /* If PAM_RHOST was used for from */ + struct addrinfo *res; /* Cached DNS resolution of from */ + int gai_rv; /* Cached retval of getaddrinfo */ +}; + +/* Parse module config arguments */ + +static int +parse_args(pam_handle_t *pamh, struct login_info *loginfo, + int argc, const char **argv) +{ + int i; + + loginfo->noaudit = NO; + loginfo->quiet_log = NO; + loginfo->debug = NO; + loginfo->only_new_group_syntax = NO; + loginfo->fs = ":"; + loginfo->sep = ", \t"; + for (i=0; iconfig_file = str; + fclose(fp); + } else { + pam_syslog(pamh, LOG_ERR, + "failed to open accessfile=[%s]: %m", str); + return 0; + } + + } else if (strcmp (argv[i], "debug") == 0) { + loginfo->debug = YES; + } else if (strcmp (argv[i], "nodefgroup") == 0) { + loginfo->only_new_group_syntax = YES; + } else if (strcmp (argv[i], "noaudit") == 0) { + loginfo->noaudit = YES; + } else if (strcmp (argv[i], "quiet_log") == 0) { + loginfo->quiet_log = YES; + } else { + pam_syslog(pamh, LOG_ERR, "unrecognized option [%s]", argv[i]); + } + } + + return 1; /* OK */ +} + +/* --- evaluating all files in VENDORDIR/security/access.d and /etc/security/access.d --- */ +static const char *base_name(const char *path) +{ + const char *base = strrchr(path, '/'); + return base ? base+1 : path; +} + +static int +compare_filename(const void *a, const void *b) +{ + return strcmp(base_name(* (const char * const *) a), + base_name(* (const char * const *) b)); +} + +/* Evaluating a list of files which have to be parsed in the right order: + * + * - If etc/security/access.d/@filename@.conf exists, then + * %vendordir%/security/access.d/@filename@.conf should not be used. + * - All files in both access.d directories are sorted by their @filename@.conf in + * lexicographic order regardless of which of the directories they reside in. */ +static char **read_access_dir(pam_handle_t *pamh) +{ + glob_t globbuf; + size_t i=0; + int glob_rv = glob(ACCESS_CONF_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); + char **file_list; + size_t file_list_size = glob_rv == 0 ? globbuf.gl_pathc : 0; + +#ifdef VENDOR_ACCESS_CONF_GLOB + glob_t globbuf_vendor; + int glob_rv_vendor = glob(VENDOR_ACCESS_CONF_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf_vendor); + if (glob_rv_vendor == 0) + file_list_size += globbuf_vendor.gl_pathc; +#endif + file_list = malloc((file_list_size + 1) * sizeof(char*)); + if (file_list == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory for file list: %m"); +#ifdef VENDOR_ACCESS_CONF_GLOB + if (glob_rv_vendor == 0) + globfree(&globbuf_vendor); +#endif + if (glob_rv == 0) + globfree(&globbuf); + return NULL; + } + + if (glob_rv == 0) { + for (i = 0; i < globbuf.gl_pathc; i++) { + file_list[i] = strdup(globbuf.gl_pathv[i]); + if (file_list[i] == NULL) { + pam_syslog(pamh, LOG_ERR, "strdup failed: %m"); + break; + } + } + } +#ifdef VENDOR_ACCESS_CONF_GLOB + if (glob_rv_vendor == 0) { + for (size_t j = 0; j < globbuf_vendor.gl_pathc; j++) { + if (glob_rv == 0 && globbuf.gl_pathc > 0) { + int double_found = 0; + for (size_t k = 0; k < globbuf.gl_pathc; k++) { + if (strcmp(base_name(globbuf.gl_pathv[k]), + base_name(globbuf_vendor.gl_pathv[j])) == 0) { + double_found = 1; + break; + } + } + if (double_found) + continue; + } + file_list[i] = strdup(globbuf_vendor.gl_pathv[j]); + if (file_list[i] == NULL) { + pam_syslog(pamh, LOG_ERR, "strdup failed: %m"); + break; + } + i++; + } + globfree(&globbuf_vendor); + } +#endif + file_list[i] = NULL; + qsort(file_list, i, sizeof(char *), compare_filename); + + if (glob_rv == 0) + globfree(&globbuf); + + return file_list; +} + +/* --- static functions for checking whether the user should be let in --- */ + +typedef int match_func (pam_handle_t *, char *, struct login_info *); + +static int list_match (pam_handle_t *, char *, char *, struct login_info *, + match_func *); +static int user_match (pam_handle_t *, char *, struct login_info *); +static int group_match (pam_handle_t *, char *, const char *, int); +static int from_match (pam_handle_t *, char *, struct login_info *); +static int remote_match (pam_handle_t *, char *, struct login_info *); +static int string_match (pam_handle_t *, const char *, const char *, int); +static int network_netmask_match (pam_handle_t *, const char *, const char *, struct login_info *); + + +/* isipaddr - find out if string provided is an IP address or not */ + +static int +isipaddr (const char *string, int *addr_type, + struct sockaddr_storage *addr) +{ + struct sockaddr_storage local_addr; + int is_ip; + + /* We use struct sockaddr_storage addr because + * struct in_addr/in6_addr is an integral part + * of struct sockaddr and we doesn't want to + * use its value. + */ + + if (addr == NULL) + addr = &local_addr; + + memset(addr, 0, sizeof(struct sockaddr_storage)); + + /* first ipv4 */ + if (inet_pton(AF_INET, string, addr) > 0) + { + if (addr_type != NULL) + *addr_type = AF_INET; + + is_ip = YES; + } + else if (inet_pton(AF_INET6, string, addr) > 0) + { /* then ipv6 */ + if (addr_type != NULL) { + *addr_type = AF_INET6; + } + is_ip = YES; + } + else + is_ip = NO; + + return is_ip; +} + +/* is_local_addr - checks if the IP address is local */ +static int +is_local_addr (const char *string, int addr_type) +{ + if (addr_type == AF_INET) { + if (strcmp(string, "127.0.0.1") == 0) { + return YES; + } + } else if (addr_type == AF_INET6) { + if (strcmp(string, "::1") == 0) { + return YES; + } + } + + return NO; +} + + +/* are_addresses_equal - translate IP address strings to real IP + * addresses and compare them to find out if they are equal. + * If netmask was provided it will be used to focus comparison to + * relevant bits. + */ +static int +are_addresses_equal (const char *ipaddr0, const char *ipaddr1, + const char *netmask) +{ + struct sockaddr_storage addr0; + struct sockaddr_storage addr1; + int addr_type0 = 0; + int addr_type1 = 0; + + if (isipaddr (ipaddr0, &addr_type0, &addr0) == NO) + return NO; + + if (isipaddr (ipaddr1, &addr_type1, &addr1) == NO) + return NO; + + if (addr_type0 != addr_type1) { + /* different address types, but there is still a possibility that they are + * both local addresses + */ + int local1 = is_local_addr(ipaddr0, addr_type0); + int local2 = is_local_addr(ipaddr1, addr_type1); + + if (local1 == YES && local2 == YES) + return YES; + + return NO; + } + + if (netmask != NULL) { + /* Got a netmask, so normalize addresses? */ + struct sockaddr_storage nmask; + unsigned char *byte_a, *byte_nm; + + memset(&nmask, 0, sizeof(struct sockaddr_storage)); + if (inet_pton(addr_type0, netmask, (void *)&nmask) > 0) { + unsigned int i; + byte_a = (unsigned char *)(&addr0); + byte_nm = (unsigned char *)(&nmask); + for (i=0; i= 8) { + byte_nm[i] = 0xff; + netmask -= 8; + } else + if (netmask > 0) { + byte_nm[i] = 0xff << (8 - netmask); + break; + } else + if (netmask <= 0) { + break; + } + } + + /* now generate netmask address string */ + ipaddr_dst = inet_ntop(addr_type, &nmask, ipaddr_buf, ipaddr_buf_len); + if (ipaddr_dst == ipaddr_buf) { + return (ipaddr_buf); + } + + return (NULL); +} + +/* login_access - match username/group and host/tty with access control file */ + +static int +login_access (pam_handle_t *pamh, struct login_info *item) +{ + FILE *fp; + char *line = NULL; + char *perm; /* becomes permission field */ + char *users; /* becomes list of login names */ + char *froms; /* becomes list of terminals or hosts */ + int match = NO; +#ifdef HAVE_LIBAUDIT + int nonall_match = NO; +#endif + int result; + size_t end; + size_t lineno = 0; /* for diagnostics */ + size_t n = 0; + char *sptr; + + if (item->debug) + pam_syslog (pamh, LOG_DEBUG, + "login_access: user=%s, from=%s, file=%s", + item->user->pw_name, + item->from, item->config_file); + + /* + * Process the table one line at a time and stop at the first match. + * Blank lines and lines that begin with a '#' character are ignored. + * Non-comment lines are broken at the ':' character. All fields are + * mandatory. The first field should be a "+" or "-" character. A + * non-existing table means no access control. + */ + + if ((fp = fopen(item->config_file, "r"))!=NULL) { + while (!match && getline(&line, &n, fp) != -1) { + lineno++; + if (line[0] == 0) + continue; + if (line[end = strlen(line) - 1] != '\n') { + pam_syslog(pamh, LOG_ERR, + "%s: line %zu: missing newline or line too long", + item->config_file, lineno); + continue; + } + if (line[0] == '#') + continue; /* comment line */ + while (end > 0 && isspace((unsigned char)line[end - 1])) + end--; + line[end] = 0; /* strip trailing whitespace */ + if (line[0] == 0) /* skip blank lines */ + continue; + + /* Allow field separator in last field of froms */ + if (!(perm = strtok_r(line, item->fs, &sptr)) + || !(users = strtok_r(NULL, item->fs, &sptr)) + || !(froms = strtok_r(NULL, "\n", &sptr))) { + pam_syslog(pamh, LOG_ERR, "%s: line %zu: bad field count", + item->config_file, lineno); + continue; + } + if (perm[0] != '+' && perm[0] != '-') { + pam_syslog(pamh, LOG_ERR, "%s: line %zu: bad first field", + item->config_file, lineno); + continue; + } + if (item->debug) + pam_syslog (pamh, LOG_DEBUG, + "line %zu: %s : %s : %s", lineno, perm, users, froms); + match = list_match(pamh, users, NULL, item, user_match); + if (item->debug) + pam_syslog (pamh, LOG_DEBUG, "user_match=%d, \"%s\"", + match, item->user->pw_name); + if (match) { + match = list_match(pamh, froms, NULL, item, from_match); +#ifdef HAVE_LIBAUDIT + if (!match && perm[0] == '+') { + nonall_match = YES; + } +#endif + if (item->debug) + pam_syslog (pamh, LOG_DEBUG, + "from_match=%d, \"%s\"", match, item->from); + } + } + (void) fclose(fp); + } else if (errno == ENOENT) { + /* This is no error. */ + pam_syslog(pamh, LOG_WARNING, "warning: cannot open %s: %m", + item->config_file); + } else { + pam_syslog(pamh, LOG_ERR, "cannot open %s: %m", item->config_file); + return NO; + } +#ifdef HAVE_LIBAUDIT + if (!item->noaudit && (match == YES || (match == ALL && + nonall_match == YES)) && line != NULL && line[0] == '-') { + pam_modutil_audit_write(pamh, AUDIT_ANOM_LOGIN_LOCATION, + "pam_access", 0); + } +#endif + if (match == NO) + result = NOMATCH; + else if (line != NULL && line[0] == '+') + result = YES; + else + result = NO; + free(line); + return result; +} + + +/* list_match - match an item against a list of tokens with exceptions */ + +static int +list_match(pam_handle_t *pamh, char *list, char *sptr, + struct login_info *item, match_func *match_fn) +{ + char *tok; + int match = NO; + + if (item->debug && list != NULL) + pam_syslog (pamh, LOG_DEBUG, + "list_match: list=%s, item=%s", list, item->user->pw_name); + + /* + * Process tokens one at a time. We have exhausted all possible matches + * when we reach an "EXCEPT" token or the end of the list. If we do find + * a match, look for an "EXCEPT" list and recurse to determine whether + * the match is affected by any exceptions. + */ + + for (tok = strtok_r(list, item->sep, &sptr); tok != 0; + tok = strtok_r(NULL, item->sep, &sptr)) { + if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */ + break; + if ((match = (*match_fn) (pamh, tok, item))) /* YES */ + break; + } + /* Process exceptions to matches. */ + + if (match != NO) { + while ((tok = strtok_r(NULL, item->sep, &sptr)) && strcasecmp(tok, "EXCEPT")) + /* VOID */ ; + if (tok == 0) + return match; + if (list_match(pamh, NULL, sptr, item, match_fn) == NO) + return YES; /* drop special meaning of ALL */ + } + return (NO); +} + +/* netgroup_match - match group against machine or user */ + +static int +netgroup_match (pam_handle_t *pamh, const char *netgroup, + const char *machine, const char *user, int debug) +{ + int retval; + char *mydomain = NULL; + +#ifdef HAVE_GETDOMAINNAME + char domainname_res[256]; + + if (getdomainname (domainname_res, sizeof (domainname_res)) == 0) + { + if (domainname_res[0] != '\0' && strcmp (domainname_res, "(none)") != 0) + { + mydomain = domainname_res; + } + } +#endif + +#ifdef HAVE_INNETGR + retval = innetgr (netgroup, machine, user, mydomain); +#else + retval = 0; + pam_syslog (pamh, LOG_ERR, "pam_access does not have netgroup support"); +#endif + if (debug == YES) + pam_syslog (pamh, LOG_DEBUG, + "netgroup_match: %d (netgroup=%s, machine=%s, user=%s, domain=%s)", + retval, netgroup ? netgroup : "NULL", + machine ? machine : "NULL", + user ? user : "NULL", mydomain ? mydomain : "NULL"); + return retval; +} + +/* user_name_or_uid_match - match a username or user uid against one token */ +static int +user_name_or_uid_match(pam_handle_t *pamh, const char *tok, + const struct login_info *item) +{ + /* ALL or exact match of username */ + int rv = string_match(pamh, tok, item->user->pw_name, item->debug); + if (rv != NO) + return rv; + + if (tok[strspn(tok, "0123456789")] != '\0') + return NO; + + char buf[sizeof(long long) * 3 + 1]; + snprintf(buf, sizeof(buf), "%llu", + zero_extend_signed_to_ull(item->user->pw_uid)); + if (item->debug) + pam_syslog(pamh, LOG_DEBUG, "user_match: tok=%s, uid=%s", tok, buf); + + /* check for exact match of uid */ + return string_match (pamh, tok, buf, item->debug); +} + +/* user_match - match a user against one token */ + +static int +user_match (pam_handle_t *pamh, char *tok, struct login_info *item) +{ + char *string = item->user->pw_name; + struct login_info fake_item; + char *at; + int rv; + + if (item->debug) + pam_syslog (pamh, LOG_DEBUG, + "user_match: tok=%s, item=%s", tok, string); + + /* + * If a token has the magic value "ALL" the match always succeeds. + * Otherwise, return YES if the token fully matches the username, if the + * token is a group that contains the username, or if the token is the + * name of the user's primary group. + */ + + /* Try to split on a pattern (@*[^@]+)(@+.*) */ + for (at = tok; *at == '@'; ++at); + + if (tok[0] == '(' && tok[strlen(tok) - 1] == ')') { + return (group_match (pamh, tok, string, item->debug)); + } else if ((at = strchr(at, '@')) != NULL) { + /* split user@host pattern */ + if (item->hostname == NULL) + return NO; + memcpy (&fake_item, item, sizeof(fake_item)); + fake_item.from = item->hostname; + fake_item.gai_rv = 0; + fake_item.res = NULL; + fake_item.from_remote_host = 1; /* hostname should be resolvable */ + *at = 0; + if (!user_match (pamh, tok, item)) + return NO; + rv = from_match (pamh, at + 1, &fake_item); + if (fake_item.gai_rv == 0 && fake_item.res) + freeaddrinfo(fake_item.res); + return rv; + } else if (tok[0] == '@') { /* netgroup */ + const char *hostname = NULL; + if (tok[1] == '@') { /* add hostname to netgroup match */ + if (item->hostname == NULL) + return NO; + ++tok; + hostname = item->hostname; + } + return (netgroup_match (pamh, tok + 1, hostname, string, item->debug)); + } else if ((rv=user_name_or_uid_match(pamh, tok, item)) != NO) /* ALL or exact match */ + return rv; + else if (item->only_new_group_syntax == NO && + pam_modutil_user_in_group_nam_nam (pamh, + item->user->pw_name, tok)) + /* try group membership */ + return YES; + + return NO; +} + + +/* group_name_or_gid_match - match a group name or group gid against one token */ +static int +group_name_or_gid_match(pam_handle_t *pamh, const char *tok, + const char *usr, int debug) +{ + /* check for exact match of group name */ + if (pam_modutil_user_in_group_nam_nam(pamh, usr, tok) != NO) + return YES; + + if (tok[strspn(tok, "0123456789")] != '\0') + return NO; + + char *endptr = NULL; + errno = 0; + unsigned long int ul = strtoul(tok, &endptr, 10); + gid_t gid = (gid_t) ul; + if (errno != 0 + || tok == endptr + || *endptr != '\0' + || (unsigned long) zero_extend_signed_to_ull(gid) != ul) { + return NO; + } + + if (debug) + pam_syslog(pamh, LOG_DEBUG, "group_match: user=%s, gid=%s", usr, tok); + + /* check for exact match of gid */ + return pam_modutil_user_in_group_nam_gid(pamh, usr, gid); +} + +/* group_match - match a username against token named group */ + +static int +group_match (pam_handle_t *pamh, char *tok, const char* usr, int debug) +{ + if (debug) + pam_syslog (pamh, LOG_DEBUG, + "group_match: grp=%s, user=%s", tok, usr); + + if (strlen(tok) < 3) + return NO; + + /* token is received under the format '(...)' */ + tok++; + tok[strlen(tok) - 1] = '\0'; + + if (group_name_or_gid_match (pamh, usr, tok, debug)) + return YES; + + return NO; +} + + +/* from_match - match a host or tty against a list of tokens */ + +static int +from_match (pam_handle_t *pamh, char *tok, struct login_info *item) +{ + const char *string = item->from; + int rv; + + if (item->debug) + pam_syslog (pamh, LOG_DEBUG, + "from_match: tok=%s, item=%s", tok, string); + + /* + * If a token has the magic value "ALL" the match always succeeds. Return + * YES if the token fully matches the string. If the token is a domain + * name, return YES if it matches the last fields of the string. If the + * token has the magic value "LOCAL", return YES if the from field was + * not taken by PAM_RHOST. If the token is a network number, return YES + * if it matches the head of the string. + */ + + if (string == NULL) { + return NO; + } else if (tok[0] == '@') { /* netgroup */ + return (netgroup_match (pamh, tok + 1, string, (char *) 0, item->debug)); + } else if ((rv = string_match(pamh, tok, string, item->debug)) != NO) { + /* ALL or exact match */ + return rv; + } else if (strcasecmp(tok, "LOCAL") == 0) { + /* LOCAL matches only local accesses */ + if (!item->from_remote_host) + return YES; + return NO; + } else if (item->from_remote_host) { + return remote_match(pamh, tok, item); + } + return NO; +} + +static int +remote_match (pam_handle_t *pamh, char *tok, struct login_info *item) +{ + const char *string = item->from; + size_t tok_len = strlen(tok); + size_t str_len; + + if (tok[0] == '.') { /* domain: match last fields */ + if ((str_len = strlen(string)) > tok_len + && strcasecmp(tok, string + str_len - tok_len) == 0) + return YES; + } else if (tok[tok_len - 1] == '.') { /* internet network numbers (end with ".") */ + struct addrinfo hint; + + memset (&hint, '\0', sizeof (hint)); + hint.ai_flags = AI_CANONNAME; + hint.ai_family = AF_INET; + + if (item->gai_rv != 0) + return NO; + else if (!item->res && + (item->gai_rv = getaddrinfo (string, NULL, &hint, &item->res)) != 0) + return NO; + else + { + struct addrinfo *runp = item->res; + + while (runp != NULL) + { + char buf[INET_ADDRSTRLEN+2]; + + if (runp->ai_family == AF_INET) + { + DIAG_PUSH_IGNORE_CAST_ALIGN; + inet_ntop (runp->ai_family, + &((struct sockaddr_in *) runp->ai_addr)->sin_addr, + buf, sizeof (buf) - 1); + DIAG_POP_IGNORE_CAST_ALIGN; + + strcat (buf, "."); + + if (strncmp(tok, buf, tok_len) == 0) + { + return YES; + } + } + runp = runp->ai_next; + } + } + return NO; + } + + /* Assume network/netmask, IP address or hostname. */ + return network_netmask_match(pamh, tok, string, item); +} + +/* string_match - match a string against one token */ + +static int +string_match (pam_handle_t *pamh, const char *tok, const char *string, + int debug) +{ + + if (debug) + pam_syslog (pamh, LOG_DEBUG, + "string_match: tok=%s, item=%s", tok, string); + + /* + * If the token has the magic value "ALL" the match always succeeds. + * Otherwise, return YES if the token fully matches the string. + * "NONE" token matches NULL string. + */ + + if (strcasecmp(tok, "ALL") == 0) { /* all: always matches */ + return (ALL); + } else if (string != NULL) { + if (strcasecmp(tok, string) == 0) { /* try exact match */ + return (YES); + } + } else if (strcasecmp(tok, "NONE") == 0) { + return (YES); + } + return (NO); +} + + +/* network_netmask_match - match a string against one token + * where string is a hostname or ip (v4,v6) address and tok + * represents either a hostname, a single ip (v4,v6) address + * or a network/netmask + */ +static int +network_netmask_match (pam_handle_t *pamh, + const char *tok, const char *string, struct login_info *item) +{ + char *netmask_ptr; + char netmask_string[MAXHOSTNAMELEN + 1]; + int addr_type; + struct addrinfo *ai = NULL; + + if (item->debug) + pam_syslog (pamh, LOG_DEBUG, + "network_netmask_match: tok=%s, item=%s", tok, string); + + /* OK, check if tok is of type addr/mask */ + if ((netmask_ptr = strchr(tok, '/')) != NULL) + { + long netmask = 0; + + /* YES */ + *netmask_ptr = 0; + netmask_ptr++; + + if (isipaddr(tok, &addr_type, NULL) == NO) + { /* no netaddr */ + return NO; + } + + /* check netmask */ + if (isipaddr(netmask_ptr, NULL, NULL) == NO) + { /* netmask as integre value */ + char *endptr = NULL; + netmask = strtol(netmask_ptr, &endptr, 0); + if ((endptr == netmask_ptr) || (*endptr != '\0')) + { /* invalid netmask value */ + return NO; + } + if ((netmask < 0) + || (addr_type == AF_INET && netmask > 32) + || (addr_type == AF_INET6 && netmask > 128)) + { /* netmask value out of range */ + return NO; + } + + netmask_ptr = number_to_netmask(netmask, addr_type, + netmask_string, MAXHOSTNAMELEN); + } + + /* + * Construct an addrinfo list from the IP address. + * This should not fail as the input is a correct IP address... + */ + if (getaddrinfo (tok, NULL, NULL, &ai) != 0) + { + return NO; + } + } + else + { + /* + * It is either an IP address or a hostname. + * Let getaddrinfo sort everything out + */ + if (getaddrinfo (tok, NULL, NULL, &ai) != 0) + { + if (item->debug) + pam_syslog(pamh, LOG_DEBUG, "cannot resolve hostname \"%s\"", tok); + + return NO; + } + netmask_ptr = NULL; + } + + if (isipaddr(string, NULL, NULL) != YES) + { + struct addrinfo hint; + + /* Assume network/netmask with a name of a host. */ + memset (&hint, '\0', sizeof (hint)); + hint.ai_flags = AI_CANONNAME; + hint.ai_family = AF_UNSPEC; + + if (item->gai_rv != 0) + { + freeaddrinfo(ai); + return NO; + } + else if (!item->res && + (item->gai_rv = getaddrinfo (string, NULL, &hint, &item->res)) != 0) + { + freeaddrinfo(ai); + return NO; + } + else + { + struct addrinfo *runp = item->res; + struct addrinfo *runp1; + + while (runp != NULL) + { + char buf[INET6_ADDRSTRLEN]; + + if (getnameinfo (runp->ai_addr, runp->ai_addrlen, buf, sizeof (buf), NULL, 0, NI_NUMERICHOST) != 0) + { + freeaddrinfo(ai); + return NO; + } + + for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next) + { + char buf1[INET6_ADDRSTRLEN]; + + if (runp->ai_family != runp1->ai_family) + continue; + + if (getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST) != 0) + { + freeaddrinfo(ai); + return NO; + } + + if (are_addresses_equal (buf, buf1, netmask_ptr)) + { + freeaddrinfo(ai); + return YES; + } + } + runp = runp->ai_next; + } + } + } + else + { + struct addrinfo *runp1; + + for (runp1 = ai; runp1 != NULL; runp1 = runp1->ai_next) + { + char buf1[INET6_ADDRSTRLEN]; + + (void) getnameinfo (runp1->ai_addr, runp1->ai_addrlen, buf1, sizeof (buf1), NULL, 0, NI_NUMERICHOST); + + if (are_addresses_equal(string, buf1, netmask_ptr)) + { + freeaddrinfo(ai); + return YES; + } + } + } + + freeaddrinfo(ai); + + return NO; +} + + +/* --- public PAM management functions --- */ + +static int +pam_access(pam_handle_t *pamh, int argc, const char **argv) +{ + struct login_info loginfo; + const char *user=NULL; + const void *void_from=NULL; + const char *from; + const char *default_config = PAM_ACCESS_CONFIG; + struct passwd *user_pw; + char hostname[MAXHOSTNAMELEN + 1]; + int rv; + + /* set username */ + + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name"); + return PAM_USER_UNKNOWN; + } + + if ((user_pw=pam_modutil_getpwnam(pamh, user))==NULL) + return (PAM_USER_UNKNOWN); + + /* + * Bundle up the arguments to avoid unnecessary clumsiness later on. + */ + memset(&loginfo, '\0', sizeof(loginfo)); + loginfo.user = user_pw; + loginfo.config_file = default_config; + + /* parse the argument list */ + + if (!parse_args(pamh, &loginfo, argc, argv)) { + pam_syslog(pamh, LOG_ERR, "failed to parse the module arguments"); + return PAM_ABORT; + } + +#ifdef VENDOR_PAM_ACCESS_CONFIG + if (loginfo.config_file == default_config) { + /* Check whether PAM_ACCESS_CONFIG file is available. + * If it does not exist, fall back to VENDOR_PAM_ACCESS_CONFIG file. */ + struct stat buffer; + if (stat(loginfo.config_file, &buffer) != 0 && errno == ENOENT) { + default_config = VENDOR_PAM_ACCESS_CONFIG; + loginfo.config_file = default_config; + } + } +#endif + + /* remote host name */ + + if (pam_get_item(pamh, PAM_RHOST, &void_from) + != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "cannot find the remote host name"); + return PAM_ABORT; + } + from = void_from; + + if ((from==NULL) || (*from=='\0')) { + + /* local login, set tty name */ + + loginfo.from_remote_host = 0; + + if (pam_get_item(pamh, PAM_TTY, &void_from) != PAM_SUCCESS + || void_from == NULL) { + D(("PAM_TTY not set, probing stdin")); + from = ttyname(STDIN_FILENO); + if (from != NULL) { + if (pam_set_item(pamh, PAM_TTY, from) != PAM_SUCCESS) + pam_syslog(pamh, LOG_WARNING, "couldn't set tty name"); + } else { + if (pam_get_item(pamh, PAM_SERVICE, &void_from) != PAM_SUCCESS + || void_from == NULL) { + pam_syslog (pamh, LOG_ERR, + "cannot determine remote host, tty or service name"); + return PAM_ABORT; + } + from = void_from; + if (loginfo.debug) + pam_syslog (pamh, LOG_DEBUG, + "cannot determine tty or remote hostname, using service %s", + from); + } + } + else + from = void_from; + + if (from[0] == '/') { /* full path, remove device path. */ + const char *f; + from++; + if ((f = strchr(from, '/')) != NULL) { + from = f + 1; + } + } + } + else + loginfo.from_remote_host = 1; + + loginfo.from = from; + + hostname[sizeof(hostname)-1] = '\0'; + if (gethostname(hostname, sizeof(hostname)-1) == 0) + loginfo.hostname = hostname; + else { + pam_syslog (pamh, LOG_ERR, "gethostname failed: %m"); + loginfo.hostname = NULL; + } + + rv = login_access(pamh, &loginfo); + + if (rv == NOMATCH && loginfo.config_file == default_config) { + char **filename_list = read_access_dir(pamh); + if (filename_list != NULL) { + for (int i = 0; filename_list[i] != NULL; i++) { + loginfo.config_file = filename_list[i]; + rv = login_access(pamh, &loginfo); + if (rv != NOMATCH) + break; + } + for (int i = 0; filename_list[i] != NULL; i++) + free(filename_list[i]); + free(filename_list); + } + } + + if (loginfo.gai_rv == 0 && loginfo.res) + freeaddrinfo(loginfo.res); + + if (rv) { + return (PAM_SUCCESS); + } else { + if (!loginfo.quiet_log) { + pam_syslog(pamh, LOG_ERR, + "access denied for user `%s' from `%s'",user,from); + } + return (PAM_PERM_DENIED); + } +} + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_access(pamh, argc, argv); +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_access(pamh, argc, argv); +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_access(pamh, argc, argv); +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_access(pamh, argc, argv); +} + +int +pam_sm_chauthtok(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_access(pamh, argc, argv); +} + +/* end of module definition */ diff --git a/modules/pam_access/tst-pam_access b/modules/pam_access/tst-pam_access new file mode 100755 index 0000000..271e69f --- /dev/null +++ b/modules/pam_access/tst-pam_access @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_access.so diff --git a/modules/pam_canonicalize_user/README.xml b/modules/pam_canonicalize_user/README.xml new file mode 100644 index 0000000..293b218 --- /dev/null +++ b/modules/pam_canonicalize_user/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_canonicalize_user.8.xml" xpointer='xpointer(id("pam_canonicalize_user-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
diff --git a/modules/pam_canonicalize_user/meson.build b/modules/pam_canonicalize_user/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_canonicalize_user/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_canonicalize_user/pam_canonicalize_user.8.xml b/modules/pam_canonicalize_user/pam_canonicalize_user.8.xml new file mode 100644 index 0000000..832f7ec --- /dev/null +++ b/modules/pam_canonicalize_user/pam_canonicalize_user.8.xml @@ -0,0 +1,137 @@ + + + + pam_canonicalize_user + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_canonicalize_user + Get user name and canonicalize it + + + + + pam_canonicalize_user.so + + + + + DESCRIPTION + + This PAM module uses the name of the user obtained via + + pam_get_user3 + + as a key to query the password database, and replaces + PAM_USER with the pw_name value + that has been returned. + + + + + OPTIONS + This module does not recognise any options. + + + + MODULE TYPES PROVIDED + Only the module type is provided. + + + + RETURN VALUES + + + PAM_IGNORE + + The user name was set successfully. + + + + PAM_USER_UNKNOWN + + The user was not found. + + + + PAM_SYSTEM_ERR + + The application did not supply neither a user name nor a conversation method. + + + + PAM_INCOMPLETE + + The conversation method supplied by the application is waiting for an event. + + + + PAM_CONV_ERR + + The conversation method supplied by the application failed to obtain the user name. + + + + PAM_ABORT + + Error resuming an old conversation. + + + + PAM_BUF_ERR + + Memory buffer error. + + + + + + + EXAMPLES + + Prepend the PAM auth stack with the following line to canonicalize + the user name before the authentication: + + auth required pam_canonicalize_user.so + + + + + + SEE ALSO + + + pam_get_user3 + , + + pam_get_item3 + , + + pam_set_item3 + , + + getpwnam3 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_canonicalize_user was written by Dmitry V. Levin <ldv@strace.io>. + + + + diff --git a/modules/pam_canonicalize_user/pam_canonicalize_user.c b/modules/pam_canonicalize_user/pam_canonicalize_user.c new file mode 100644 index 0000000..66f794c --- /dev/null +++ b/modules/pam_canonicalize_user/pam_canonicalize_user.c @@ -0,0 +1,76 @@ +/* + * pam_canonicalize_user - get user name and canonicalize it + * + * Copyright (c) 2023 Dmitry V. Levin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL + * are required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include +#include + +#include +#include +#include + +int +pam_sm_authenticate(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + const char *user; + int rc = pam_get_user(pamh, &user, 0); + if (rc != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", + pam_strerror(pamh, rc)); + return rc == PAM_CONV_AGAIN ? PAM_INCOMPLETE : rc; + } + + struct passwd *pw = pam_modutil_getpwnam(pamh, user); + if (!pw) { + pam_syslog(pamh, LOG_NOTICE, "user unknown"); + return PAM_USER_UNKNOWN; + } + + if (strcmp(user, pw->pw_name) == 0) + return PAM_IGNORE; + + rc = pam_set_item(pamh, PAM_USER, pw->pw_name); + return rc == PAM_SUCCESS ? PAM_IGNORE : rc; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} diff --git a/modules/pam_canonicalize_user/tst-pam_canonicalize_user b/modules/pam_canonicalize_user/tst-pam_canonicalize_user new file mode 100755 index 0000000..2d3cb6f --- /dev/null +++ b/modules/pam_canonicalize_user/tst-pam_canonicalize_user @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_canonicalize_user.so diff --git a/modules/pam_canonicalize_user/tst-pam_canonicalize_user-retval.c b/modules/pam_canonicalize_user/tst-pam_canonicalize_user-retval.c new file mode 100644 index 0000000..1c8af39 --- /dev/null +++ b/modules/pam_canonicalize_user/tst-pam_canonicalize_user-retval.c @@ -0,0 +1,197 @@ +/* + * Check pam_canonicalize_user return values. + * + * Copyright (c) 2023 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_canonicalize_user" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static struct pam_conv null_conv; + +static int +again_conv_func(int num_msg UNUSED, const struct pam_message **msg UNUSED, + struct pam_response **resp UNUSED, void *appdata_ptr UNUSED) +{ + return PAM_CONV_AGAIN; +} + +static struct pam_conv again_conv = { .conv = again_conv_func }; + +#ifdef HAVE_GETPWNAM_R + +int +getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) +{ + if (strcmp(name, "root") == 0 || + strcmp(name, "ROOT") == 0) + return getpwuid_r(0, pwd, buf, buflen, result); + + *result = NULL; + return 0; +} + +#else /* !HAVE_GETPWNAM_R */ + +struct passwd * +getpwnam(const char *name) +{ + if (strcmp(name, "root") == 0 || + strcmp(name, "ROOT") == 0) + return getpwuid(0); + + errno = 0; + return NULL; +} + +#endif /* !HAVE_GETPWNAM_R */ + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + + char cwd[PATH_MAX]; + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + struct passwd *pw; + ASSERT_NE(NULL, (pw = getpwuid(0))); + ASSERT_EQ(0, strcmp("root", pw->pw_name)); + + /* PAM_USER_UNKNOWN */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, ":", &null_conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_USER_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_IGNORE -> PAM_PERM_DENIED */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "root", &null_conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_IGNORE -> PAM_SUCCESS */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "auth required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "account required %s/" LTDIR "%s.so\n" + "account required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "password required %s/" LTDIR "%s.so\n" + "password required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "session required %s/" LTDIR "%s.so\n" + "session required %s/../pam_permit/" LTDIR "pam_permit.so\n", + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "root", &null_conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_INCOMPLETE */ + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, NULL, &again_conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_INCOMPLETE, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_ABORT, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_ABORT, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_ABORT, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_ABORT, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_ABORT, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_IGNORE -> PAM_SUCCESS, "ROOT" -> "root" */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "auth required %s/../pam_succeed_if/" LTDIR "pam_succeed_if.so user = root\n" + "account required %s/" LTDIR "%s.so\n" + "account required %s/../pam_succeed_if/" LTDIR "pam_succeed_if.so user = root\n" + "password required %s/" LTDIR "%s.so\n" + "password required %s/../pam_succeed_if/" LTDIR "pam_succeed_if.so user = root\n" + "session required %s/" LTDIR "%s.so\n" + "session required %s/../pam_succeed_if/" LTDIR "pam_succeed_if.so user = root\n", + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "ROOT", &null_conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_debug/README.xml b/modules/pam_debug/README.xml new file mode 100644 index 0000000..cdcec7f --- /dev/null +++ b/modules/pam_debug/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_debug.8.xml" xpointer='xpointer(id("pam_debug-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_debug/meson.build b/modules/pam_debug/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_debug/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_debug/pam_debug.8.xml b/modules/pam_debug/pam_debug.8.xml new file mode 100644 index 0000000..1c98f17 --- /dev/null +++ b/modules/pam_debug/pam_debug.8.xml @@ -0,0 +1,228 @@ + + + + pam_debug + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_debug + PAM module to debug the PAM stack + + + + + pam_debug.so + + auth=value + + + cred=value + + + acct=value + + + prechauthtok=value + + + chauthtok=value + + + auth=value + + + open_session=value + + + close_session=value + + + + + + DESCRIPTION + + The pam_debug PAM module is intended as a debugging aide for + determining how the PAM stack is operating. This module returns + what its module arguments tell it to return. + + + + + OPTIONS + + + + auth=value + + + + The + + pam_sm_authenticate3 + function will return + value. + + + + + + cred=value + + + + The + + pam_sm_setcred3 + function will return + value. + + + + + + acct=value + + + + The + + pam_sm_acct_mgmt3 + function will return + value. + + + + + + prechauthtok=value + + + + The + + pam_sm_chauthtok3 + function will return + value if the + PAM_PRELIM_CHECK flag is set. + + + + + + chauthtok=value + + + + The + + pam_sm_chauthtok3 + function will return + value if the + PAM_PRELIM_CHECK flag is + not set. + + + + + + open_session=value + + + + The + + pam_sm_open_session3 + function will return + value. + + + + + + close_session=value + + + + The + + pam_sm_close_session3 + function will return + value. + + + + + + Where value can be one of: success, + open_err, symbol_err, service_err, system_err, buf_err, perm_denied, + auth_err, cred_insufficient, authinfo_unavail, user_unknown, + maxtries, new_authtok_reqd, acct_expired, session_err, cred_unavail, + cred_expired, cred_err, no_module_data, conv_err, authtok_err, + authtok_recover_err, authtok_lock_busy, authtok_disable_aging, + try_again, ignore, abort, authtok_expired, module_unknown, + bad_item, conv_again, incomplete. + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + Default return code if no other value was specified, + else specified return value. + + + + + + + + EXAMPLES + +auth requisite pam_permit.so +auth [success=2 default=ok] pam_debug.so auth=perm_denied cred=success +auth [default=reset] pam_debug.so auth=success cred=perm_denied +auth [success=done default=die] pam_debug.so +auth optional pam_debug.so auth=perm_denied cred=perm_denied +auth sufficient pam_debug.so auth=success cred=success + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_debug was written by Andrew G. Morgan <morgan@kernel.org>. + + + + \ No newline at end of file diff --git a/modules/pam_debug/pam_debug.c b/modules/pam_debug/pam_debug.c new file mode 100644 index 0000000..0def516 --- /dev/null +++ b/modules/pam_debug/pam_debug.c @@ -0,0 +1,102 @@ +/* + * pam_debug module + * + * Written by Andrew Morgan 2001/02/04 + * + * This module is intended as a debugging aide for determining how + * the PAM stack is operating. + */ + +#include "config.h" +#include + +#include +#include +#include + +#define _PAM_ACTION_UNDEF (-10) +#include "../../libpam/pam_tokens.h" + +#define DEFAULT_USER "nobody" + +/* --- authentication management functions --- */ + +static void state(pam_handle_t *pamh, const char *text) +{ + if (pam_info(pamh, "%s", text) != PAM_SUCCESS) { + D(("pam_info failed")); + } +} + +static int parse_args(int retval, const char *event, + pam_handle_t *pamh, int argc, const char **argv) +{ + int i; + + for (i=0; i + + + + +
+ +
+ +
+ +
+ +
+ +
+ + \ No newline at end of file diff --git a/modules/pam_deny/meson.build b/modules/pam_deny/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_deny/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_deny/pam_deny.8.xml b/modules/pam_deny/pam_deny.8.xml new file mode 100644 index 0000000..db8fcb6 --- /dev/null +++ b/modules/pam_deny/pam_deny.8.xml @@ -0,0 +1,132 @@ + + + + pam_deny + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_deny + The locking-out PAM module + + + + + pam_deny.so + + + + + + DESCRIPTION + + + This module can be used to deny access. It always indicates a failure + to the application through the PAM framework. It might be suitable + for using for default (the OTHER) entries. + + + + + + OPTIONS + This module does not recognise any options. + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + + + PAM_AUTH_ERR + + + This is returned by the account and auth services. + + + + + + PAM_CRED_ERR + + + This is returned by the setcred function. + + + + + + PAM_AUTHTOK_ERR + + + This is returned by the password service. + + + + + + PAM_SESSION_ERR + + + This is returned by the session service. + + + + + + + + + + EXAMPLES + +#%PAM-1.0 +# +# If we don't have config entries for a service, the +# OTHER entries are used. To be secure, warn and deny +# access to everything. +other auth required pam_warn.so +other auth required pam_deny.so +other account required pam_warn.so +other account required pam_deny.so +other password required pam_warn.so +other password required pam_deny.so +other session required pam_warn.so +other session required pam_deny.so + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_deny was written by Andrew G. Morgan <morgan@kernel.org> + + + + \ No newline at end of file diff --git a/modules/pam_deny/pam_deny.c b/modules/pam_deny/pam_deny.c new file mode 100644 index 0000000..a2fe0c2 --- /dev/null +++ b/modules/pam_deny/pam_deny.c @@ -0,0 +1,60 @@ +/* + * pam_deny module + * + * Written by Andrew Morgan 1996/3/11 + */ + +#include "config.h" +#include + +/* --- authentication management functions --- */ + +int +pam_sm_authenticate(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_AUTH_ERR; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_CRED_ERR; +} + +/* --- account management functions --- */ + +int +pam_sm_acct_mgmt(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_AUTH_ERR; +} + +/* --- password management --- */ + +int +pam_sm_chauthtok(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_AUTHTOK_ERR; +} + +/* --- session management --- */ + +int +pam_sm_open_session(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SESSION_ERR; +} + +int +pam_sm_close_session(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SESSION_ERR; +} + +/* end of module definition */ diff --git a/modules/pam_deny/tst-pam_deny b/modules/pam_deny/tst-pam_deny new file mode 100755 index 0000000..7d9d6ba --- /dev/null +++ b/modules/pam_deny/tst-pam_deny @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_deny.so diff --git a/modules/pam_deny/tst-pam_deny-retval.c b/modules/pam_deny/tst-pam_deny-retval.c new file mode 100644 index 0000000..665fcef --- /dev/null +++ b/modules/pam_deny/tst-pam_deny-retval.c @@ -0,0 +1,58 @@ +/* + * Check pam_deny return values. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_deny" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char user_name[] = ""; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_AUTH_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_CRED_ERR, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_AUTHTOK_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SESSION_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SESSION_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_echo/README.xml b/modules/pam_echo/README.xml new file mode 100644 index 0000000..ceecf9e --- /dev/null +++ b/modules/pam_echo/README.xml @@ -0,0 +1,23 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_echo.8.xml" xpointer='xpointer(id("pam_echo-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_echo/meson.build b/modules/pam_echo/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_echo/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_echo/pam_echo.8.xml b/modules/pam_echo/pam_echo.8.xml new file mode 100644 index 0000000..07b793d --- /dev/null +++ b/modules/pam_echo/pam_echo.8.xml @@ -0,0 +1,167 @@ + + + pam_echo + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_echo + PAM module for printing text messages + + + + + + + pam_echo.so + + file=/path/message + + + + + + DESCRIPTION + + The pam_echo PAM module is for printing + text messages to inform user about special things. Sequences + starting with the % character are + interpreted in the following way: + + + + %H + + The name of the remote host (PAM_RHOST). + + + + %h + + The name of the local host. + + + + %s + + The service name (PAM_SERVICE). + + + + %t + + The name of the controlling terminal (PAM_TTY). + + + + %U + + The remote user name (PAM_RUSER). + + + + %u + + The local user name (PAM_USER). + + + + + + All other sequences beginning with % + expands to the characters following the % + character. + + + + + OPTIONS + + + + file=/path/message + + + + The content of the file /path/message + will be printed with the PAM conversion function as PAM_TEXT_INFO. + + + + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_SUCCESS + + + Message was successful printed. + + + + + PAM_IGNORE + + + PAM_SILENT flag was given or message file does not + exist, no message printed. + + + + + + + + EXAMPLES + + For an example of the use of this module, we show how it may be + used to print information about good passwords: + +password optional pam_echo.so file=/usr/share/doc/good-password.txt +password required pam_unix.so + + + + + + SEE ALSO + + + pam.conf8 + , + + pam.d5 + , + + pam8 + + + + + AUTHOR + Thorsten Kukuk <kukuk@thkukuk.de> + + \ No newline at end of file diff --git a/modules/pam_echo/pam_echo.c b/modules/pam_echo/pam_echo.c new file mode 100644 index 0000000..5a88202 --- /dev/null +++ b/modules/pam_echo/pam_echo.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2005, 2006 Thorsten Kukuk + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 255 +#endif + +#include +#include +#include +#include +#include "pam_inline.h" + +static int +replace_and_print (pam_handle_t *pamh, const char *mesg) +{ + char *output; + size_t length = strlen (mesg) + PAM_MAX_MSG_SIZE; + char myhostname[HOST_NAME_MAX+1]; + const void *str = NULL; + const char *p, *q; + int item; + size_t len; + + output = malloc (length); + if (output == NULL) + { + pam_syslog (pamh, LOG_CRIT, "running out of memory"); + return PAM_BUF_ERR; + } + + for (p = mesg, len = 0; *p != '\0' && len < length - 1; ++p) + { + if (*p != '%' || p[1] == '\0') + { + output[len++] = *p; + continue; + } + switch (*++p) + { + case 'H': + item = PAM_RHOST; + break; + case 'h': + item = -2; /* aka PAM_LOCALHOST */ + break; + case 's': + item = PAM_SERVICE; + break; + case 't': + item = PAM_TTY; + break; + case 'U': + item = PAM_RUSER; + break; + case 'u': + item = PAM_USER; + break; + default: + output[len++] = *p; + continue; + } + if (item == -2) + { + if (gethostname (myhostname, sizeof (myhostname)) == -1) + str = NULL; + else + str = &myhostname; + } + else + { + if (pam_get_item (pamh, item, &str) != PAM_SUCCESS) + str = NULL; + } + if (str == NULL) + str = "(null)"; + for (q = str; *q != '\0' && len < length - 1; ++q) + output[len++] = *q; + } + output[len] = '\0'; + + pam_info (pamh, "%s", output); + free (output); + + return PAM_SUCCESS; +} + +static int +pam_echo (pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + int fd; + int orig_argc = argc; + const char **orig_argv = argv; + const char *file = NULL; + int retval; + + if (flags & PAM_SILENT) + return PAM_IGNORE; + + for (; argc-- > 0; ++argv) + { + const char *str = pam_str_skip_prefix(*argv, "file="); + if (str != NULL) + file = str; + } + + /* No file= option, use argument for output. */ + if (file == NULL || file[0] == '\0') + { + char msg[PAM_MAX_MSG_SIZE]; + const char *p; + int i; + size_t len; + + for (i = 0, len = 0; i < orig_argc && len < sizeof (msg) - 1; ++i) + { + if (i > 0) + msg[len++] = ' '; + for (p = orig_argv[i]; *p != '\0' && len < sizeof(msg) - 1; ++p) + msg[len++] = *p; + } + msg[len] = '\0'; + + retval = replace_and_print (pamh, msg); + } + else if ((fd = open (file, O_RDONLY, 0)) >= 0) + { + char *mtmp = NULL; + struct stat st; + + /* load file into message buffer. */ + if ((fstat (fd, &st) < 0) || !st.st_size) + { + close (fd); + return PAM_IGNORE; + } + + if ((uintmax_t) st.st_size > (uintmax_t) INT_MAX) + { + close (fd); + return PAM_BUF_ERR; + } + + mtmp = malloc (st.st_size + 1); + if (!mtmp) + { + close (fd); + return PAM_BUF_ERR; + } + + if (pam_modutil_read (fd, mtmp, st.st_size) != st.st_size) + { + pam_syslog (pamh, LOG_ERR, "Error while reading %s: %m", file); + free (mtmp); + close (fd); + return PAM_IGNORE; + } + + if (mtmp[st.st_size - 1] == '\n') + mtmp[st.st_size - 1] = '\0'; + else + mtmp[st.st_size] = '\0'; + + close (fd); + retval = replace_and_print (pamh, mtmp); + free (mtmp); + } + else + { + pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", file); + retval = PAM_IGNORE; + } + return retval; +} + +int +pam_sm_authenticate (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return pam_echo (pamh, flags, argc, argv); +} + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +int +pam_sm_acct_mgmt (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return pam_echo (pamh, flags, argc, argv); +} + +int +pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return pam_echo (pamh, flags, argc, argv); +} + +int +pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +int +pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + if (flags & PAM_PRELIM_CHECK) + return pam_echo (pamh, flags, argc, argv); + else + return PAM_IGNORE; +} diff --git a/modules/pam_echo/tst-pam_echo b/modules/pam_echo/tst-pam_echo new file mode 100755 index 0000000..483a2c2 --- /dev/null +++ b/modules/pam_echo/tst-pam_echo @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_echo.so diff --git a/modules/pam_echo/tst-pam_echo-retval.c b/modules/pam_echo/tst-pam_echo-retval.c new file mode 100644 index 0000000..8264cb0 --- /dev/null +++ b/modules/pam_echo/tst-pam_echo-retval.c @@ -0,0 +1,101 @@ +/* + * Check pam_echo return values. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_echo" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char user_name[] = ""; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + /* PAM_SUCCESS -> PAM_SUCCESS, PAM_IGNORE -> PAM_PERM_DENIED */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_SILENT: PAM_IGNORE -> PAM_PERM_DENIED */ + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_PERM_DENIED, pam_acct_mgmt(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_PERM_DENIED, pam_chauthtok(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_PERM_DENIED, pam_open_session(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_PERM_DENIED, pam_close_session(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_IGNORE -> PAM_SUCCESS */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "auth required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "account required %s/" LTDIR "%s.so\n" + "account required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "password required %s/" LTDIR "%s.so\n" + "password required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "session required %s/" LTDIR "%s.so\n" + "session required %s/../pam_permit/" LTDIR "pam_permit.so\n", + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, PAM_SILENT)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_env/README.xml b/modules/pam_env/README.xml new file mode 100644 index 0000000..8becf87 --- /dev/null +++ b/modules/pam_env/README.xml @@ -0,0 +1,21 @@ +
+ + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_env.8.xml" xpointer='xpointer(id("pam_env-name")/*)'/> + + + +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_env/environment b/modules/pam_env/environment new file mode 100644 index 0000000..3e704a6 --- /dev/null +++ b/modules/pam_env/environment @@ -0,0 +1,5 @@ +# +# This file is parsed by pam_env module +# +# Syntax: simple "KEY=VAL" pairs on separate lines +# diff --git a/modules/pam_env/meson.build b/modules/pam_env/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_env/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_env/pam_env.8.xml b/modules/pam_env/pam_env.8.xml new file mode 100644 index 0000000..c7889e0 --- /dev/null +++ b/modules/pam_env/pam_env.8.xml @@ -0,0 +1,329 @@ + + + + pam_env + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_env + + PAM module to set/unset environment variables + + + + + + + + pam_env.so + + debug + + + conffile=conf-file + + + envfile=env-file + + + readenv=0|1 + + + user_envfile=env-file + + + user_readenv=0|1 + + + + + + + DESCRIPTION + + The pam_env PAM module allows the (un)setting of environment + variables. Supported is the use of previously set environment + variables as well as PAM_ITEMs such as + PAM_RHOST. + + + Rules for (un)setting of variables can be defined in an own config + file. The path to this file can be specified with the + conffile option. + If this file does not exist, the default rules are taken from the + config files /etc/security/pam_env.conf and + /etc/security/pam_env.conf.d/*.conf. + If the file /etc/security/pam_env.conf does not + exist, the rules are taken from the files + %vendordir%/security/pam_env.conf, + %vendordir%/security/pam_env.conf.d/*.conf and + /etc/security/pam_env.conf.d/*.conf in that order. + + + By default rules for (un)setting of variables are taken from the + config file /etc/security/pam_env.conf. + If this file does not exist %vendordir%/security/pam_env.conf is used. + An alternate file can be specified with the conffile + option, which overrules all other files. + + + By default rules for (un)setting of variables are taken from the + config file /etc/security/pam_env.conf. An + alternate file can be specified with the conffile + option. + + + Environment variables can be defined in a file with simple KEY=VAL + pairs on separate lines. The path to this file can be specified with the + envfile option. + If this file has not been defined, the settings are read from the + files /etc/security/environment and + /etc/security/environment.d/*. + If the file /etc/environment does not exist, the + settings are read from the files %vendordir%/environment, + %vendordir%/environment.d/* and + /etc/environment.d/* in that order. + And last but not least, with the readenv option this mechanism can + be completely disabled. + + + Second a file (/etc/environment by default) with simple + KEY=VAL pairs on separate lines will be read. + If this file does not exist, %vendordir%/etc/environment is used. + With the envfile option an alternate file can be specified, + which overrules all other files. + And with the readenv option this can be completely disabled. + + + Second a file (/etc/environment by default) with simple + KEY=VAL pairs on separate lines will be read. + With the envfile option an alternate file can be specified. + And with the readenv option this can be completely disabled. + + + Third it will read a user configuration file + ($HOME/.pam_environment by default). + The default file can be changed with the + user_envfile option + and it can be turned on and off with the user_readenv option. + + + Since setting of PAM environment variables can have side effects + to other modules, this module should be the last one on the stack. + + + This module is only executed if the main application calls + + pam_setcred3 + or + + pam_open_session3 + . + The module does nothing and returns PAM_IGNORE if called by + + pam_authenticate3 + . + + + + + OPTIONS + + + + + conffile=/path/to/pam_env.conf + + + + Indicate an alternative pam_env.conf + style configuration file to override the default. This can + be useful when different services need different environments. + + + + + + + debug + + + + A lot of debug information is printed with + syslog3. + + + + + + + envfile=/path/to/environment + + + + Indicate an alternative environment + file to override the default. The syntax are simple + KEY=VAL pairs on separate lines. The + export instruction can be specified for bash + compatibility, but will be ignored. + This can be useful when different services need different environments. + + + + + + + readenv=0|1 + + + + Turns on or off the reading of the file specified by envfile + (0 is off, 1 is on). By default this option is on. + + + + + + + + user_envfile=filename + + + + Indicate an alternative .pam_environment + file to override the default. The syntax is the same as + for /etc/security/pam_env.conf. + The filename is relative to the user home directory. + This can be useful when different services need different + environments. + + + + + + + user_readenv=0|1 + + + + Turns on or off the reading of the user specific environment + file. 0 is off, 1 is on. By default this option is off as user + supplied environment variables in the PAM environment could affect + behavior of subsequent modules in the stack without the consent + of the system administrator. + + + Due to problematic security this functionality is deprecated + since the 1.5.0 version and will be removed completely at some + point in the future. + + + + + + + + + MODULE TYPES PROVIDED + + The and module + types are provided. + + + + + RETURN VALUES + + + PAM_ABORT + + + Not all relevant data or options could be gotten. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_IGNORE + + + No pam_env.conf and environment file was found or the module got + called by + + pam_authenticate3 + . + + + + + PAM_SUCCESS + + + Environment variables were set. + + + + + + + + FILES + + + %vendordir%/security/pam_env.conf + /etc/security/pam_env.conf + + Default configuration file + + + + %vendordir%/environment + /etc/environment + + Default environment file + + + + $HOME/.pam_environment + + User specific environment file + + + + + + + SEE ALSO + + + pam_env.conf5 + , + + pam.d5 + , + + pam8 + , + + environ7 + . + + + + + AUTHOR + + pam_env was written by Dave Kinchlea <kinch@kinch.ark.com>. + + + diff --git a/modules/pam_env/pam_env.c b/modules/pam_env/pam_env.c new file mode 100644 index 0000000..5947b86 --- /dev/null +++ b/modules/pam_env/pam_env.c @@ -0,0 +1,1143 @@ +/* + * pam_env module + * + * Written by Dave Kinchlea 1997/01/31 + * Inspired by Andrew Morgan , who also supplied the + * template for this file (via pam_mail) + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_ECONF +#include "pam_econf.h" +#endif + +#include +#include +#include +#include +#include "pam_inline.h" + +#ifndef USE_ECONF +#include "pam_line.h" +#endif + +/* This little structure makes it easier to keep variables together */ + +typedef struct var { + char *name; + char *value; + char *defval; + char *override; +} VAR; + +#define DEFAULT_ETC_ENVFILE "/etc/environment" +#ifdef VENDORDIR +#define VENDOR_DEFAULT_ETC_ENVFILE (VENDORDIR "/environment") +#endif +#define DEFAULT_READ_ENVFILE 1 + +#define DEFAULT_USER_ENVFILE ".pam_environment" +#define DEFAULT_USER_READ_ENVFILE 0 + +#define DEFAULT_CONF_FILE (SCONFIG_DIR "/pam_env.conf") +#ifdef VENDOR_SCONFIG_DIR +#define VENDOR_DEFAULT_CONF_FILE (VENDOR_SCONFIG_DIR "/pam_env.conf") +#endif + +#define GOOD_LINE 0 +#define BAD_LINE 100 /* This must be > the largest PAM_* error code */ + +#define DEFINE_VAR 101 +#define UNDEFINE_VAR 102 +#define ILLEGAL_VAR 103 + +struct string_buffer { + char *str; + size_t len; + size_t size; +}; + +/* This is a special value used to designate an empty string */ +static char quote='\0'; + +static void free_string_array(char **array) +{ + if (array == NULL) + return; + for (char **entry = array; *entry != NULL; ++entry) { + pam_overwrite_string(*entry); + free(*entry); + } + free(array); +} + +/* argument parsing */ + +#define PAM_DEBUG_ARG 0x01 + +static int +_pam_parse (const pam_handle_t *pamh, int argc, const char **argv, + const char **conffile, const char **envfile, int *readenv, + const char **user_envfile, int *user_readenv) +{ + int ctrl=0; + + *user_envfile = DEFAULT_USER_ENVFILE; + *envfile = NULL; + *readenv = DEFAULT_READ_ENVFILE; + *user_readenv = DEFAULT_USER_READ_ENVFILE; + *conffile = NULL; + + /* step through arguments */ + for (; argc-- > 0; ++argv) { + const char *str; + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_DEBUG_ARG; + else if ((str = pam_str_skip_prefix(*argv, "conffile=")) != NULL) { + if (str[0] == '\0') { + pam_syslog(pamh, LOG_ERR, + "conffile= specification missing argument - ignored"); + } else { + *conffile = str; + D(("new Configuration File: %s", *conffile)); + } + } else if ((str = pam_str_skip_prefix(*argv, "envfile=")) != NULL) { + if (str[0] == '\0') { + pam_syslog (pamh, LOG_ERR, + "envfile= specification missing argument - ignored"); + } else { + *envfile = str; + D(("new Env File: %s", *envfile)); + } + } else if ((str = pam_str_skip_prefix(*argv, "user_envfile=")) != NULL) { + if (str[0] == '\0') { + pam_syslog (pamh, LOG_ERR, + "user_envfile= specification missing argument - ignored"); + } else { + *user_envfile = str; + D(("new User Env File: %s", *user_envfile)); + } + } else if ((str = pam_str_skip_prefix(*argv, "readenv=")) != NULL) { + *readenv = atoi(str); + } else if ((str = pam_str_skip_prefix(*argv, "user_readenv=")) != NULL) { + *user_readenv = atoi(str); + } else + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + + if (*user_readenv) + pam_syslog(pamh, LOG_DEBUG, "deprecated reading of user environment enabled"); + + return ctrl; +} + +#ifdef USE_ECONF + +#define ENVIRONMENT "environment" +#define PAM_ENV "pam_env" + +static int +isDirectory(const char *path) { + struct stat statbuf; + if (stat(path, &statbuf) != 0) + return 0; + return S_ISDIR(statbuf.st_mode); +} + +/* + * Remove escaped newline from string. + * + * All occurrences of "\\n" will be removed from string. + */ +static void +econf_unescnl(char *val) +{ + char *dest, *p; + + dest = p = val; + + while (*p != '\0') { + if (p[0] == '\\' && p[1] == '\n') { + p += 2; + } else { + *dest++ = *p++; + } + } + *dest = '\0'; +} + +static int +econf_read_file(const pam_handle_t *pamh, const char *filename, const char *delim, + const char *name, const char *suffix, const char *subpath, + char ***lines) +{ + econf_file *key_file = NULL; + econf_err error; + size_t key_number = 0; + char **keys = NULL; + const char *base_dir = ""; + + if (filename != NULL) { + if (isDirectory(filename)) { + /* Set base directory which can be different from root */ + D(("filename argument is a directory: %s", filename)); + base_dir = filename; + } else { + /* Read only one file */ + error = econf_readFile (&key_file, filename, delim, "#"); + D(("File name is: %s", filename)); + if (error != ECONF_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "Unable to open env file: %s: %s", filename, + econf_errString(error)); + if (error == ECONF_NOFILE) + return PAM_IGNORE; + else + return PAM_ABORT; + } + } + } + if (filename == NULL || base_dir[0] != '\0') { + /* Read and merge all setting in e.g. /usr/etc and /etc */ + char *vendor_dir = NULL, *sysconf_dir; + if (subpath != NULL && subpath[0] != '\0') { +#ifdef VENDORDIR + if (asprintf(&vendor_dir, "%s%s/%s/", base_dir, VENDORDIR, subpath) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + return PAM_BUF_ERR; + } +#endif + if (asprintf(&sysconf_dir, "%s%s/%s/", base_dir, SYSCONFDIR, subpath) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + free(vendor_dir); + return PAM_BUF_ERR; + } + } else { +#ifdef VENDORDIR + if (asprintf(&vendor_dir, "%s%s/", base_dir, VENDORDIR) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + return PAM_BUF_ERR; + } +#endif + if (asprintf(&sysconf_dir, "%s%s/", base_dir, SYSCONFDIR) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + free(vendor_dir); + return PAM_BUF_ERR; + } + } + + error = pam_econf_readconfig (&key_file, vendor_dir, sysconf_dir, name, suffix, + delim, "#", NULL, NULL); + free(vendor_dir); + free(sysconf_dir); + if (error != ECONF_SUCCESS) { + if (error == ECONF_NOFILE) { + pam_syslog(pamh, LOG_ERR, "Configuration file not found: %s%s", name, suffix); + return PAM_IGNORE; + } else { + char *error_filename = NULL; + uint64_t error_line = 0; + + econf_errLocation(&error_filename, &error_line); + pam_syslog(pamh, LOG_ERR, "Unable to read configuration file %s line %ld: %s", + error_filename, + error_line, + econf_errString(error)); + free(error_filename); + return PAM_ABORT; + } + } + } + + error = econf_getKeys(key_file, NULL, &key_number, &keys); + if (error != ECONF_SUCCESS && error != ECONF_NOKEY) { + pam_syslog(pamh, LOG_ERR, "Unable to read keys: %s", + econf_errString(error)); + econf_freeFile(key_file); + return PAM_ABORT; + } + + *lines = calloc((key_number + 1), sizeof(char**)); + if (*lines == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + econf_free(keys); + econf_freeFile(key_file); + return PAM_BUF_ERR; + } + + size_t n = 0; + for (size_t i = 0; i < key_number; i++) { + char *val; + + error = econf_getStringValue (key_file, NULL, keys[i], &val); + if (error != ECONF_SUCCESS || val == NULL) { + pam_syslog(pamh, LOG_ERR, "Unable to get string from key %s: %s", + keys[i], + econf_errString(error)); + } else { + econf_unescnl(val); + if (asprintf(&(*lines)[n],"%s%c%s", keys[i], delim[0], val) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + econf_free(keys); + econf_freeFile(key_file); + (*lines)[i] = NULL; + free_string_array(*lines); + free (val); + return PAM_BUF_ERR; + } + free (val); + n++; + } + } + + econf_free(keys); + econf_free(key_file); + return PAM_SUCCESS; +} + +#else + +static int read_file(const pam_handle_t *pamh, const char*filename, char ***lines) +{ + FILE *conf; + struct pam_line_buffer buffer; + + _pam_line_buffer_init(&buffer); + + D(("Parsed file name is: %s", filename)); + + if ((conf = fopen(filename,"r")) == NULL) { + pam_syslog(pamh, LOG_ERR, "Unable to open env file: %s", filename); + return PAM_IGNORE; + } + + size_t i = 0; + *lines = malloc((i + 1)* sizeof(char*)); + if (*lines == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + (void) fclose(conf); + return PAM_BUF_ERR; + } + (*lines)[i] = 0; + while (_pam_line_assemble(conf, &buffer, '\0') > 0) { + char *p = buffer.assembled; + char **tmp = NULL; + D(("Read line: %s", p)); + tmp = realloc(*lines, (++i + 1) * sizeof(char*)); + if (tmp == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + (void) fclose(conf); + free_string_array(*lines); + _pam_line_buffer_clear(&buffer); + return PAM_BUF_ERR; + } + *lines = tmp; + (*lines)[i-1] = strdup(p); + if ((*lines)[i-1] == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory."); + (void) fclose(conf); + free_string_array(*lines); + _pam_line_buffer_clear(&buffer); + return PAM_BUF_ERR; + } + (*lines)[i] = 0; + } + + (void) fclose(conf); + _pam_line_buffer_clear(&buffer); + return PAM_SUCCESS; +} +#endif + +static int +_parse_line(const pam_handle_t *pamh, const char *buffer, VAR *var) +{ + /* + * parse buffer into var, legal syntax is + * VARIABLE [DEFAULT=[[string]] [OVERRIDE=[value]] + * + * Any other options defined make this a bad line, + * error logged and no var set + */ + + int length, quoteflg=0; + const char *ptr, *tmpptr; + char **valptr; + + D(("Called buffer = <%s>", buffer)); + + length = strcspn(buffer," \t\n"); + + /* + * The first thing on the line HAS to be the variable name, + * it may be the only thing though. + */ + if ((var->name = strndup(buffer, length)) == NULL) { + D(("out of memory")); + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return PAM_BUF_ERR; + } + D(("var->name = <%s>, length = %d", var->name, length)); + + /* + * Now we check for arguments, we only support two kinds and ('cause I am lazy) + * each one can actually be listed any number of times + */ + + ptr = buffer+length; + while ((length = strspn(ptr, " \t")) > 0) { + ptr += length; /* remove leading whitespace */ + D(("%s", ptr)); + if ((tmpptr = pam_str_skip_prefix(ptr, "DEFAULT=")) != NULL) { + ptr = tmpptr; + D(("Default arg found: <%s>", ptr)); + valptr=&(var->defval); + } else if ((tmpptr = pam_str_skip_prefix(ptr, "OVERRIDE=")) != NULL) { + ptr = tmpptr; + D(("Override arg found: <%s>", ptr)); + valptr=&(var->override); + } else { + D(("Unrecognized options: <%s> - ignoring line", ptr)); + pam_syslog(pamh, LOG_ERR, "Unrecognized Option: %s - ignoring line", ptr); + return BAD_LINE; + } + + if ('"' != *ptr) { /* Escaped quotes not supported */ + length = strcspn(ptr, " \t\n"); + tmpptr = ptr+length; + } else { + tmpptr = strchr(++ptr, '"'); + if (!tmpptr) { + D(("Unterminated quoted string: %s", ptr-1)); + pam_syslog(pamh, LOG_ERR, "Unterminated quoted string: %s", ptr-1); + return BAD_LINE; + } + length = tmpptr - ptr; + if (*++tmpptr && ' ' != *tmpptr && '\t' != *tmpptr && '\n' != *tmpptr) { + D(("Quotes must cover the entire string: <%s>", ptr)); + pam_syslog(pamh, LOG_ERR, "Quotes must cover the entire string: <%s>", ptr); + return BAD_LINE; + } + quoteflg++; + } + if (length) { + if (*valptr != "e) + free(*valptr); + if ((*valptr = strndup(ptr, length)) == NULL) { + D(("out of memory")); + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return PAM_BUF_ERR; + } + } else if (quoteflg) { + quoteflg--; + *valptr = "e; /* a quick hack to handle the empty string */ + } + ptr = tmpptr; /* Start the search where we stopped */ + } /* while */ + + /* + * The line is parsed, all is well. + */ + + D(("Exit.")); + ptr = NULL; tmpptr = NULL; valptr = NULL; + return GOOD_LINE; +} + +static const char * +_pam_get_item_byname(pam_handle_t *pamh, const char *name) +{ + /* + * This function just allows me to use names as given in the config + * file and translate them into the appropriate PAM_ITEM macro + */ + + int item; + const void *itemval; + + D(("Called.")); + if (strcmp(name, "PAM_USER") == 0 || strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0) { + item = PAM_USER; + } else if (strcmp(name, "PAM_USER_PROMPT") == 0) { + item = PAM_USER_PROMPT; + } else if (strcmp(name, "PAM_TTY") == 0) { + item = PAM_TTY; + } else if (strcmp(name, "PAM_RUSER") == 0) { + item = PAM_RUSER; + } else if (strcmp(name, "PAM_RHOST") == 0) { + item = PAM_RHOST; + } else { + D(("Unknown PAM_ITEM: <%s>", name)); + pam_syslog (pamh, LOG_ERR, "Unknown PAM_ITEM: <%s>", name); + return NULL; + } + + if (pam_get_item(pamh, item, &itemval) != PAM_SUCCESS) { + D(("pam_get_item failed")); + return NULL; /* let pam_get_item() log the error */ + } + + if (itemval && (strcmp(name, "HOME") == 0 || strcmp(name, "SHELL") == 0)) { + struct passwd *user_entry; + user_entry = pam_modutil_getpwnam (pamh, itemval); + if (!user_entry) { + pam_syslog(pamh, LOG_ERR, "No such user!?"); + return NULL; + } + return (strcmp(name, "SHELL") == 0) ? + user_entry->pw_shell : + user_entry->pw_dir; + } + + D(("Exit.")); + return itemval; +} + +static void +_strbuf_init(struct string_buffer *buffer) +{ + buffer->str = NULL; + buffer->len = 0; + buffer->size = 0; +} + +static void +_strbuf_free(struct string_buffer *buffer) +{ + pam_overwrite_n(buffer->str, buffer->len); + _pam_drop(buffer->str); + buffer->len = 0; + buffer->size = 0; +} + +/* + * Allocates given amount of bytes in buffer for addition. + * Internally adding an extra byte for NUL character. + * + * Returns 0 on success, 1 on error. + */ +static int +_strbuf_reserve(struct string_buffer *buffer, size_t add) +{ + char *p; + size_t s; + + /* Is already enough memory allocated? */ + if (add < buffer->size - buffer->len) { + return 0; + } + + /* Can the requested bytes (plus additional NUL byte) fit at all? */ + if (buffer->len >= SIZE_MAX - add) { + return 1; + } + + if (buffer->size == 0 && add < 64) { + /* Start with 64 bytes if that's enough */ + s = 64; + } else if (buffer->size >= SIZE_MAX / 2 || buffer->size * 2 < add + 1) { + /* If doubling is not enough (or not possible), get as much as needed */ + s = buffer->len + add + 1; + } else { + /* Ideally, double allocated memory */ + s = buffer->size * 2; + } + + if ((p = realloc(buffer->str, s)) == NULL) { + return 1; + } + + buffer->str = p; + buffer->size = s; + + return 0; +} + +static int +_strbuf_add_char(struct string_buffer *buffer, char c) +{ + D(("Called <%s> + <%c>.", buffer->str == NULL ? "" : buffer->str, c)); + + if (_strbuf_reserve(buffer, 1)) { + return 1; + } + + buffer->str[buffer->len++] = c; + buffer->str[buffer->len] = '\0'; + + return 0; +} + +static int +_strbuf_add_string(struct string_buffer *buffer, const char *str) +{ + size_t len = strlen(str); + + D(("Called <%s> + <%s>.", buffer->str == NULL ? "" : buffer->str, str)); + + if (_strbuf_reserve(buffer, len)) { + return 1; + } + + strcpy(buffer->str + buffer->len, str); + buffer->len += len; + + return 0; +} + +static int +_expand_arg(pam_handle_t *pamh, char **value) +{ + const char *orig=*value; + struct string_buffer buf; + + /* + * Return early if there are no special characters present in the value. + */ + if ((*value)[strcspn(*value, "\\$@")] == '\0') { + return PAM_SUCCESS; + } + + _strbuf_init(&buf); + + /* + * (possibly non-existent) environment variables can be used as values + * by prepending a "$" and wrapping in {} (ie: ${HOST}), can escape with "\" + * (possibly non-existent) PAM items can be used as values + * by prepending a "@" and wrapping in {} (ie: @{PAM_RHOST}, can escape + * + */ + D(("Expanding <%s>",orig)); + while (*orig) { /* while there is some input to deal with */ + if ('\\' == *orig) { + ++orig; + if ('$' != *orig && '@' != *orig && '\\' != *orig) { + D(("Unrecognized escaped character: <%c> - ignoring", *orig)); + pam_syslog(pamh, LOG_ERR, + "Unrecognized escaped character: <%c> - ignoring", + *orig); + } else { + /* Note the increment */ + if (_strbuf_add_char(&buf, *orig++)) { + goto buf_err; + } + } + continue; + } + if ('$' == *orig || '@' == *orig) { + if ('{' != *(orig+1)) { + D(("Expandable variables must be wrapped in {}" + " <%s> - ignoring", orig)); + pam_syslog(pamh, LOG_ERR, "Expandable variables must be wrapped in {}" + " <%s> - ignoring", orig); + /* Note the increment */ + if (_strbuf_add_char(&buf, *orig++)) { + goto buf_err; + } + continue; + } else { + const char *tmpptr=NULL, *tmpval; + char *ptr; /* + * Sure would be nice to use tmpptr but it needs to be + * a constant so that the compiler will shut up when I + * call pam_getenv and _pam_get_item_byname -- sigh + */ + char type; + + D(("Expandable argument: <%s>", orig)); + type = *orig; + orig+=2; /* skip the ${ or @{ characters */ + ptr = strchr(orig, '}'); + if (ptr) { + *ptr++ = '\0'; + } else { + D(("Unterminated expandable variable: <%s>", orig-2)); + pam_syslog(pamh, LOG_ERR, + "Unterminated expandable variable: <%s>", orig-2); + goto abort_err; + } + tmpval = orig; + orig=ptr; + /* + * so, we know we need to expand tmpval, it is either + * an environment variable or a PAM_ITEM. type will tell us which + */ + switch (type) { + + case '$': + D(("Expanding env var: <%s>",tmpval)); + tmpptr = pam_getenv(pamh, tmpval); + D(("Expanded to <%s>", tmpptr)); + break; + + case '@': + D(("Expanding pam item: <%s>",tmpval)); + tmpptr = _pam_get_item_byname(pamh, tmpval); + D(("Expanded to <%s>", tmpptr)); + break; + + default: + D(("Impossible error, type == <%c>", type)); + pam_syslog(pamh, LOG_CRIT, "Impossible error, type == <%c>", type); + goto abort_err; + } /* switch */ + + if (tmpptr) { + if (_strbuf_add_string(&buf, tmpptr)) { + goto buf_err; + } + } + } /* if ('{' != *orig++) */ + } else { /* if ( '$' == *orig || '@' == *orig) */ + /* Note the increment */ + if (_strbuf_add_char(&buf, *orig++)) { + goto buf_err; + } + } + } /* for (;*orig;) */ + + if (buf.len > strlen(*value)) { + free(*value); + if ((*value = strdup(buf.str)) == NULL) { + goto buf_err; + } + } else { + const char *tmpptr = buf.str == NULL ? "" : buf.str; + strcpy(*value, tmpptr); + } + _strbuf_free(&buf); + D(("Exit.")); + + return PAM_SUCCESS; +buf_err: + _strbuf_free(&buf); + return PAM_BUF_ERR; +abort_err: + _strbuf_free(&buf); + return PAM_ABORT; +} + +static int +_check_var(pam_handle_t *pamh, VAR *var) +{ + /* + * Examine the variable and determine what action to take. + * Returns DEFINE_VAR, UNDEFINE_VAR depending on action to take + * or a PAM_* error code if passed back from other routines + * + * if no DEFAULT provided, the empty string is assumed + * if no OVERRIDE provided, the empty string is assumed + * if DEFAULT= and OVERRIDE evaluates to the empty string, + * this variable should be undefined + * if DEFAULT="" and OVERRIDE evaluates to the empty string, + * this variable should be defined with no value + * if OVERRIDE=value and value turns into the empty string, DEFAULT is used + * + * If DEFINE_VAR is to be returned, the correct value to define will + * be pointed to by var->value + */ + + int retval; + + D(("Called.")); + + /* + * First thing to do is to expand any arguments, but only + * if they are not the special quote values (cause expand_arg + * changes memory). + */ + + if (var->defval && ("e != var->defval) && + ((retval = _expand_arg(pamh, &(var->defval))) != PAM_SUCCESS)) { + return retval; + } + if (var->override && ("e != var->override) && + ((retval = _expand_arg(pamh, &(var->override))) != PAM_SUCCESS)) { + return retval; + } + + /* Now it's easy */ + + if (var->override && *(var->override)) { + /* if there is a non-empty string in var->override, we use it */ + D(("OVERRIDE variable <%s> being used: <%s>", var->name, var->override)); + var->value = var->override; + retval = DEFINE_VAR; + } else { + + var->value = var->defval; + if ("e == var->defval) { + /* + * This means that the empty string was given for defval value + * which indicates that a variable should be defined with no value + */ + D(("An empty variable: <%s>", var->name)); + retval = DEFINE_VAR; + } else if (var->defval) { + D(("DEFAULT variable <%s> being used: <%s>", var->name, var->defval)); + retval = DEFINE_VAR; + } else { + D(("UNDEFINE variable <%s>", var->name)); + retval = UNDEFINE_VAR; + } + } + + D(("Exit.")); + return retval; +} + +static void +_clean_var(VAR *var) +{ + if (var->name) { + pam_overwrite_string(var->name); + free(var->name); + } + if (var->defval && ("e != var->defval)) { + pam_overwrite_string(var->defval); + free(var->defval); + } + if (var->override && ("e != var->override)) { + pam_overwrite_string(var->override); + free(var->override); + } + var->name = NULL; + var->value = NULL; /* never has memory specific to it */ + var->defval = NULL; + var->override = NULL; +} + +static int +_define_var(pam_handle_t *pamh, int ctrl, VAR *var) +{ + /* We have a variable to define, this is a simple function */ + + char *envvar; + int retval = PAM_SUCCESS; + + D(("Called.")); + if (asprintf(&envvar, "%s=%s", var->name, var->value) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return PAM_BUF_ERR; + } + + retval = pam_putenv(pamh, envvar); + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "pam_putenv(\"%s\")", envvar); + } + _pam_drop(envvar); + D(("Exit.")); + return retval; +} + +static int +_undefine_var(pam_handle_t *pamh, int ctrl, VAR *var) +{ + /* We have a variable to undefine, this is a simple function */ + + D(("Called and exit.")); + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "remove variable \"%s\"", var->name); + } + return pam_putenv(pamh, var->name); +} + +static int +_parse_config_file(pam_handle_t *pamh, int ctrl, const char *file) +{ + int retval; + VAR Var, *var=&Var; + char **conf_list = NULL; + + var->name=NULL; var->defval=NULL; var->override=NULL; + + D(("Called.")); + +#ifdef USE_ECONF + /* If "file" is not NULL, only this file will be parsed. */ + retval = econf_read_file(pamh, file, " \t", PAM_ENV, ".conf", "security", &conf_list); +#else /* !USE_ECONF */ + /* Only one file will be parsed. So, file has to be set. */ + if (file == NULL) { /* No filename has been set via argv. */ + file = DEFAULT_CONF_FILE; +# ifdef VENDOR_DEFAULT_CONF_FILE + /* + * Check whether DEFAULT_CONF_FILE file is available. + * If it does not exist, fall back to VENDOR_DEFAULT_CONF_FILE file. + */ + struct stat stat_buffer; + if (stat(file, &stat_buffer) != 0 && errno == ENOENT) + file = VENDOR_DEFAULT_CONF_FILE; +# endif + } + retval = read_file(pamh, file, &conf_list); +#endif + + if (retval != PAM_SUCCESS) + return retval; + + for (char **conf = conf_list; *conf != NULL; ++conf) { + if ((retval = _parse_line(pamh, *conf, var)) == GOOD_LINE) { + retval = _check_var(pamh, var); + + if (DEFINE_VAR == retval) { + retval = _define_var(pamh, ctrl, var); + + } else if (UNDEFINE_VAR == retval) { + retval = _undefine_var(pamh, ctrl, var); + } + } + if (PAM_SUCCESS != retval && ILLEGAL_VAR != retval + && BAD_LINE != retval && PAM_BAD_ITEM != retval) break; + + _clean_var(var); + + } /* for */ + + /* tidy up */ + free_string_array(conf_list); + _clean_var(var); /* We could have got here prematurely, + * this is safe though */ + D(("Exit.")); + return (retval != 0 ? PAM_ABORT : PAM_SUCCESS); +} + +static int +_parse_env_file(pam_handle_t *pamh, int ctrl, const char *file) +{ + int retval=PAM_SUCCESS, i, t; + char *key, *mark; + char **env_list = NULL; + +#ifdef USE_ECONF + retval = econf_read_file(pamh, file, "=", ENVIRONMENT, "", "", &env_list); +#else + /* Only one file will be parsed. So, file has to be set. */ + if (file == NULL) /* No filename has been set via argv. */ + file = DEFAULT_ETC_ENVFILE; +#ifdef VENDOR_DEFAULT_ETC_ENVFILE + /* + * Check whether file is available. + * If it does not exist, fall back to VENDOR_DEFAULT_ETC_ENVFILE; file. + */ + struct stat stat_buffer; + if (stat(file, &stat_buffer) != 0 && errno == ENOENT) { + file = VENDOR_DEFAULT_ETC_ENVFILE; + } +#endif + retval = read_file(pamh, file, &env_list); +#endif + + if (retval != PAM_SUCCESS) + return retval == PAM_IGNORE ? PAM_SUCCESS : retval; + + for (char **env = env_list; *env != NULL; ++env) { + key = *env; + + /* skip leading white space */ + key += strspn(key, " \n\t"); + + /* skip blanks lines and comments */ + if (key[0] == '#') + continue; + + /* skip over "export " if present so we can be compat with + bash type declarations */ + if (strncmp(key, "export ", (size_t) 7) == 0) + key += 7; + + /* now find the end of value */ + mark = key; + while(mark[0] != '\n' && mark[0] != '#' && mark[0] != '\0') + mark++; + if (mark[0] != '\0') + mark[0] = '\0'; + + /* + * sanity check, the key must be alphanumeric + */ + + if (key[0] == '=') { + pam_syslog(pamh, LOG_ERR, + "missing key name '%s' in %s', ignoring", + key, file); + continue; + } + + for ( i = 0 ; key[i] != '=' && key[i] != '\0' ; i++ ) + if (!isalnum((unsigned char)key[i]) && key[i] != '_') { + pam_syslog(pamh, LOG_ERR, + "non-alphanumeric key '%s' in %s', ignoring", + key, file); + break; + } + /* non-alphanumeric key, ignore this line */ + if (key[i] != '=' && key[i] != '\0') + continue; + + /* now we try to be smart about quotes around the value, + but not too smart, we can't get all fancy with escaped + values like bash */ + if (key[i] == '=' && (key[++i] == '\"' || key[i] == '\'')) { + for ( t = i+1 ; key[t] != '\0' ; t++) + if (key[t] != '\"' && key[t] != '\'') + key[i++] = key[t]; + else if (key[t+1] != '\0') + key[i++] = key[t]; + key[i] = '\0'; + } + + /* if this is a request to delete a variable, check that it's + actually set first, so we don't get a vague error back from + pam_putenv() */ + for (i = 0; key[i] != '=' && key[i] != '\0'; i++); + + if (key[i] == '\0' && !pam_getenv(pamh,key)) + continue; + + /* set the env var, if it fails, we break out of the loop */ + retval = pam_putenv(pamh, key); + if (retval != PAM_SUCCESS) { + D(("error setting env \"%s\"", key)); + break; + } else if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, + "pam_putenv(\"%s\")", key); + } + } + + /* tidy up */ + free_string_array(env_list); + D(("Exit.")); + return retval; +} + +/* --- authentication management functions (only) --- */ + +int +pam_sm_authenticate (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +static int +handle_env (pam_handle_t *pamh, int argc, const char **argv) +{ + int retval, ctrl, readenv=DEFAULT_READ_ENVFILE; + int user_readenv = DEFAULT_USER_READ_ENVFILE; + const char *conf_file = NULL, *env_file = NULL, *user_env_file = NULL; + + /* + * this module sets environment variables read in from a file + */ + + D(("Called.")); + ctrl = _pam_parse(pamh, argc, argv, &conf_file, &env_file, + &readenv, &user_env_file, &user_readenv); + + retval = _parse_config_file(pamh, ctrl, conf_file); + + if(readenv && retval == PAM_SUCCESS) { + retval = _parse_env_file(pamh, ctrl, env_file); + if (retval == PAM_IGNORE) + retval = PAM_SUCCESS; + } + + if(user_readenv && retval == PAM_SUCCESS) { + char *envpath = NULL; + struct passwd *user_entry = NULL; + const char *username; + struct stat statbuf; + + username = _pam_get_item_byname(pamh, "PAM_USER"); + + if (username) + user_entry = pam_modutil_getpwnam (pamh, username); + if (!user_entry) { + pam_syslog(pamh, LOG_ERR, "No such user!?"); + } + else { + if (asprintf(&envpath, "%s/%s", user_entry->pw_dir, user_env_file) < 0) + { + pam_syslog(pamh, LOG_CRIT, "Out of memory"); + return PAM_BUF_ERR; + } + if (stat(envpath, &statbuf) == 0) { + PAM_MODUTIL_DEF_PRIVS(privs); + + if (pam_modutil_drop_priv(pamh, &privs, user_entry)) { + retval = PAM_SESSION_ERR; + } else { + retval = _parse_config_file(pamh, ctrl, envpath); + if (pam_modutil_regain_priv(pamh, &privs)) + retval = PAM_SESSION_ERR; + } + if (retval == PAM_IGNORE) + retval = PAM_SUCCESS; + } + free(envpath); + } + } + + /* indicate success or failure */ + D(("Exit.")); + return retval; +} + +int +pam_sm_acct_mgmt (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + pam_syslog (pamh, LOG_NOTICE, "pam_sm_acct_mgmt called inappropriately"); + return PAM_SERVICE_ERR; +} + +int +pam_sm_setcred (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + D(("Called")); + return handle_env (pamh, argc, argv); +} + +int +pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + D(("Called")); + return handle_env (pamh, argc, argv); +} + +int +pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + D(("Called and Exit")); + return PAM_SUCCESS; +} + +int +pam_sm_chauthtok (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + pam_syslog (pamh, LOG_NOTICE, "pam_sm_chauthtok called inappropriately"); + return PAM_SERVICE_ERR; +} + +/* end of module definition */ diff --git a/modules/pam_env/pam_env.conf b/modules/pam_env/pam_env.conf new file mode 100644 index 0000000..2549e43 --- /dev/null +++ b/modules/pam_env/pam_env.conf @@ -0,0 +1,73 @@ +# +# This is the configuration file for pam_env, a PAM module to load in +# a configurable list of environment variables for a +# +# The original idea for this came from Andrew G. Morgan ... +# +# Mmm. Perhaps you might like to write a pam_env module that reads a +# default environment from a file? I can see that as REALLY +# useful... Note it would be an "auth" module that returns PAM_IGNORE +# for the auth part and sets the environment returning PAM_SUCCESS in +# the setcred function... +# +# +# What I wanted was the REMOTEHOST variable set, purely for selfish +# reasons, and AGM didn't want it added to the SimpleApps login +# program (which is where I added the patch). So, my first concern is +# that variable, from there there are numerous others that might/would +# be useful to be set: NNTPSERVER, LESS, PATH, PAGER, MANPAGER ..... +# +# Of course, these are a different kind of variable than REMOTEHOST in +# that they are things that are likely to be configured by +# administrators rather than set by logging in, how to treat them both +# in the same config file? +# +# Here is my idea: +# +# Each line starts with the variable name, there are then two possible +# options for each variable DEFAULT and OVERRIDE. +# DEFAULT allows an administrator to set the value of the +# variable to some default value, if none is supplied then the empty +# string is assumed. The OVERRIDE option tells pam_env that it should +# enter in its value (overriding the default value) if there is one +# to use. OVERRIDE is not used, "" is assumed and no override will be +# done. +# +# VARIABLE [DEFAULT=[value]] [OVERRIDE=[value]] +# +# (Possibly non-existent) environment variables may be used in values +# using the ${string} syntax and (possibly non-existent) PAM_ITEMs may +# be used in values using the @{string} syntax. Both the $ and @ +# characters can be backslash escaped to be used as literal values +# values can be delimited with "", escaped " not supported. +# Note that many environment variables that you would like to use +# may not be set by the time the module is called. +# For example, HOME is used below several times, but +# many PAM applications don't make it available by the time you need it. +# +# +# First, some special variables +# +# Set the REMOTEHOST variable for any hosts that are remote, default +# to "localhost" rather than not being set at all +#REMOTEHOST DEFAULT=localhost OVERRIDE=@{PAM_RHOST} +# +# Set the DISPLAY variable if it seems reasonable +#DISPLAY DEFAULT=${REMOTEHOST}:0.0 OVERRIDE=${DISPLAY} +# +# +# Now some simple variables +# +#PAGER DEFAULT=less +#MANPAGER DEFAULT=less +#LESS DEFAULT="M q e h15 z23 b80" +#NNTPSERVER DEFAULT=localhost +#PATH DEFAULT=${HOME}/bin:/usr/local/bin:/bin\ +#:/usr/bin:/usr/local/bin/X11:/usr/bin/X11 +# +# silly examples of escaped variables, just to show how they work. +# +#DOLLAR DEFAULT=\$ +#DOLLARDOLLAR DEFAULT= OVERRIDE=\$${DOLLAR} +#DOLLARPLUS DEFAULT=\${REMOTEHOST}${REMOTEHOST} +#ATSIGN DEFAULT="" OVERRIDE=\@ diff --git a/modules/pam_env/pam_env.conf.5.xml b/modules/pam_env/pam_env.conf.5.xml new file mode 100644 index 0000000..46df480 --- /dev/null +++ b/modules/pam_env/pam_env.conf.5.xml @@ -0,0 +1,149 @@ + + + + pam_env.conf + 5 + Linux-PAM + Linux-PAM Manual + + + + pam_env.conf + environment + the environment variables config files + + + + + DESCRIPTION + + + The %vendordir%/security/pam_env.conf and + /etc/security/pam_env.conf files specify + the environment variables to be set, unset or modified by + pam_env8. + When someone logs in, these files are read and the environment + variables are set according. + + + The /etc/security/pam_env.conf file specifies + the environment variables to be set, unset or modified by + pam_env8. + When someone logs in, this file is read and the environment + variables are set according. + + + Each line starts with the variable name, there are then two possible + options for each variable DEFAULT and OVERRIDE. DEFAULT allows an + administrator to set the value of the variable to some default + value, if none is supplied then the empty string is assumed. The + OVERRIDE option tells pam_env that it should enter in its value + (overriding the default value) if there is one to use. When OVERRIDE is + not used, "" is assumed and no override will be done. + + + VARIABLE + [DEFAULT=[value]] + [OVERRIDE=[value]] + + + + (Possibly non-existent) environment variables may be used in values + using the ${string} syntax and (possibly non-existent) PAM_ITEMs as well + as HOME and SHELL may be used in values using the @{string} syntax. Both + the $ and @ characters can be backslash escaped to be used as literal values + values can be delimited with "", escaped " not supported. + Note that many environment variables that you would like to use + may not be set by the time the module is called. + For example, ${HOME} is used below several times, but + many PAM applications don't make it available by the time you need it. + The special variables @{HOME} and @{SHELL} are expanded to the values + for the user from the corresponding passwd entry. + + + + The "#" character at start of line (no space + at front) can be used to mark this line as a comment line. + + + + The %vendordir%/environment and /etc/environment files specify + the environment variables to be set. These files must consist of simple + NAME=VALUE pairs on separate lines. + The pam_env8 + module will read these files after the pam_env.conf + file. + + + The /etc/environment file specifies + the environment variables to be set. The file must consist of simple + NAME=VALUE pairs on separate lines. + The pam_env8 + module will read the file after the pam_env.conf + file. + + + + + EXAMPLES + + These are some example lines which might be specified in + /etc/security/pam_env.conf. + + + + Set the REMOTEHOST variable for any hosts that are remote, default + to "localhost" rather than not being set at all + + + REMOTEHOST DEFAULT=localhost OVERRIDE=@{PAM_RHOST} + + + + Set the DISPLAY variable if it seems reasonable + + + DISPLAY DEFAULT=${REMOTEHOST}:0.0 OVERRIDE=${DISPLAY} + + + + Now some simple variables + + + PAGER DEFAULT=less + MANPAGER DEFAULT=less + LESS DEFAULT="M q e h15 z23 b80" + NNTPSERVER DEFAULT=localhost + PATH DEFAULT=${HOME}/bin:/usr/local/bin:/bin\ + :/usr/bin:/usr/local/bin/X11:/usr/bin/X11 + XDG_DATA_HOME DEFAULT=@{HOME}/share/ + + + + Silly examples of escaped variables, just to show how they work. + + + DOLLAR DEFAULT=\$ + DOLLARDOLLAR DEFAULT= OVERRIDE=\$${DOLLAR} + DOLLARPLUS DEFAULT=\${REMOTEHOST}${REMOTEHOST} + ATSIGN DEFAULT="" OVERRIDE=\@ + + + + + SEE ALSO + + pam_env8, + pam.d5, + pam8, + environ7 + + + + + AUTHOR + + pam_env was written by Dave Kinchlea <kinch@kinch.ark.com>. + + + diff --git a/modules/pam_env/tst-pam_env b/modules/pam_env/tst-pam_env new file mode 100755 index 0000000..c40e70a --- /dev/null +++ b/modules/pam_env/tst-pam_env @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_env.so diff --git a/modules/pam_env/tst-pam_env-retval.c b/modules/pam_env/tst-pam_env-retval.c new file mode 100644 index 0000000..c08f44c --- /dev/null +++ b/modules/pam_env/tst-pam_env-retval.c @@ -0,0 +1,290 @@ +/* + * Check pam_env return values. + * + * Copyright (c) 2020-2022 Dmitry V. Levin + * Copyright (c) 2022 Stefan Schubert + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_env" +#define TEST_NAME "tst-" MODULE_NAME "-retval" +#define TEST_NAME_DIR TEST_NAME ".dir" + +static const char service_file[] = TEST_NAME ".service"; +static const char missing_file[] = TEST_NAME ".missing"; +static const char my_conf[] = TEST_NAME ".conf"; +static const char my_env[] = TEST_NAME ".env"; +#ifdef VENDORDIR +static const char dir_usr_etc_security[] = TEST_NAME_DIR VENDOR_SCONFIG_DIR; +static const char usr_env[] = TEST_NAME_DIR VENDORDIR "/environment"; +static const char usr_conf[] = TEST_NAME_DIR VENDOR_SCONFIG_DIR "/pam_env.conf"; +#endif + +static struct pam_conv conv; + +#ifdef VENDORDIR +static void +mkdir_p(const char *pathname, mode_t mode) +{ + if (mkdir(pathname, mode) == 0 || errno == EEXIST) + return; + ASSERT_EQ(errno, ENOENT); + + char *buf; + ASSERT_NE(NULL, buf = strdup(pathname)); + mkdir_p(dirname(buf), mode); + free(buf); + + ASSERT_EQ(0, mkdir(pathname, mode)); +} + +static void +rmdir_p(const char *pathname) +{ + if (rmdir(pathname) != 0) + return; + + char *buf; + ASSERT_NE(NULL, buf = strdup(pathname)); + rmdir_p(dirname(buf)); + free(buf); +} +#endif + +static void +setup(void) +{ + FILE *fp; + + ASSERT_NE(NULL, fp = fopen(my_conf, "w")); + ASSERT_LT(0, fprintf(fp, + "EDITOR\tDEFAULT=vi DEFAULT= DEFAULT=vim\n" + "PAGER\tDEFAULT=more\n" + "# ignore escaped newlines in comments \\\n" + "NAME\tDEFAULT=@{PAM_\\ \t\n" + "USER}\\\\name\n")); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_NE(NULL, fp = fopen(my_env, "w")); + ASSERT_LT(0, fprintf(fp, + "test_value=foo\n" + "test2_value=bar\n")); + ASSERT_EQ(0, fclose(fp)); + +#ifdef VENDORDIR + mkdir_p(dir_usr_etc_security, 0755); + + ASSERT_NE(NULL, fp = fopen(usr_env, "w")); + ASSERT_LT(0, fprintf(fp, + "usr_etc_test=foo\n" + "usr_etc_test2=bar\n")); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_NE(NULL, fp = fopen(usr_conf, "w")); + ASSERT_LT(0, fprintf(fp, + "PAGER DEFAULT=emacs\n" + "MANPAGER DEFAULT=less\n")); + ASSERT_EQ(0, fclose(fp)); +#endif +} + +static void +cleanup(void) +{ + ASSERT_EQ(0, unlink(my_conf)); + ASSERT_EQ(0, unlink(my_env)); +#ifdef VENDORDIR + ASSERT_EQ(0, unlink(usr_env)); + ASSERT_EQ(0, unlink(usr_conf)); + rmdir_p(dir_usr_etc_security); +#endif +} + +static void +check_array(const char **array1, char **array2) +{ + for (const char **a1 = array1; *a1 != NULL; ++a1) { + char **a2; + for (a2 = array2; *a2 != NULL; ++a2) { + if (strcmp(*a1, *a2) == 0) + break; + } + ASSERT_NE(NULL, *a2); + } +} + +static void +check_env(const char **list) +{ + pam_handle_t *pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "user", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + + char **env_list = pam_getenvlist(pamh); + ASSERT_NE(NULL, env_list); + + check_array(list, env_list); + + for (char **e = env_list; *e != NULL; ++e) + free(*e); + free(env_list); + + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); +} + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + setup(); + + /* + * When conffile= specifies a missing file, all methods except + * pam_sm_acct_mgmt and pam_sm_chauthtok return PAM_IGNORE. + * The return code of the stack where every module returns PAM_IGNORE + * is PAM_PERM_DENIED. + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so conffile=%s/%s\n" + "account required %s/" LTDIR "%s.so conffile=%s/%s\n" + "password required %s/" LTDIR "%s.so conffile=%s/%s\n" + "session required %s/" LTDIR "%s.so conffile=%s/%s\n", + cwd, MODULE_NAME, cwd, missing_file, + cwd, MODULE_NAME, cwd, missing_file, + cwd, MODULE_NAME, cwd, missing_file, + cwd, MODULE_NAME, cwd, missing_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* + * When conffile= specifies a missing file, all methods except + * pam_sm_acct_mgmt and pam_sm_chauthtok return PAM_IGNORE. + * pam_permit is added after pam_env to convert PAM_IGNORE to PAM_SUCCESS. + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so conffile=%s/%s\n" + "auth required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "account required %s/" LTDIR "%s.so conffile=%s/%s\n" + "account required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "password required %s/" LTDIR "%s.so conffile=%s/%s\n" + "password required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "session required %s/" LTDIR "%s.so conffile=%s/%s\n" + "session required %s/../pam_permit/" LTDIR "pam_permit.so\n", + cwd, MODULE_NAME, cwd, missing_file, cwd, + cwd, MODULE_NAME, cwd, missing_file, cwd, + cwd, MODULE_NAME, cwd, missing_file, cwd, + cwd, MODULE_NAME, cwd, missing_file, cwd)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* + * conffile= specifies an existing file, + * envfile= specifies an empty file. + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "session required %s/" LTDIR "%s.so" + " conffile=%s/%s envfile=%s\n", + cwd, MODULE_NAME, + cwd, my_conf, "/dev/null")); + ASSERT_EQ(0, fclose(fp)); + + const char *env1[] = { "EDITOR=vim", "PAGER=more", "NAME=user\\name", NULL }; + check_env(env1); + + /* + * conffile= specifies an empty file, + * envfile= specifies an existing file. + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "session required %s/" LTDIR "%s.so" + " conffile=%s envfile=%s/%s\n", + cwd, MODULE_NAME, + "/dev/null", cwd, my_env)); + ASSERT_EQ(0, fclose(fp)); + + const char *env2[] = { "test_value=foo", "test2_value=bar", NULL }; + check_env(env2); + +#if defined (USE_ECONF) && defined (VENDORDIR) + + /* envfile is a directory. So values will be read from {TEST_NAME_DIR}/usr/etc and {TEST_NAME_DIR}/etc */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "session required %s/" LTDIR "%s.so" + " conffile=%s envfile=%s/%s/\n", + cwd, MODULE_NAME, + "/dev/null", + cwd, TEST_NAME_DIR)); + ASSERT_EQ(0, fclose(fp)); + + const char *env3[] = {"usr_etc_test=foo", "usr_etc_test2=bar", NULL}; + check_env(env3); + + /* conffile is a directory. So values will be read from {TEST_NAME_DIR}/usr/etc and {TEST_NAME_DIR}/etc */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "session required %s/" LTDIR "%s.so" + " conffile=%s/%s/ envfile=%s\n", + cwd, MODULE_NAME, + cwd, TEST_NAME_DIR, + "/dev/null")); + ASSERT_EQ(0, fclose(fp)); + + const char *env4[] = {"PAGER=emacs", "MANPAGER=less", NULL}; + check_env(env4); + +#endif + + /* cleanup */ + cleanup(); + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_exec/README.xml b/modules/pam_exec/README.xml new file mode 100644 index 0000000..1928d7f --- /dev/null +++ b/modules/pam_exec/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_exec.8.xml" xpointer='xpointer(id("pam_exec-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_exec/meson.build b/modules/pam_exec/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_exec/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_exec/pam_exec.8.xml b/modules/pam_exec/pam_exec.8.xml new file mode 100644 index 0000000..677d598 --- /dev/null +++ b/modules/pam_exec/pam_exec.8.xml @@ -0,0 +1,316 @@ + + + + pam_exec + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_exec + PAM module which calls an external command + + + + + pam_exec.so + + debug + + + expose_authtok + + + seteuid + + + quiet + + + quiet_log + + + stdout + + + log=file + + + type=type + + + command + + + ... + + + + + + + DESCRIPTION + + + pam_exec is a PAM module that can be used to run + an external command. + + + + The child's environment is set to the current PAM environment list, as + returned by + + pam_getenvlist3 + + In addition, the following PAM items are + exported as environment variables: PAM_RHOST, + PAM_RUSER, PAM_SERVICE, + PAM_TTY, PAM_USER and + PAM_TYPE, which contains one of the module + types: , , + , and + . + + + + Commands called by pam_exec need to be aware of that the user + can have control over the environment. + + + + + + + OPTIONS + + + + + + debug + + + + Print debug information. + + + + + + + expose_authtok + + + + During authentication and password change the calling command can read + the password from + stdin3 + . Only first PAM_MAX_RESP_SIZE + bytes of a password are provided to the command. + + + + + + + log=file + + + + The output of the command is appended to + file + + + + + + + type=type + + + + Only run the command if the module type matches the given type. + + + + + + + stdout + + + + Per default the output of the executed command is written to /dev/null. With this option, the stdout output of the executed command is redirected to the calling application. It's in the responsibility of this application what happens with the output. The option is ignored. + + + + + + + quiet + + + + Per default pam_exec.so will echo the exit status of the + external command if it fails. + Specifying this option will suppress the message. + + + + + + + quiet_log + + + + Per default pam_exec.so will log the exit status of the + external command if it fails. + Specifying this option will suppress the log message. + + + + + + + seteuid + + + + Per default pam_exec.so will execute the external command + with the real user ID of the calling process. + Specifying this option means the command is run + with the effective user ID. + + + + + + + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + + + PAM_SUCCESS + + + The external command was run successfully. + + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_CONV_ERR + + + The conversation method supplied by the application + failed to obtain the username. + + + + + + PAM_INCOMPLETE + + + The conversation method supplied by the application + returned PAM_CONV_AGAIN. + + + + + + PAM_SERVICE_ERR + + + No argument or a wrong number of arguments were given. + + + + + + PAM_SYSTEM_ERR + + + A system error occurred or the command to execute failed. + + + + + + PAM_IGNORE + + + pam_setcred was called, which + does not execute the command. Or, the value given for the type= + parameter did not match the module type. + + + + + + + + + + EXAMPLES + + Add the following line to /etc/pam.d/passwd to + rebuild the NIS database after each local password change: + + password optional pam_exec.so seteuid /usr/bin/make -C /var/yp + + + This will execute the command + make -C /var/yp + with effective user ID. + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_exec was written by Thorsten Kukuk <kukuk@thkukuk.de> and + Josh Triplett <josh@joshtriplett.org>. + + + + diff --git a/modules/pam_exec/pam_exec.c b/modules/pam_exec/pam_exec.c new file mode 100644 index 0000000..e328be1 --- /dev/null +++ b/modules/pam_exec/pam_exec.c @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2006, 2008 Thorsten Kukuk + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "pam_inline.h" +#include "pam_i18n.h" + +#define ENV_ITEM(n) { (n), #n } +static struct { + int item; + const char *name; +} env_items[] = { + ENV_ITEM(PAM_SERVICE), + ENV_ITEM(PAM_USER), + ENV_ITEM(PAM_TTY), + ENV_ITEM(PAM_RHOST), + ENV_ITEM(PAM_RUSER), +}; + +/* move_fd_to_non_stdio copies the given file descriptor to something other + * than stdin, stdout, or stderr. Assumes that the caller will close all + * unwanted fds after calling. */ +static int +move_fd_to_non_stdio (pam_handle_t *pamh, int fd) +{ + while (fd < 3) + { + fd = dup(fd); + if (fd == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "dup failed: %m"); + _exit (err); + } + } + return fd; +} + +static int +call_exec (const char *pam_type, pam_handle_t *pamh, + int argc, const char **argv) +{ + int debug = 0; + int call_setuid = 0; + int quiet = 0; + int quiet_log = 0; + int expose_authtok = 0; + int use_stdout = 0; + int optargc; + const char *logfile = NULL; + char authtok[PAM_MAX_RESP_SIZE] = {}; + pid_t pid; + int fds[2]; + int stdout_fds[2]; + FILE *stdout_file = NULL; + int retval; + const char *name; + struct sigaction newsa, oldsa; + + if (argc < 1) { + pam_syslog (pamh, LOG_ERR, + "This module needs at least one argument"); + return PAM_SERVICE_ERR; + } + + for (optargc = 0; optargc < argc; optargc++) + { + const char *str; + + if (argv[optargc][0] == '/') /* paths starts with / */ + break; + + if (strcasecmp (argv[optargc], "debug") == 0) + debug = 1; + else if (strcasecmp (argv[optargc], "stdout") == 0) + use_stdout = 1; + else if ((str = pam_str_skip_icase_prefix (argv[optargc], "log=")) != NULL) + logfile = str; + else if ((str = pam_str_skip_icase_prefix (argv[optargc], "type=")) != NULL) + { + if (strcmp (pam_type, str) != 0) + return PAM_IGNORE; + } + else if (strcasecmp (argv[optargc], "seteuid") == 0) + call_setuid = 1; + else if (strcasecmp (argv[optargc], "quiet") == 0) + quiet = 1; + else if (strcasecmp (argv[optargc], "quiet_log") == 0) + quiet_log = 1; + else if (strcasecmp (argv[optargc], "expose_authtok") == 0) + expose_authtok = 1; + else + break; /* Unknown option, assume program to execute. */ + } + + /* Request user name to be available. */ + + retval = pam_get_user(pamh, &name, NULL); + if (retval != PAM_SUCCESS) + { + if (retval == PAM_CONV_AGAIN) + retval = PAM_INCOMPLETE; + return retval; + } + + if (expose_authtok == 1) + { + if (strcmp (pam_type, "auth") != 0 && strcmp (pam_type, "password") != 0) + { + pam_syslog (pamh, LOG_ERR, + "expose_authtok not supported for type %s", pam_type); + expose_authtok = 0; + } + else + { + const void *void_pass; + + retval = pam_get_item (pamh, PAM_AUTHTOK, &void_pass); + if (retval != PAM_SUCCESS) + { + if (debug) + pam_syslog (pamh, LOG_DEBUG, + "pam_get_item (PAM_AUTHTOK) failed, return %d", + retval); + return retval; + } + else if (void_pass == NULL) + { + char *resp = NULL; + + retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, + &resp, _("Password: ")); + + if (retval != PAM_SUCCESS) + { + pam_overwrite_string (resp); + _pam_drop (resp); + if (retval == PAM_CONV_AGAIN) + retval = PAM_INCOMPLETE; + return retval; + } + + if (resp) + { + pam_set_item (pamh, PAM_AUTHTOK, resp); + strncpy (authtok, resp, sizeof(authtok) - 1); + pam_overwrite_string (resp); + _pam_drop (resp); + } + } + else + strncpy (authtok, void_pass, sizeof(authtok) - 1); + + if (pipe(fds) != 0) + { + pam_overwrite_array(authtok); + pam_syslog (pamh, LOG_ERR, "Could not create pipe: %m"); + return PAM_SYSTEM_ERR; + } + } + } + + if (use_stdout) + { + if (pipe(stdout_fds) != 0) + { + pam_overwrite_array(authtok); + pam_syslog (pamh, LOG_ERR, "Could not create pipe: %m"); + return PAM_SYSTEM_ERR; + } + stdout_file = fdopen(stdout_fds[0], "r"); + if (!stdout_file) + { + pam_overwrite_array(authtok); + pam_syslog (pamh, LOG_ERR, "Could not fdopen pipe: %m"); + return PAM_SYSTEM_ERR; + } + } + + if (optargc >= argc) { + pam_overwrite_array(authtok); + pam_syslog (pamh, LOG_ERR, "No path given as argument"); + return PAM_SERVICE_ERR; + } + + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) { + pam_overwrite_array(authtok); + pam_syslog(pamh, LOG_ERR, "failed to reset SIGCHLD handler: %m"); + return PAM_SYSTEM_ERR; + } + + pid = fork(); + if (pid == -1) { + pam_overwrite_array(authtok); + return PAM_SYSTEM_ERR; + } + if (pid > 0) /* parent */ + { + int status = 0; + pid_t rc; + + if (expose_authtok) /* send the password to the child */ + { + if (debug) + pam_syslog (pamh, LOG_DEBUG, "send password to child"); + if (write(fds[1], authtok, strlen(authtok)) == -1) + pam_syslog (pamh, LOG_ERR, + "sending password to child failed: %m"); + + close(fds[0]); /* close here to avoid possible SIGPIPE above */ + close(fds[1]); + } + + pam_overwrite_array(authtok); + + if (use_stdout) + { + char *buf = NULL; + size_t n = 0; + close(stdout_fds[1]); + while (getline(&buf, &n, stdout_file) != -1) + { + buf[strcspn(buf, "\n")] = '\0'; + pam_info(pamh, "%s", buf); + } + free(buf); + fclose(stdout_file); + } + + while ((rc = waitpid (pid, &status, 0)) == -1 && + errno == EINTR); + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + if (rc == (pid_t)-1) + { + pam_syslog (pamh, LOG_ERR, "waitpid returns with -1: %m"); + return PAM_SYSTEM_ERR; + } + else if (status != 0) + { + if (WIFEXITED(status)) + { + if (!quiet_log) + pam_syslog (pamh, LOG_ERR, "%s failed: exit code %d", + argv[optargc], WEXITSTATUS(status)); + if (!quiet) + pam_error (pamh, _("%s failed: exit code %d"), + argv[optargc], WEXITSTATUS(status)); + } + else if (WIFSIGNALED(status)) + { + if (!quiet_log) + pam_syslog (pamh, LOG_ERR, "%s failed: caught signal %d%s", + argv[optargc], WTERMSIG(status), + WCOREDUMP(status) ? " (core dumped)" : ""); + if (!quiet) + pam_error (pamh, _("%s failed: caught signal %d%s"), + argv[optargc], WTERMSIG(status), + WCOREDUMP(status) ? " (core dumped)" : ""); + } + else + { + if (!quiet_log) + pam_syslog (pamh, LOG_ERR, "%s failed: unknown status 0x%x", + argv[optargc], status); + if (!quiet) + pam_error (pamh, _("%s failed: unknown status 0x%x"), + argv[optargc], status); + } + return PAM_SYSTEM_ERR; + } + return PAM_SUCCESS; + } + else /* child */ + { + const char **arggv; + int i; + char **envlist; + int envlen, nitems; + char *envstr; + enum pam_modutil_redirect_fd redirect_stdin = + expose_authtok ? PAM_MODUTIL_IGNORE_FD : PAM_MODUTIL_PIPE_FD; + enum pam_modutil_redirect_fd redirect_stdout = + (use_stdout || logfile) ? PAM_MODUTIL_IGNORE_FD : PAM_MODUTIL_NULL_FD; + + pam_overwrite_array(authtok); + + /* First, move all the pipes off of stdin, stdout, and stderr, to ensure + * that calls to dup2 won't close them. */ + + if (expose_authtok) + { + fds[0] = move_fd_to_non_stdio(pamh, fds[0]); + close(fds[1]); + } + + if (use_stdout) + { + stdout_fds[1] = move_fd_to_non_stdio(pamh, stdout_fds[1]); + close(stdout_fds[0]); + } + + /* Set up stdin. */ + + if (expose_authtok) + { + /* reopen stdin as pipe */ + if (dup2(fds[0], STDIN_FILENO) == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "dup2 of STDIN failed: %m"); + _exit (err); + } + } + + /* Set up stdout. */ + + if (use_stdout) + { + if (dup2(stdout_fds[1], STDOUT_FILENO) == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "dup2 to stdout failed: %m"); + _exit (err); + } + } + else if (logfile) + { + time_t tm = time (NULL); + char *buffer = NULL; + + close (STDOUT_FILENO); + if ((i = open (logfile, O_CREAT|O_APPEND|O_WRONLY, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "open of %s failed: %m", + logfile); + _exit (err); + } + if (i != STDOUT_FILENO) + { + if (dup2 (i, STDOUT_FILENO) == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "dup2 failed: %m"); + _exit (err); + } + close (i); + } + if (asprintf (&buffer, "*** %s", ctime (&tm)) > 0) + { + pam_modutil_write (STDOUT_FILENO, buffer, strlen (buffer)); + free (buffer); + } + } + + if ((use_stdout || logfile) && + dup2 (STDOUT_FILENO, STDERR_FILENO) == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "dup2 failed: %m"); + _exit (err); + } + + if (pam_modutil_sanitize_helper_fds(pamh, redirect_stdin, + redirect_stdout, redirect_stdout) < 0) + _exit(1); + + if (call_setuid) + if (setuid (geteuid ()) == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "setuid(%lu) failed: %m", + (unsigned long) geteuid ()); + _exit (err); + } + + if (setsid () == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "setsid failed: %m"); + _exit (err); + } + + arggv = calloc ((size_t) argc + 1, sizeof (char *)); + if (arggv == NULL) + _exit (ENOMEM); + + for (i = 0; i < (argc - optargc); i++) + arggv[i] = argv[i+optargc]; + arggv[i] = NULL; + + /* + * Set up the child's environment list. It consists of the PAM + * environment, plus a few hand-picked PAM items. + */ + envlist = pam_getenvlist(pamh); + for (envlen = 0; envlist[envlen] != NULL; ++envlen) + /* nothing */ ; + nitems = PAM_ARRAY_SIZE(env_items); + /* + 2 because of PAM_TYPE and NULL entry */ + envlist = realloc(envlist, (envlen + nitems + 2) * sizeof(*envlist)); + if (envlist == NULL) + { + pam_syslog (pamh, LOG_CRIT, "realloc environment failed: %m"); + _exit (ENOMEM); + } + for (i = 0; i < nitems; ++i) + { + const void *item; + + if (pam_get_item(pamh, env_items[i].item, &item) != PAM_SUCCESS || item == NULL) + continue; + if (asprintf(&envstr, "%s=%s", env_items[i].name, (const char *)item) < 0) + { + pam_syslog (pamh, LOG_CRIT, "prepare environment failed: %m"); + _exit (ENOMEM); + } + envlist[envlen++] = envstr; + envlist[envlen] = NULL; + } + + if (asprintf(&envstr, "PAM_TYPE=%s", pam_type) < 0) + { + pam_syslog (pamh, LOG_CRIT, "prepare environment failed: %m"); + _exit (ENOMEM); + } + envlist[envlen++] = envstr; + envlist[envlen] = NULL; + + if (debug) + pam_syslog (pamh, LOG_DEBUG, "Calling %s ...", arggv[0]); + + DIAG_PUSH_IGNORE_CAST_QUAL; + execve (arggv[0], (char **) arggv, envlist); + DIAG_POP_IGNORE_CAST_QUAL; + i = errno; + pam_syslog (pamh, LOG_ERR, "execve(%s,...) failed: %m", arggv[0]); + _exit (i); + } + return PAM_SYSTEM_ERR; /* will never be reached. */ +} + +int +pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return call_exec ("auth", pamh, argc, argv); +} + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +/* password updating functions */ + +int +pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + if (flags & PAM_PRELIM_CHECK) + return PAM_SUCCESS; + return call_exec ("password", pamh, argc, argv); +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return call_exec ("account", pamh, argc, argv); +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return call_exec ("open_session", pamh, argc, argv); +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return call_exec ("close_session", pamh, argc, argv); +} diff --git a/modules/pam_exec/tst-pam_exec b/modules/pam_exec/tst-pam_exec new file mode 100755 index 0000000..a0b0039 --- /dev/null +++ b/modules/pam_exec/tst-pam_exec @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_exec.so diff --git a/modules/pam_faildelay/README.xml b/modules/pam_faildelay/README.xml new file mode 100644 index 0000000..8530a3d --- /dev/null +++ b/modules/pam_faildelay/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_faildelay.8.xml" xpointer='xpointer(id("pam_faildelay-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_faildelay/meson.build b/modules/pam_faildelay/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_faildelay/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_faildelay/pam_faildelay.8.xml b/modules/pam_faildelay/pam_faildelay.8.xml new file mode 100644 index 0000000..c31b507 --- /dev/null +++ b/modules/pam_faildelay/pam_faildelay.8.xml @@ -0,0 +1,133 @@ + + + + pam_faildelay + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_faildelay + Change the delay on failure per-application + + + + + pam_faildelay.so + + debug + + + delay=microseconds + + + + + + + DESCRIPTION + + + pam_faildelay is a PAM module that can be used to set + the delay on failure per-application. + + + If no is given, pam_faildelay will + use the value of FAIL_DELAY from /etc/login.defs. + + + + + + OPTIONS + + + + debug + + + + Turns on debugging messages sent to syslog. + + + + + + delay=N + + + + Set the delay on failure to N microseconds. + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_IGNORE + + + Delay was successful adjusted. + + + + + PAM_SYSTEM_ERR + + + The specified delay was not valid. + + + + + + + + EXAMPLES + + The following example will set the delay on failure to + 10 seconds: + +auth optional pam_faildelay.so delay=10000000 + + + + + + SEE ALSO + + + pam_fail_delay3 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_faildelay was written by Darren Tucker <dtucker@zip.com.au>. + + + + \ No newline at end of file diff --git a/modules/pam_faildelay/pam_faildelay.c b/modules/pam_faildelay/pam_faildelay.c new file mode 100644 index 0000000..0d4d42e --- /dev/null +++ b/modules/pam_faildelay/pam_faildelay.c @@ -0,0 +1,152 @@ +/* + * pam_faildelay module + * + * Allows an admin to set the delay on failure per-application. + * Provides "auth" interface only. + * + * Use by putting something like this in the relevant pam config: + * auth required pam_faildelay.so delay=[microseconds] + * + * eg: + * auth required pam_faildelay.so delay=10000000 + * will set the delay on failure to 10 seconds. + * + * If no delay option was given, pam_faildelay.so will use the + * FAIL_DELAY value of /etc/login.defs. + * + * Based on pam_rootok and parts of pam_unix both by Andrew Morgan + * + * + * Copyright (c) 2006 Thorsten Kukuk + * - Rewrite to use extended PAM functions + * - Add /etc/login.defs support + * + * Portions Copyright (c) 2005 Darren Tucker . + * + * Redistribution and use in source and binary forms of, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain any existing copyright + * notice, and this entire permission notice in its entirety, + * including the disclaimer of warranties. + * + * 2. Redistributions in binary form must reproduce all prior and current + * copyright notices, this list of conditions, and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. The name of any author may not be used to endorse or promote + * products derived from this software without their specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of the + * GNU General Public License, in which case the provisions of the GNU + * GPL are required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential conflict between the GNU GPL and the + * restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#include "config.h" +#include "pam_inline.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LOGIN_DEFS "/etc/login.defs" +#define S_TO_MICROS 1000000 + +/* --- authentication management functions (only) --- */ + +static long long parse_delay(const char *val) +{ + long long delay; + char *endptr; + + delay = strtoll (val, &endptr, 10); + if (delay < 0 || val == endptr || *endptr != '\0') + return -1; + return delay; +} + +int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int i, debug_flag = 0; + long long delay = -1; + + /* step through arguments */ + for (i = 0; i < argc; i++) { + const char *val = pam_str_skip_prefix (argv[i], "delay="); + if (val != NULL) { + delay = parse_delay (val); + if (delay < 0 || (unsigned long long) delay > UINT_MAX) + { + pam_syslog (pamh, LOG_ERR, "%s (%s) not valid", argv[i], val); + return PAM_IGNORE; + } + } else if (strcmp (argv[i], "debug") == 0) + debug_flag = 1; + else + pam_syslog (pamh, LOG_ERR, "unknown option; %s", argv[i]); + } + + if (delay == -1) + { + char *val = pam_modutil_search_key (pamh, LOGIN_DEFS, "FAIL_DELAY"); + + if (val == NULL) + return PAM_IGNORE; + + delay = parse_delay (val); + if (delay < 0 || (unsigned long long) delay > UINT_MAX / S_TO_MICROS) + { + pam_syslog (pamh, LOG_ERR, "FAIL_DELAY=%s in %s not valid", + val, LOGIN_DEFS); + free (val); + return PAM_IGNORE; + } + + free (val); + /* delay is in seconds, convert to microseconds. */ + delay *= S_TO_MICROS; + } + + if (debug_flag) + pam_syslog (pamh, LOG_DEBUG, "setting fail delay to %lld", delay); + + i = pam_fail_delay(pamh, (unsigned int) delay); + if (i == PAM_SUCCESS) + return PAM_IGNORE; + else + return i; +} + +int pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +/* end of module definition */ diff --git a/modules/pam_faildelay/tst-pam_faildelay b/modules/pam_faildelay/tst-pam_faildelay new file mode 100755 index 0000000..87f7fd4 --- /dev/null +++ b/modules/pam_faildelay/tst-pam_faildelay @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_faildelay.so diff --git a/modules/pam_faildelay/tst-pam_faildelay-retval.c b/modules/pam_faildelay/tst-pam_faildelay-retval.c new file mode 100644 index 0000000..72b16ef --- /dev/null +++ b/modules/pam_faildelay/tst-pam_faildelay-retval.c @@ -0,0 +1,88 @@ +/* + * Check pam_faildelay return values. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_faildelay" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char user_name[] = ""; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + /* PAM_IGNORE -> PAM_PERM_DENIED */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so delay=1\n" + "account required %s/" LTDIR "%s.so delay=1\n" + "password required %s/" LTDIR "%s.so delay=1\n" + "session required %s/" LTDIR "%s.so delay=1\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_IGNORE -> PAM_SUCCESS */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so delay=1\n" + "auth required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "account required %s/" LTDIR "%s.so delay=1\n" + "account required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "password required %s/" LTDIR "%s.so delay=1\n" + "password required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "session required %s/" LTDIR "%s.so delay=1\n" + "session required %s/../pam_permit/" LTDIR "pam_permit.so\n", + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_faillock/README.xml b/modules/pam_faillock/README.xml new file mode 100644 index 0000000..a62c917 --- /dev/null +++ b/modules/pam_faillock/README.xml @@ -0,0 +1,31 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_faillock.8.xml" xpointer='xpointer(id("pam_faillock-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_faillock/faillock.8.xml b/modules/pam_faillock/faillock.8.xml new file mode 100644 index 0000000..74440fc --- /dev/null +++ b/modules/pam_faillock/faillock.8.xml @@ -0,0 +1,137 @@ + + + + faillock + 8 + Linux-PAM + Linux-PAM Manual + + + + faillock + Tool for displaying and modifying the authentication failure record files + + + + + faillock + + --dir /path/to/tally-directory + + + --user username + + + --reset + + + + + + + DESCRIPTION + + + The pam_faillock.so module maintains a list of + failed authentication attempts per user during a specified interval + and locks the account in case there were more than + deny consecutive failed authentications. + It stores the failure records into per-user files in the tally + directory. + + + The faillock command is an application which + can be used to examine and modify the contents of the + tally files. It can display the recent failed authentication + attempts of the username or clear the tally + files of all or individual usernames. + + + + + + OPTIONS + + + + --conf /path/to/config-file + + + + The file where the configuration is located. The default is + /etc/security/faillock.conf. + + + + + + --dir /path/to/tally-directory + + + + The directory where the user files with the failure records are kept. + + + The priority to set this option is to use the value provided + from the command line. If this isn't provided, then the value + from the configuration file is used. Finally, if neither of + them has been provided, then + /var/run/faillock is used. + + + + + + --user username + + + + The user whose failure records should be displayed or cleared. + + + + + + --reset + + + + Instead of displaying the user's failure records, clear them. + + + + + + + + FILES + + + /var/run/faillock/* + + the files logging the authentication failures for users + + + + + + + SEE ALSO + + + pam_faillock8 + , + + pam8 + + + + + + AUTHOR + + faillock was written by Tomas Mraz. + + + + \ No newline at end of file diff --git a/modules/pam_faillock/faillock.c b/modules/pam_faillock/faillock.c new file mode 100644 index 0000000..9248474 --- /dev/null +++ b/modules/pam_faillock/faillock.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2010 Tomas Mraz + * Copyright (c) 2010, 2016, 2017 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "faillock.h" + +#define ignore_return(x) if (1==((int)(x))) {;} + +int +open_tally (const char *dir, const char *user, uid_t uid, int create) +{ + char *path; + int flags = O_RDWR; + int fd, r; + + if (dir == NULL || strstr(user, "../") != NULL) + /* just a defensive programming as the user must be a + * valid user on the system anyway + */ + return -1; + if (*dir && dir[strlen(dir) - 1] != '/') + r = asprintf(&path, "%s/%s", dir, user); + else + r = asprintf(&path, "%s%s", dir, user); + if (r < 0) + return -1; + + if (create) { + flags |= O_CREAT; + if (access(dir, F_OK) != 0) { + mkdir(dir, 0755); + } + } + + fd = open(path, flags, 0660); + + free(path); + + if (fd != -1) { + struct stat st; + + while (flock(fd, LOCK_EX) == -1 && errno == EINTR); + if (fstat(fd, &st) == 0) { + if (st.st_uid != uid) { + ignore_return(fchown(fd, uid, -1)); + } + + /* + * If umask is set to 022, as will probably in most systems, then the + * group will not be able to write to the file. So, change the file + * permissions just in case. + * Note: owners of this file are user:root, so if the permissions are + * not changed the root process writing to this file will require + * CAP_DAC_OVERRIDE. + */ + if (!(st.st_mode & S_IWGRP)) { + ignore_return(fchmod(fd, 0660)); + } + } + } + + return fd; +} + +#define CHUNK_SIZE (64 * sizeof(struct tally)) +#define MAX_RECORDS 1024 + +int +read_tally(int fd, struct tally_data *tallies) +{ + void *data = NULL, *newdata; + unsigned int count = 0; + ssize_t chunk = 0; + + do { + newdata = realloc(data, count * sizeof(struct tally) + CHUNK_SIZE); + if (newdata == NULL) { + free(data); + return -1; + } + + data = newdata; + + chunk = pam_modutil_read(fd, (char *)data + count * sizeof(struct tally), CHUNK_SIZE); + if (chunk < 0) { + free(data); + return -1; + } + + count += chunk/sizeof(struct tally); + + if (count >= MAX_RECORDS) + break; + } + while (chunk == CHUNK_SIZE); + + tallies->records = data; + tallies->count = count; + + return 0; +} + +int +update_tally(int fd, struct tally_data *tallies) +{ + void *data = tallies->records; + unsigned int count = tallies->count; + ssize_t chunk; + + if (tallies->count > MAX_RECORDS) { + data = tallies->records + (count - MAX_RECORDS); + count = MAX_RECORDS; + } + + if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { + return -1; + } + + chunk = pam_modutil_write(fd, data, count * sizeof(struct tally)); + + if (chunk != (ssize_t)(count * sizeof(struct tally))) { + return -1; + } + + if (ftruncate(fd, count * sizeof(struct tally)) == -1) + return -1; + + return 0; +} diff --git a/modules/pam_faillock/faillock.conf b/modules/pam_faillock/faillock.conf new file mode 100644 index 0000000..16d93df --- /dev/null +++ b/modules/pam_faillock/faillock.conf @@ -0,0 +1,62 @@ +# Configuration for locking the user after multiple failed +# authentication attempts. +# +# The directory where the user files with the failure records are kept. +# The default is /var/run/faillock. +# dir = /var/run/faillock +# +# Will log the user name into the system log if the user is not found. +# Enabled if option is present. +# audit +# +# Don't print informative messages. +# Enabled if option is present. +# silent +# +# Don't log informative messages via syslog. +# Enabled if option is present. +# no_log_info +# +# Only track failed user authentications attempts for local users +# in /etc/passwd and ignore centralized (AD, IdM, LDAP, etc.) users. +# The `faillock` command will also no longer track user failed +# authentication attempts. Enabling this option will prevent a +# double-lockout scenario where a user is locked out locally and +# in the centralized mechanism. +# Enabled if option is present. +# local_users_only +# +# Deny access if the number of consecutive authentication failures +# for this user during the recent interval exceeds n tries. +# The default is 3. +# deny = 3 +# +# The length of the interval during which the consecutive +# authentication failures must happen for the user account +# lock out is n seconds. +# The default is 900 (15 minutes). +# fail_interval = 900 +# +# The access will be re-enabled after n seconds after the lock out. +# The value 0 has the same meaning as value `never` - the access +# will not be re-enabled without resetting the faillock +# entries by the `faillock` command. +# The default is 600 (10 minutes). +# unlock_time = 600 +# +# Root account can become locked as well as regular accounts. +# Enabled if option is present. +# even_deny_root +# +# This option implies the `even_deny_root` option. +# Allow access after n seconds to root account after the +# account is locked. In case the option is not specified +# the value is the same as of the `unlock_time` option. +# root_unlock_time = 900 +# +# If a group name is specified with this option, members +# of the group will be handled by this module the same as +# the root account (the options `even_deny_root>` and +# `root_unlock_time` will apply to them. +# By default, the option is not set. +# admin_group = diff --git a/modules/pam_faillock/faillock.conf.5.xml b/modules/pam_faillock/faillock.conf.5.xml new file mode 100644 index 0000000..cc750fb --- /dev/null +++ b/modules/pam_faillock/faillock.conf.5.xml @@ -0,0 +1,254 @@ + + + + faillock.conf + 5 + Linux-PAM + Linux-PAM Manual + + + + faillock.conf + pam_faillock configuration file + + + + + DESCRIPTION + + faillock.conf provides a way to configure the + default settings for locking the user after multiple failed authentication attempts. + This file is read by the pam_faillock module and is the + preferred method over configuring pam_faillock directly. + + + The file has a very simple name = value format with possible comments + starting with # character. The whitespace at the beginning of line, end + of line, and around the = sign is ignored. + + + + + + OPTIONS + + + + dir=/path/to/tally-directory + + + + The directory where the user files with the failure records are kept. The + default is /var/run/faillock. + + + Note: These files will disappear after reboot on systems configured with + directory /var/run/faillock mounted on virtual memory. + + + + + + audit + + + + Will log the user name into the system log if the user is not found. + + + + + + silent + + + + Don't print informative messages to the user. Please note that when + this option is not used there will be difference in the authentication + behavior for users which exist on the system and non-existing users. + + + + + + no_log_info + + + + Don't log informative messages via syslog3. + + + + + + local_users_only + + + + Only track failed user authentications attempts for local users + in /etc/passwd and ignore centralized (AD, IdM, LDAP, etc.) users. + The faillock8 + command will also no longer track user failed + authentication attempts. Enabling this option will prevent a + double-lockout scenario where a user is locked out locally and + in the centralized mechanism. + + + + + + nodelay + + + + Don't enforce a delay after authentication failures. + + + + + + deny=n + + + + Deny access if the number of consecutive authentication failures + for this user during the recent interval exceeds + n. The default is 3. + + + + + + fail_interval=n + + + + The length of the interval during which the consecutive + authentication failures must happen for the user account + lock out is n seconds. + The default is 900 (15 minutes). + + + + + + unlock_time=n + + + + The access will be re-enabled after + n seconds after the lock out. + The value 0 has the same meaning as value + never - the access + will not be re-enabled without resetting the faillock + entries by the faillock8 command. + The default is 600 (10 minutes). + + + Note that the default directory that pam_faillock + uses is usually cleared on system boot so the access will be also re-enabled + after system reboot. If that is undesirable a different tally directory + must be set with the option. + + + Also note that it is usually undesirable to permanently lock + out users as they can become easily a target of denial of service + attack unless the usernames are random and kept secret to potential + attackers. + + + + + + even_deny_root + + + + Root account can become locked as well as regular accounts. + + + + + + root_unlock_time=n + + + + This option implies option. + Allow access after n seconds + to root account after the account is locked. In case the + option is not specified the value is the same as of the + option. + + + + + + admin_group=name + + + + If a group name is specified with this option, members + of the group will be handled by this module the same as + the root account (the options + and will apply to them. + By default the option is not set. + + + + + + + + EXAMPLES + + /etc/security/faillock.conf file example: + + +deny=4 +unlock_time=1200 +silent + + + + + FILES + + + /etc/security/faillock.conf + + the config file for custom options + + + + + + + SEE ALSO + + + faillock8 + , + + pam_faillock8 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_faillock was written by Tomas Mraz. The support for faillock.conf was written by Brian Ward. + + + + \ No newline at end of file diff --git a/modules/pam_faillock/faillock.h b/modules/pam_faillock/faillock.h new file mode 100644 index 0000000..0ea0ffb --- /dev/null +++ b/modules/pam_faillock/faillock.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010 Tomas Mraz + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * faillock.h - authentication failure data file record structure + * + * Each record in the file represents an instance of login failure of + * the user at the recorded time. + */ + + +#ifndef _FAILLOCK_H +#define _FAILLOCK_H + +#include +#include + +#define TALLY_STATUS_VALID 0x1 /* the tally file entry is valid */ +#define TALLY_STATUS_RHOST 0x2 /* the source is rhost */ +#define TALLY_STATUS_TTY 0x4 /* the source is tty */ +/* If neither TALLY_FLAG_RHOST nor TALLY_FLAG_TTY are set the source is service. */ + +struct tally { + char source[52]; /* rhost or tty of the login failure */ + /* (not necessarily NULL terminated) */ + uint16_t reserved; /* reserved for future use */ + uint16_t status; /* record status */ + uint64_t time; /* time of the login failure */ +}; +/* 64 bytes per entry */ + +struct tally_data { + struct tally *records; /* array of tallies */ + unsigned int count; /* number of records */ +}; + +#define FAILLOCK_DEFAULT_TALLYDIR "/var/run/faillock" + +int open_tally(const char *dir, const char *user, uid_t uid, int create); +int read_tally(int fd, struct tally_data *tallies); +int update_tally(int fd, struct tally_data *tallies); +#endif diff --git a/modules/pam_faillock/faillock_config.c b/modules/pam_faillock/faillock_config.c new file mode 100644 index 0000000..d8e7e80 --- /dev/null +++ b/modules/pam_faillock/faillock_config.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2022 Tomas Mraz + * Copyright (c) 2022 Iker Pedrosa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "faillock_config.h" +#include "faillock.h" + +#define FAILLOCK_DEFAULT_CONF SCONFIG_DIR "/faillock.conf" +#ifdef VENDOR_SCONFIG_DIR +#define VENDOR_FAILLOCK_DEFAULT_CONF VENDOR_SCONFIG_DIR "/faillock.conf" +#endif + +static void PAM_FORMAT((printf, 3, 4)) PAM_NONNULL((3)) +config_log(const pam_handle_t *pamh, int priority, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (pamh) { + pam_vsyslog(pamh, priority, fmt, args); + } else { + char *buf = NULL; + + if (vasprintf(&buf, fmt, args) < 0) { + fprintf(stderr, "vasprintf: %m"); + va_end(args); + return; + } + fprintf(stderr, "%s\n", buf); + free(buf); + } + va_end(args); +} + +/* parse a single configuration file */ +int +read_config_file(pam_handle_t *pamh, struct options *opts, const char *cfgfile) +{ + char *linebuf = NULL; + size_t n = 0; + const char *fname = (cfgfile != NULL) ? cfgfile : FAILLOCK_DEFAULT_CONF; + FILE *f = fopen(fname, "r"); + +#ifdef VENDOR_FAILLOCK_DEFAULT_CONF + if (f == NULL && errno == ENOENT && cfgfile == NULL) { + /* + * If the default configuration file in /etc does not exist, + * try the vendor configuration file as fallback. + */ + f = fopen(VENDOR_FAILLOCK_DEFAULT_CONF, "r"); + } +#endif /* VENDOR_FAILLOCK_DEFAULT_CONF */ + + if (f == NULL) { + /* ignore non-existent default config file */ + if (errno == ENOENT && cfgfile == NULL) + return PAM_SUCCESS; + return PAM_SERVICE_ERR; + } + + while (getline(&linebuf, &n, f) != -1) { + size_t len; + char *ptr; + char *name; + int eq; + + len = strlen(linebuf); + if (len && linebuf[len - 1] != '\n' && !feof(f)) { + free(linebuf); + (void) fclose(f); + return PAM_SERVICE_ERR; + } + + if ((ptr=strchr(linebuf, '#')) != NULL) { + *ptr = '\0'; + } else { + ptr = linebuf + len; + } + + /* drop terminating whitespace including the \n */ + while (ptr > linebuf) { + if (!isspace((unsigned char)*(ptr-1))) { + *ptr = '\0'; + break; + } + --ptr; + } + + /* skip initial whitespace */ + for (ptr = linebuf; isspace((unsigned char)*ptr); ptr++); + if (*ptr == '\0') + continue; + + /* grab the key name */ + eq = 0; + name = ptr; + while (*ptr != '\0') { + if (isspace((unsigned char)*ptr) || *ptr == '=') { + eq = *ptr == '='; + *ptr = '\0'; + ++ptr; + break; + } + ++ptr; + } + + /* grab the key value */ + while (*ptr != '\0') { + if (*ptr != '=' || eq) { + if (!isspace((unsigned char)*ptr)) { + break; + } + } else { + eq = 1; + } + ++ptr; + } + + /* set the key:value pair on opts */ + set_conf_opt(pamh, opts, name, ptr); + } + + free(linebuf); + (void)fclose(f); + return PAM_SUCCESS; +} + +void +set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, + const char *value) +{ + if (strcmp(name, "dir") == 0) { + if (value[0] != '/') { + config_log(pamh, LOG_ERR, + "Tally directory is not absolute path (%s); keeping value", + value); + } else { + free(opts->dir); + opts->dir = strdup(value); + if (opts->dir == NULL) { + opts->fatal_error = 1; + config_log(pamh, LOG_CRIT, "Error allocating memory: %m"); + } + } + } + else if (strcmp(name, "deny") == 0) { + if (sscanf(value, "%hu", &opts->deny) != 1) { + config_log(pamh, LOG_ERR, + "Bad number supplied for deny argument"); + } + } + else if (strcmp(name, "fail_interval") == 0) { + unsigned int temp; + if (sscanf(value, "%u", &temp) != 1 || + temp > MAX_TIME_INTERVAL) { + config_log(pamh, LOG_ERR, + "Bad number supplied for fail_interval argument"); + } else { + opts->fail_interval = temp; + } + } + else if (strcmp(name, "unlock_time") == 0) { + unsigned int temp; + + if (strcmp(value, "never") == 0) { + opts->unlock_time = 0; + } + else if (sscanf(value, "%u", &temp) != 1 || + temp > MAX_TIME_INTERVAL) { + config_log(pamh, LOG_ERR, + "Bad number supplied for unlock_time argument"); + } + else { + opts->unlock_time = temp; + } + } + else if (strcmp(name, "root_unlock_time") == 0) { + unsigned int temp; + + if (strcmp(value, "never") == 0) { + opts->root_unlock_time = 0; + } + else if (sscanf(value, "%u", &temp) != 1 || + temp > MAX_TIME_INTERVAL) { + config_log(pamh, LOG_ERR, + "Bad number supplied for root_unlock_time argument"); + } else { + opts->root_unlock_time = temp; + } + } + else if (strcmp(name, "admin_group") == 0) { + free(opts->admin_group); + opts->admin_group = strdup(value); + if (opts->admin_group == NULL) { + opts->fatal_error = 1; + config_log(pamh, LOG_CRIT, "Error allocating memory: %m"); + } + } + else if (strcmp(name, "even_deny_root") == 0) { + opts->flags |= FAILLOCK_FLAG_DENY_ROOT; + } + else if (strcmp(name, "audit") == 0) { + opts->flags |= FAILLOCK_FLAG_AUDIT; + } + else if (strcmp(name, "silent") == 0) { + opts->flags |= FAILLOCK_FLAG_SILENT; + } + else if (strcmp(name, "no_log_info") == 0) { + opts->flags |= FAILLOCK_FLAG_NO_LOG_INFO; + } + else if (strcmp(name, "local_users_only") == 0) { + opts->flags |= FAILLOCK_FLAG_LOCAL_ONLY; + } + else if (strcmp(name, "nodelay") == 0) { + opts->flags |= FAILLOCK_FLAG_NO_DELAY; + } + else { + config_log(pamh, LOG_ERR, "Unknown option: %s", name); + } +} + +const char *get_tally_dir(const struct options *opts) +{ + return (opts->dir != NULL) ? opts->dir : FAILLOCK_DEFAULT_TALLYDIR; +} diff --git a/modules/pam_faillock/faillock_config.h b/modules/pam_faillock/faillock_config.h new file mode 100644 index 0000000..da5f363 --- /dev/null +++ b/modules/pam_faillock/faillock_config.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 Tomas Mraz + * Copyright (c) 2022 Iker Pedrosa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * faillock_config.h - load configuration options from file + * + */ + +#ifndef _FAILLOCK_CONFIG_H +#define _FAILLOCK_CONFIG_H + +#include +#include +#include + +#include + +#define FAILLOCK_FLAG_DENY_ROOT 0x1 +#define FAILLOCK_FLAG_AUDIT 0x2 +#define FAILLOCK_FLAG_SILENT 0x4 +#define FAILLOCK_FLAG_NO_LOG_INFO 0x8 +#define FAILLOCK_FLAG_UNLOCKED 0x10 +#define FAILLOCK_FLAG_LOCAL_ONLY 0x20 +#define FAILLOCK_FLAG_NO_DELAY 0x40 + +#define MAX_TIME_INTERVAL 604800 /* 7 days */ + +struct options { + unsigned int action; + unsigned int flags; + unsigned short deny; + unsigned int fail_interval; + unsigned int unlock_time; + unsigned int root_unlock_time; + char *dir; + const char *user; + char *admin_group; + int failures; + uint64_t latest_time; + uid_t uid; + int is_admin; + uint64_t now; + int fatal_error; + + unsigned int reset; + const char *progname; + int legacy_output; /* show failure info in pam_tally2 style */ +}; + +int read_config_file(pam_handle_t *pamh, struct options *opts, + const char *cfgfile); +void set_conf_opt(pam_handle_t *pamh, struct options *opts, const char *name, + const char *value); +const char *get_tally_dir(const struct options *opts); + +#endif /* _FAILLOCK_CONFIG_H */ diff --git a/modules/pam_faillock/main.c b/modules/pam_faillock/main.c new file mode 100644 index 0000000..c6dd19c --- /dev/null +++ b/modules/pam_faillock/main.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2010 Tomas Mraz + * Copyright (c) 2010 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBAUDIT +#include + +#define AUDIT_NO_ID ((unsigned int) -1) +#endif + +#include "pam_inline.h" +#include "pam_i18n.h" +#include "faillock.h" +#include "faillock_config.h" + +static int +args_parse(int argc, char **argv, struct options *opts) +{ + int i; + int rv; + const char *dir = NULL; + const char *conf = NULL; + + memset(opts, 0, sizeof(*opts)); + + opts->progname = argv[0]; + + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--conf") == 0) { + ++i; + if (i >= argc || strlen(argv[i]) == 0) { + fprintf(stderr, "%s: No configuration file supplied.\n", + argv[0]); + return -1; + } + conf = argv[i]; + } + else if (strcmp(argv[i], "--dir") == 0) { + ++i; + if (i >= argc || strlen(argv[i]) == 0) { + fprintf(stderr, "%s: No records directory supplied.\n", + argv[0]); + return -1; + } + dir = argv[i]; + } + else if (strcmp(argv[i], "--user") == 0) { + ++i; + if (i >= argc || strlen(argv[i]) == 0) { + fprintf(stderr, "%s: No user name supplied.\n", argv[0]); + return -1; + } + opts->user = argv[i]; + } + else if (strcmp(argv[i], "--reset") == 0) { + opts->reset = 1; + } + else if (!strcmp(argv[i], "--legacy-output")) { + opts->legacy_output = 1; + } + else { + fprintf(stderr, "%s: Unknown option: %s\n", argv[0], argv[i]); + return -1; + } + } + + if ((rv = read_config_file(NULL, opts, conf)) != PAM_SUCCESS) { + fprintf(stderr, "Configuration file missing or broken"); + return rv; + } + + if (dir != NULL) { + free(opts->dir); + opts->dir = strdup(dir); + if (opts->dir == NULL) { + fprintf(stderr, "Error allocating memory: %m"); + return -1; + } + } + + return 0; +} + +static void +usage(const char *progname) +{ + fprintf(stderr, + _("Usage: %s [--dir /path/to/tally-directory]" + " [--user username] [--reset] [--legacy-output]\n"), progname); + +} + +static int +get_local_time(time_t when, char *timebuf, size_t timebuf_size) +{ + struct tm *tm; + + tm = localtime(&when); + if (tm == NULL) { + return -1; + } + strftime(timebuf, timebuf_size, "%Y-%m-%d %H:%M:%S", tm); + return 0; +} + +static void +print_in_new_format(struct options *opts, const struct tally_data *tallies, const char *user) +{ + uint32_t i; + + printf("%s:\n", user); + printf("%-19s %-5s %-48s %-5s\n", "When", "Type", "Source", "Valid"); + + for (i = 0; i < tallies->count; i++) { + uint16_t status; + char timebuf[80]; + + if (get_local_time(tallies->records[i].time, timebuf, sizeof(timebuf)) != 0) { + fprintf(stderr, "%s: Invalid timestamp in the tally record\n", + opts->progname); + continue; + } + + status = tallies->records[i].status; + + printf("%-19s %-5s %-52.52s %s\n", timebuf, + status & TALLY_STATUS_RHOST ? "RHOST" : (status & TALLY_STATUS_TTY ? "TTY" : "SVC"), + tallies->records[i].source, status & TALLY_STATUS_VALID ? "V":"I"); + } +} + +static void +print_in_legacy_format(struct options *opts, const struct tally_data *tallies, const char *user) +{ + uint32_t tally_count; + static uint32_t pr_once; + + if (pr_once == 0) { + printf(_("Login Failures Latest failure From\n")); + pr_once = 1; + } + + printf("%-15.15s ", user); + + tally_count = tallies->count; + + if (tally_count > 0) { + uint32_t i; + char timebuf[80]; + + i = tally_count - 1; + + if (get_local_time(tallies->records[i].time, timebuf, sizeof(timebuf)) != 0) { + fprintf(stderr, "%s: Invalid timestamp in the tally record\n", + opts->progname); + return; + } + + printf("%5u %25s %s\n", + tally_count, timebuf, tallies->records[i].source); + } + else { + printf("%5u\n", tally_count); + } +} + +static int +do_user(struct options *opts, const char *user) +{ + int fd; + int rv; + struct tally_data tallies; + struct passwd *pwd; + const char *dir = get_tally_dir(opts); + + pwd = getpwnam(user); + if (pwd == NULL) { + fprintf(stderr, "%s: Error no such user: %s\n", opts->progname, user); + return 1; + } + + fd = open_tally(dir, user, pwd->pw_uid, 1); + + if (fd == -1) { + if (errno == ENOENT) { + return 0; + } + else { + fprintf(stderr, "%s: Error opening the tally file for %s:", + opts->progname, user); + perror(NULL); + return 3; + } + } + if (opts->reset) { +#ifdef HAVE_LIBAUDIT + int audit_fd; +#endif + + while ((rv=ftruncate(fd, 0)) == -1 && errno == EINTR); + if (rv == -1) { + fprintf(stderr, "%s: Error clearing the tally file for %s:", + opts->progname, user); + perror(NULL); +#ifdef HAVE_LIBAUDIT + } + if ((audit_fd=audit_open()) >= 0) { + (void) !audit_log_acct_message(audit_fd, + AUDIT_USER_MGMT, NULL, + "faillock-reset", user, + pwd != NULL ? pwd->pw_uid : AUDIT_NO_ID, + NULL, NULL, NULL, rv == 0); + close(audit_fd); + } + if (rv == -1) { +#endif + close(fd); + return 4; + } + } + else { + memset(&tallies, 0, sizeof(tallies)); + if (read_tally(fd, &tallies) == -1) { + fprintf(stderr, "%s: Error reading the tally file for %s:", + opts->progname, user); + perror(NULL); + close(fd); + return 5; + } + + if (opts->legacy_output == 0) { + print_in_new_format(opts, &tallies, user); + } + else { + print_in_legacy_format(opts, &tallies, user); + } + + free(tallies.records); + } + close(fd); + return 0; +} + +static int +do_allusers(struct options *opts) +{ + struct dirent **userlist; + int rv, i; + const char *dir = get_tally_dir(opts); + + rv = scandir(dir, &userlist, NULL, alphasort); + if (rv < 0) { + fprintf(stderr, "%s: Error reading tally directory: %m\n", opts->progname); + return 2; + } + + for (i = 0; i < rv; i++) { + if (userlist[i]->d_name[0] == '.') { + if ((userlist[i]->d_name[1] == '.' && userlist[i]->d_name[2] == '\0') || + userlist[i]->d_name[1] == '\0') + continue; + } + do_user(opts, userlist[i]->d_name); + free(userlist[i]); + } + free(userlist); + + return 0; +} + + +/*-----------------------------------------------------------------------*/ +int +main (int argc, char *argv[]) +{ + struct options opts; + + if (args_parse(argc, argv, &opts)) { + usage(argv[0]); + return 1; + } + + if (opts.user == NULL) { + return do_allusers(&opts); + } + + return do_user(&opts, opts.user); +} diff --git a/modules/pam_faillock/meson.build b/modules/pam_faillock/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_faillock/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_faillock/pam_faillock.8.xml b/modules/pam_faillock/pam_faillock.8.xml new file mode 100644 index 0000000..ce0ae05 --- /dev/null +++ b/modules/pam_faillock/pam_faillock.8.xml @@ -0,0 +1,380 @@ + + + + pam_faillock + 8 + Linux-PAM Manual + + + + pam_faillock + Module counting authentication failures during a specified interval + + + + + auth ... pam_faillock.so + + preauth|authfail|authsucc + + + conf=/path/to/config-file + + + dir=/path/to/tally-directory + + + even_deny_root + + + deny=n + + + fail_interval=n + + + unlock_time=n + + + root_unlock_time=n + + + admin_group=name + + + audit + + + silent + + + no_log_info + + + + account ... pam_faillock.so + + dir=/path/to/tally-directory + + + no_log_info + + + + + + + DESCRIPTION + + + This module maintains a list of failed authentication attempts per + user during a specified interval and locks the account in case + there were more than deny consecutive + failed authentications. + + + Normally, failed attempts to authenticate root will + not cause the root account to become + blocked, to prevent denial-of-service: if your users aren't given + shell accounts and root may only login via su or + at the machine console (not telnet/rsh, etc), this is safe. + + + + + + OPTIONS + + + + {preauth|authfail|authsucc} + + + + This argument must be set accordingly to the position of this module + instance in the PAM stack. + + + The preauth argument must be used when the module + is called before the modules which ask for the user credentials such + as the password. The module just examines whether the user should + be blocked from accessing the service in case there were anomalous + number of failed consecutive authentication attempts recently. This + call is optional if authsucc is used. + + + The authfail argument must be used when the module + is called after the modules which determine the authentication outcome, + failed. Unless the user is already blocked due to previous authentication + failures, the module will record the failure into the appropriate user + tally file. + + + The authsucc argument must be used when the module + is called after the modules which determine the authentication outcome, + succeeded. Unless the user is already blocked due to previous authentication + failures, the module will then clear the record of the failures in the + respective user tally file. Otherwise it will return authentication error. + If this call is not done, the pam_faillock will not distinguish between + consecutive and non-consecutive failed authentication attempts. The + preauth call must be used in such case. Due to + complications in the way the PAM stack can be configured it is also + possible to call pam_faillock as an account module. + In such configuration the module must be also called in the + preauth stage. + + + + + + conf=/path/to/config-file + + + + Use another configuration file instead of the default + /etc/security/faillock.conf. + + + Use another configuration file instead of the default + which is to use the file + /etc/security/faillock.conf or, + if that one is not present, the file + %vendordir%/security/faillock.conf. + + + + + + The options for configuring the module behavior are described in the + faillock.conf5 + manual page. The options specified on the module command + line override the values from the configuration file. + + + + + MODULE TYPES PROVIDED + + The and module types are + provided. + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + An invalid option was given, the module was not able + to retrieve the user name, no valid counter file + was found, or too many failed logins. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_CONV_ERR + + + The conversation method supplied by the application + failed to obtain the username. + + + + + PAM_INCOMPLETE + + + The conversation method supplied by the application + returned PAM_CONV_AGAIN. + + + + + PAM_SUCCESS + + + Everything was successful. + + + + + PAM_IGNORE + + + User not present in passwd database. + + + + + + + + NOTES + + Configuring options on the module command line is not recommend. The + /etc/security/faillock.conf should be used instead. + + + The setup of pam_faillock in the PAM stack is different + from the pam_tally2 module setup. + + + Individual files with the failure records are created as owned by + the user. This allows pam_faillock.so module + to work correctly when it is called from a screensaver. + + + Note that using the module in without the + option specified in /etc/security/faillock.conf + or with requisite control field leaks an information about + existence or non-existence of a user account in the system because + the failures are not recorded for the unknown users. The message + about the user account being locked is never displayed for non-existing + user accounts allowing the adversary to infer that a particular account + is not existing on a system. + + + + + EXAMPLES + + Here are two possible configuration examples for /etc/pam.d/login. + They make pam_faillock to lock the account after 4 consecutive + failed logins during the default interval of 15 minutes. Root account will be locked + as well. The accounts will be automatically unlocked after 20 minutes. + + + In the first example the module is called only in the auth + phase and the module does not print any information about the account being blocked + by pam_faillock. The preauth call can + be added to tell users that their logins are blocked by the module and also to abort + the authentication without even asking for password in such case. + + + /etc/security/faillock.conf file example: + + +deny=4 +unlock_time=1200 +silent + + + /etc/pam.d/config file example: + + +auth required pam_securetty.so +auth required pam_env.so +auth required pam_nologin.so +# optionally call: auth requisite pam_faillock.so preauth +# to display the message about account being locked +auth [success=1 default=bad] pam_unix.so +auth [default=die] pam_faillock.so authfail +auth sufficient pam_faillock.so authsucc +auth required pam_deny.so +account required pam_unix.so +password required pam_unix.so shadow +session required pam_selinux.so close +session required pam_loginuid.so +session required pam_unix.so +session required pam_selinux.so open + + + In the second example the module is called both in the auth + and account phases and the module informs the authenticating + user when the account is locked if option is not + specified in the faillock.conf. + + +auth required pam_securetty.so +auth required pam_env.so +auth required pam_nologin.so +auth required pam_faillock.so preauth +# optionally use requisite above if you do not want to prompt for the password +# on locked accounts +auth sufficient pam_unix.so +auth [default=die] pam_faillock.so authfail +auth required pam_deny.so +account required pam_faillock.so +# if you drop the above call to pam_faillock.so the lock will be done also +# on non-consecutive authentication failures +account required pam_unix.so +password required pam_unix.so shadow +session required pam_selinux.so close +session required pam_loginuid.so +session required pam_unix.so +session required pam_selinux.so open + + + + + FILES + + + /var/run/faillock/* + + the files logging the authentication failures for users + + Note: These files will disappear after reboot on systems configured with + directory /var/run/faillock mounted on virtual memory. + For persistent storage use the option dir= in + file /etc/security/faillock.conf. + + + + + /etc/security/faillock.conf + + the config file for pam_faillock options + + + + %vendordir%/security/faillock.conf + + + the config file for pam_faillock options. It will be used if + /etc/security/faillock.conf does not exist. + + + + + + + + SEE ALSO + + + faillock8 + , + + faillock.conf5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_faillock was written by Tomas Mraz. + + + + \ No newline at end of file diff --git a/modules/pam_faillock/pam_faillock.c b/modules/pam_faillock/pam_faillock.c new file mode 100644 index 0000000..9385038 --- /dev/null +++ b/modules/pam_faillock/pam_faillock.c @@ -0,0 +1,562 @@ +/* + * Copyright (c) 2010, 2017, 2019 Tomas Mraz + * Copyright (c) 2010, 2017, 2019 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBAUDIT +#include +#endif + +#include +#include +#include + +#include "pam_inline.h" +#include "pam_i18n.h" +#include "faillock.h" +#include "faillock_config.h" + +#define FAILLOCK_ACTION_PREAUTH 0 +#define FAILLOCK_ACTION_AUTHSUCC 1 +#define FAILLOCK_ACTION_AUTHFAIL 2 + +static int +args_parse(pam_handle_t *pamh, int argc, const char **argv, + int flags, struct options *opts) +{ + int i; + int config_arg_index = -1; + int rv; + const char *conf = NULL; + + memset(opts, 0, sizeof(*opts)); + + opts->deny = 3; + opts->fail_interval = 900; + opts->unlock_time = 600; + opts->root_unlock_time = MAX_TIME_INTERVAL+1; + + for (i = 0; i < argc; ++i) { + const char *str = pam_str_skip_prefix(argv[i], "conf="); + + if (str != NULL) { + conf = str; + config_arg_index = i; + } + } + + if ((rv = read_config_file(pamh, opts, conf)) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, + "Configuration file missing or broken"); + return rv; + } + + for (i = 0; i < argc; ++i) { + if (i == config_arg_index) { + continue; + } + else if (strcmp(argv[i], "preauth") == 0) { + opts->action = FAILLOCK_ACTION_PREAUTH; + } + else if (strcmp(argv[i], "authfail") == 0) { + opts->action = FAILLOCK_ACTION_AUTHFAIL; + } + else if (strcmp(argv[i], "authsucc") == 0) { + opts->action = FAILLOCK_ACTION_AUTHSUCC; + } + else { + char *name, *val; + + if ((name = strdup(argv[i])) == NULL) { + pam_syslog(pamh, LOG_CRIT, + "Error allocating memory: %m"); + return PAM_BUF_ERR; + } + + val = strchr(name, '='); + if (val != NULL) { + *val = '\0'; + ++val; + } + else { + val = name + strlen(name); + } + set_conf_opt(pamh, opts, name, val); + + free(name); + } + } + + if (opts->root_unlock_time == MAX_TIME_INTERVAL+1) + opts->root_unlock_time = opts->unlock_time; + if (flags & PAM_SILENT) + opts->flags |= FAILLOCK_FLAG_SILENT; + + if (opts->fatal_error) + return PAM_BUF_ERR; + return PAM_SUCCESS; +} + +static int +check_local_user (pam_handle_t *pamh, const char *user) +{ + return pam_modutil_check_user_in_passwd(pamh, user, NULL) == PAM_SUCCESS; +} + +static int +get_pam_user(pam_handle_t *pamh, struct options *opts) +{ + const char *user; + int rv; + struct passwd *pwd; + + if ((rv=pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) { + return rv == PAM_CONV_AGAIN ? PAM_INCOMPLETE : rv; + } + + if (*user == '\0') { + return PAM_IGNORE; + } + + if ((pwd=pam_modutil_getpwnam(pamh, user)) == NULL) { + if (opts->flags & FAILLOCK_FLAG_AUDIT) { + pam_syslog(pamh, LOG_NOTICE, "User unknown: %s", user); + } + else { + pam_syslog(pamh, LOG_NOTICE, "User unknown"); + } + return PAM_IGNORE; + } + opts->user = user; + opts->uid = pwd->pw_uid; + + if (pwd->pw_uid == 0) { + opts->is_admin = 1; + return PAM_SUCCESS; + } + + if (opts->admin_group && *opts->admin_group) { + opts->is_admin = pam_modutil_user_in_group_uid_nam(pamh, + pwd->pw_uid, opts->admin_group); + } + + return PAM_SUCCESS; +} + +static int +check_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies, int *fd) +{ + int tfd; + unsigned int i; + uint64_t latest_time; + int failures; + const char *dir = get_tally_dir(opts); + + opts->now = time(NULL); + + tfd = open_tally(dir, opts->user, opts->uid, 0); + + *fd = tfd; + + if (tfd == -1) { + if (errno == EACCES || errno == ENOENT) { + return PAM_SUCCESS; + } + pam_syslog(pamh, LOG_ERR, "Error opening the tally file for %s: %m", opts->user); + return PAM_SYSTEM_ERR; + } + + if (read_tally(tfd, tallies) != 0) { + pam_syslog(pamh, LOG_ERR, "Error reading the tally file for %s: %m", opts->user); + return PAM_SYSTEM_ERR; + } + + if (opts->is_admin && !(opts->flags & FAILLOCK_FLAG_DENY_ROOT)) { + return PAM_SUCCESS; + } + + latest_time = 0; + for (i = 0; i < tallies->count; i++) { + if ((tallies->records[i].status & TALLY_STATUS_VALID) && + tallies->records[i].time > latest_time) + latest_time = tallies->records[i].time; + } + + opts->latest_time = latest_time; + + failures = 0; + for (i = 0; i < tallies->count; i++) { + if ((tallies->records[i].status & TALLY_STATUS_VALID) && + latest_time - tallies->records[i].time < opts->fail_interval) { + ++failures; + } + } + + opts->failures = failures; + + if (opts->deny && failures >= opts->deny) { + if ((!opts->is_admin && opts->unlock_time && latest_time + opts->unlock_time < opts->now) || + (opts->is_admin && opts->root_unlock_time && latest_time + opts->root_unlock_time < opts->now)) { +#ifdef HAVE_LIBAUDIT + if (opts->action != FAILLOCK_ACTION_PREAUTH) { /* do not audit in preauth */ + char buf[64]; + int audit_fd; + const void *rhost = NULL, *tty = NULL; + + audit_fd = audit_open(); + /* If there is an error & audit support is in the kernel report error */ + if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT)) + return PAM_SYSTEM_ERR; + + (void)pam_get_item(pamh, PAM_TTY, &tty); + (void)pam_get_item(pamh, PAM_RHOST, &rhost); + snprintf(buf, sizeof(buf), "op=pam_faillock suid=%u ", opts->uid); + if (audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_UNLOCK_TIMED, buf, + rhost, NULL, tty, 1) <= 0) + pam_syslog(pamh, LOG_ERR, + "Error sending audit message: %m"); + audit_close(audit_fd); + } +#endif + opts->flags |= FAILLOCK_FLAG_UNLOCKED; + return PAM_SUCCESS; + } + return PAM_AUTH_ERR; + } + return PAM_SUCCESS; +} + +static void +reset_tally(pam_handle_t *pamh, struct options *opts, int *fd) +{ + int rv; + const char *dir = get_tally_dir(opts); + + if (*fd == -1) { + *fd = open_tally(dir, opts->user, opts->uid, 1); + } + else { + while ((rv=ftruncate(*fd, 0)) == -1 && errno == EINTR); + if (rv == -1) { + pam_syslog(pamh, LOG_ERR, "Error clearing the tally file for %s: %m", opts->user); + } + } +} + +static int +write_tally(pam_handle_t *pamh, struct options *opts, struct tally_data *tallies, int *fd) +{ + struct tally *records; + unsigned int i; + int failures; + unsigned int oldest; + uint64_t oldtime; + const void *source = NULL; + const char *dir = get_tally_dir(opts); + + if (*fd == -1) { + *fd = open_tally(dir, opts->user, opts->uid, 1); + } + if (*fd == -1) { + if (errno == EACCES) { + return PAM_SUCCESS; + } + pam_syslog(pamh, LOG_ERR, "Error opening the tally file for %s: %m", opts->user); + return PAM_SYSTEM_ERR; + } + + oldtime = 0; + oldest = 0; + failures = 0; + + for (i = 0; i < tallies->count; ++i) { + if (oldtime == 0 || tallies->records[i].time < oldtime) { + oldtime = tallies->records[i].time; + oldest = i; + } + if (opts->flags & FAILLOCK_FLAG_UNLOCKED || + opts->now - tallies->records[i].time >= opts->fail_interval ) { + tallies->records[i].status &= ~TALLY_STATUS_VALID; + } else { + ++failures; + } + } + + if (oldest >= tallies->count || (tallies->records[oldest].status & TALLY_STATUS_VALID)) { + oldest = tallies->count; + + if ((records=realloc(tallies->records, (oldest+1) * sizeof (*tallies->records))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "Error allocating memory for tally records: %m"); + return PAM_BUF_ERR; + } + + ++tallies->count; + tallies->records = records; + } + + memset(&tallies->records[oldest], 0, sizeof (*tallies->records)); + + tallies->records[oldest].status = TALLY_STATUS_VALID; + if (pam_get_item(pamh, PAM_RHOST, &source) != PAM_SUCCESS || source == NULL) { + if (pam_get_item(pamh, PAM_TTY, &source) != PAM_SUCCESS || source == NULL) { + if (pam_get_item(pamh, PAM_SERVICE, &source) != PAM_SUCCESS || source == NULL) { + source = ""; + } + } + else { + tallies->records[oldest].status |= TALLY_STATUS_TTY; + } + } + else { + tallies->records[oldest].status |= TALLY_STATUS_RHOST; + } + + strncpy(tallies->records[oldest].source, source, sizeof(tallies->records[oldest].source)); + /* source does not have to be null terminated */ + + tallies->records[oldest].time = opts->now; + + ++failures; + + if (opts->deny && failures == opts->deny) { +#ifdef HAVE_LIBAUDIT + char buf[64]; + int audit_fd; + + audit_fd = audit_open(); + /* If there is an error & audit support is in the kernel report error */ + if ((audit_fd < 0) && !(errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT)) + return PAM_SYSTEM_ERR; + + snprintf(buf, sizeof(buf), "op=pam_faillock suid=%u ", opts->uid); + if (audit_log_user_message(audit_fd, AUDIT_ANOM_LOGIN_FAILURES, buf, + NULL, NULL, NULL, 1) <= 0) + pam_syslog(pamh, LOG_ERR, + "Error sending audit message: %m"); + + if (!opts->is_admin || (opts->flags & FAILLOCK_FLAG_DENY_ROOT)) { + if (audit_log_user_message(audit_fd, AUDIT_RESP_ACCT_LOCK, buf, + NULL, NULL, NULL, 1) <= 0) + pam_syslog(pamh, LOG_ERR, + "Error sending audit message: %m"); + } + close(audit_fd); +#endif + if (!(opts->flags & FAILLOCK_FLAG_NO_LOG_INFO) && + ((opts->flags & FAILLOCK_FLAG_DENY_ROOT) || (opts->uid != 0))) { + pam_syslog(pamh, LOG_INFO, + "Consecutive login failures for user %s account temporarily locked", + opts->user); + } + } + + if (update_tally(*fd, tallies) == 0) + return PAM_SUCCESS; + + return PAM_SYSTEM_ERR; +} + +static void +faillock_message(pam_handle_t *pamh, struct options *opts) +{ + int64_t left; + + if (!(opts->flags & FAILLOCK_FLAG_SILENT)) { + if (opts->is_admin) { + left = opts->latest_time + opts->root_unlock_time - opts->now; + } + else { + left = opts->latest_time + opts->unlock_time - opts->now; + } + + pam_info(pamh, _("The account is locked due to %u failed logins."), + (unsigned int)opts->failures); + if (left > 0) { + left = (left + 59)/60; /* minutes */ + +#if defined HAVE_DNGETTEXT && defined ENABLE_NLS + pam_info( + pamh, + dngettext(PACKAGE, + "(%d minute left to unlock)", + "(%d minutes left to unlock)", + (int)left), + (int)left); +#else + if (left == 1) + pam_info(pamh, _("(%d minute left to unlock)"), (int)left); + else + /* TRANSLATORS: only used if dngettext is not supported. */ + pam_info(pamh, _("(%d minutes left to unlock)"), (int)left); +#endif + } + } +} + +static void +tally_cleanup(struct tally_data *tallies, int fd) +{ + if (fd != -1) { + close(fd); + } + + free(tallies->records); +} + +static void +opts_cleanup(struct options *opts) +{ + free(opts->dir); + free(opts->admin_group); +} + +/*---------------------------------------------------------------------*/ + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + struct options opts; + int rv, fd = -1; + struct tally_data tallies; + + memset(&tallies, 0, sizeof(tallies)); + + rv = args_parse(pamh, argc, argv, flags, &opts); + if (rv != PAM_SUCCESS) + goto err; + + if (!(opts.flags & FAILLOCK_FLAG_NO_DELAY)) { + pam_fail_delay(pamh, 2000000); /* 2 sec delay on failure */ + } + + if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) { + goto err; + } + + if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) || + check_local_user (pamh, opts.user) != 0) { + switch (opts.action) { + case FAILLOCK_ACTION_PREAUTH: + rv = check_tally(pamh, &opts, &tallies, &fd); + if (rv == PAM_AUTH_ERR && !(opts.flags & FAILLOCK_FLAG_SILENT)) { + faillock_message(pamh, &opts); + } + break; + + case FAILLOCK_ACTION_AUTHSUCC: + rv = check_tally(pamh, &opts, &tallies, &fd); + if (rv == PAM_SUCCESS) { + reset_tally(pamh, &opts, &fd); + } + break; + + case FAILLOCK_ACTION_AUTHFAIL: + rv = check_tally(pamh, &opts, &tallies, &fd); + if (rv == PAM_SUCCESS) { + rv = PAM_IGNORE; /* this return value should be ignored */ + write_tally(pamh, &opts, &tallies, &fd); + } + break; + } + } + + tally_cleanup(&tallies, fd); + +err: + opts_cleanup(&opts); + + return rv; +} + +/*---------------------------------------------------------------------*/ + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +/*---------------------------------------------------------------------*/ + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + struct options opts; + int rv, fd = -1; + struct tally_data tallies; + + memset(&tallies, 0, sizeof(tallies)); + + rv = args_parse(pamh, argc, argv, flags, &opts); + + if (rv != PAM_SUCCESS) + goto err; + + opts.action = FAILLOCK_ACTION_AUTHSUCC; + + if ((rv=get_pam_user(pamh, &opts)) != PAM_SUCCESS) { + goto err; + } + + if (!(opts.flags & FAILLOCK_FLAG_LOCAL_ONLY) || + check_local_user (pamh, opts.user) != 0) { + check_tally(pamh, &opts, &tallies, &fd); /* for auditing */ + reset_tally(pamh, &opts, &fd); + } + + tally_cleanup(&tallies, fd); + +err: + opts_cleanup(&opts); + + return rv; +} + +/*-----------------------------------------------------------------------*/ diff --git a/modules/pam_faillock/tst-pam_faillock b/modules/pam_faillock/tst-pam_faillock new file mode 100755 index 0000000..ec454c2 --- /dev/null +++ b/modules/pam_faillock/tst-pam_faillock @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_faillock.so diff --git a/modules/pam_faillock/tst-pam_faillock-retval.c b/modules/pam_faillock/tst-pam_faillock-retval.c new file mode 100644 index 0000000..cbdc168 --- /dev/null +++ b/modules/pam_faillock/tst-pam_faillock-retval.c @@ -0,0 +1,120 @@ +/* + * Check pam_faillock return values. + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_faillock" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char config_filename[] = TEST_NAME ".conf"; +static const char user_name[] = "root"; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + ASSERT_NE(NULL, fp = fopen(config_filename, "w")); + ASSERT_LT(0, fprintf(fp, + "deny = 2\n" + "unlock_time = 5\n" + "root_unlock_time = 5\n")); + ASSERT_EQ(0, fclose(fp)); + + /* root has access */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "auth required %s/" LTDIR "%s.so authsucc even_deny_root dir=%s conf=%s\n" + "account required %s/" LTDIR "%s.so dir=%s\n" + "password required %s/" LTDIR "%s.so dir=%s\n" + "session required %s/" LTDIR "%s.so dir=%s\n", + cwd, + cwd, MODULE_NAME, cwd, config_filename, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd)); + + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + ASSERT_EQ(0, unlink(service_file)); + pamh = NULL; + + /* root tries to login 2 times without success*/ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth requisite %s/" LTDIR "%s.so dir=%s preauth even_deny_root conf=%s\n" + "auth [success=1 default=bad] %s/../pam_debug/" LTDIR "pam_debug.so auth=perm_denied cred=success\n" + "auth [default=die] %s/" LTDIR "%s.so dir=%s authfail even_deny_root conf=%s\n" + "auth sufficient %s/" LTDIR "%s.so dir=%s authsucc even_deny_root conf=%s\n", + cwd, MODULE_NAME, cwd, config_filename, + cwd, + cwd, MODULE_NAME, cwd, config_filename, + cwd, MODULE_NAME, cwd, config_filename)); + + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + ASSERT_EQ(0, unlink(service_file)); + + /* root is locked for 5 sec*/ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth requisite %s/" LTDIR "%s.so dir=%s preauth even_deny_root conf=%s\n" + "auth [success=1 default=bad] %s/../pam_debug/" LTDIR "pam_debug.so auth=success cred=success\n" + "auth [default=die] %s/" LTDIR "%s.so dir=%s authfail even_deny_root conf=%s\n" + "auth sufficient %s/" LTDIR "%s.so dir=%s authsucc even_deny_root conf=%s\n", + cwd, MODULE_NAME, cwd, config_filename, + cwd, + cwd, MODULE_NAME, cwd, config_filename, + cwd, MODULE_NAME, cwd, config_filename)); + + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_AUTH_ERR, pam_authenticate(pamh, 0)); + + /* waiting at least 5 sec --> login is working again*/ + sleep(6); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + ASSERT_EQ(0, unlink(service_file)); + pamh = NULL; + + ASSERT_EQ(0,unlink(user_name)); + ASSERT_EQ(0,unlink(config_filename)); + + return 0; +} diff --git a/modules/pam_filter/README.xml b/modules/pam_filter/README.xml new file mode 100644 index 0000000..ab05317 --- /dev/null +++ b/modules/pam_filter/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_filter.8.xml" xpointer='xpointer(id("pam_filter-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_filter/meson.build b/modules/pam_filter/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_filter/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_filter/pam_filter.8.xml b/modules/pam_filter/pam_filter.8.xml new file mode 100644 index 0000000..8015f41 --- /dev/null +++ b/modules/pam_filter/pam_filter.8.xml @@ -0,0 +1,258 @@ + + + + pam_filter + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_filter + PAM filter module + + + + + pam_filter.so + + debug + + + new_term + + + non_term + + + run1|run2 + + + filter + + + ... + + + + + + + DESCRIPTION + + + This module is intended to be a platform for providing access to all + of the input/output that passes between the user and the application. + It is only suitable for tty-based and (stdin/stdout) applications. + + + To function this module requires filters to be + installed on the system. + The single filter provided with the module simply transposes upper and + lower case letters in the input and output streams. (This can be very + annoying and is not kind to termcap based editors). + + + Each component of the module has the potential to invoke the + desired filter. The filter is always + + execv2 + with the privilege of the calling application + and not that of the user. For this reason it + cannot usually be killed by the user without closing their session. + + + + + + OPTIONS + + + + + + debug + + + + Print debug information. + + + + + + + new_term + + + + The default action of the filter is to set the + PAM_TTY item to indicate the + terminal that the user is using to connect to the + application. This argument indicates that the filter + should set PAM_TTY to the filtered + pseudo-terminal. + + + + + + + non_term + + + + don't try to set the PAM_TTY item. + + + + + + + runX + + + + In order that the module can invoke a filter it should + know when to invoke it. This argument is required to tell + the filter when to do this. + + + Permitted values for X are + 1 and 2. These + indicate the precise time that the filter is to be run. + To understand this concept it will be useful to have read + the + pam3 + manual page. + Basically, for each management group there are up to two ways + of calling the module's functions. + In the case of the authentication and + session components there are actually + two separate functions. For the case of authentication, these + functions are + + pam_authenticate3 + and + + pam_setcred3 + , here means run the + filter from the pam_authenticate function + and means run the filter from + pam_setcred. In the case of the + session modules, run1 implies + that the filter is invoked at the + + pam_open_session3 + stage, and run2 for + + pam_close_session3 + . + + + For the case of the account component. Either + run1 or run2 + may be used. + + + For the case of the password component, run1 + is used to indicate that the filter is run on the first + occasion of + + pam_chauthtok3 + (the PAM_PRELIM_CHECK + phase) and run2 is used to indicate + that the filter is run on the second occasion (the + PAM_UPDATE_AUTHTOK phase). + + + + + + + + filter + + + + The full pathname of the filter to be run and any command line + arguments that the filter might expect. + + + + + + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + + + PAM_SUCCESS + + + The new filter was set successfully. + + + + + + PAM_ABORT + + + Critical error, immediate abort. + + + + + + + + + + EXAMPLES + + Add the following line to /etc/pam.d/login to + see how to configure login to transpose upper and lower case letters + once the user has logged in: + + + session required pam_filter.so run1 /lib/security/pam_filter/upperLOWER + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_filter was written by Andrew G. Morgan <morgan@kernel.org>. + + + + \ No newline at end of file diff --git a/modules/pam_filter/pam_filter.c b/modules/pam_filter/pam_filter.c new file mode 100644 index 0000000..84d5930 --- /dev/null +++ b/modules/pam_filter/pam_filter.c @@ -0,0 +1,680 @@ +/* + * pam_filter module + * + * written by Andrew Morgan with much help from + * Richard Stevens' UNIX Network Programming book. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include "pam_filter.h" + +/* ------ some tokens used for convenience throughout this file ------- */ + +#define FILTER_DEBUG 01 +#define FILTER_RUN1 02 +#define FILTER_RUN2 04 +#define NEW_TERM 010 +#define NON_TERM 020 + +/* -------------------------------------------------------------------- */ + +/* log errors */ + +#include + +#define DEV_PTMX "/dev/ptmx" + +static int +master (void) +{ + int fd; + + if ((fd = open(DEV_PTMX, O_RDWR)) >= 0) { + return fd; + } + + return -1; +} + +static int process_args(pam_handle_t *pamh + , int argc, const char **argv, const char *type + , char ***evp, const char **filtername) +{ + int ctrl=0; + + while (argc-- > 0) { + if (strcmp("debug",*argv) == 0) { + ctrl |= FILTER_DEBUG; + } else if (strcmp("new_term",*argv) == 0) { + ctrl |= NEW_TERM; + } else if (strcmp("non_term",*argv) == 0) { + ctrl |= NON_TERM; + } else if (strcmp("run1",*argv) == 0) { + ctrl |= FILTER_RUN1; + if (argc <= 0) { + pam_syslog(pamh, LOG_ERR, "no run filter supplied"); + } else + break; + } else if (strcmp("run2",*argv) == 0) { + ctrl |= FILTER_RUN2; + if (argc <= 0) { + pam_syslog(pamh, LOG_ERR, "no run filter supplied"); + } else + break; + } else { + pam_syslog(pamh, LOG_ERR, "unrecognized option: %s", *argv); + } + ++argv; /* step along list */ + } + + if (argc < 0) { + /* there was no reference to a filter */ + *filtername = NULL; + *evp = NULL; + } else { + char **levp; + char *p; + const char *user = NULL; + const void *tmp; + int i, retval; + size_t size; + + *filtername = *++argv; + if (ctrl & FILTER_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "will run filter %s", *filtername); + } + + levp = malloc(5*sizeof(char *)); + if (levp == NULL) { + pam_syslog(pamh, LOG_CRIT, "no memory for environment of filter"); + return -1; + } + + /* the "ARGS" variable */ + +#define ARGS_NAME "ARGS=" +#define ARGS_OFFSET (sizeof(ARGS_NAME) - 1) + + size = sizeof(ARGS_NAME); + + for (i=0; i= 0) { + (void) ioctl(t, TIOCNOTTY, NULL); + close(t); + } + + /* make this process its own process leader */ + if (setsid() == -1) { + pam_syslog(pamh, LOG_ERR, + "child cannot become new session: %m"); + close(fd[0]); + return PAM_ABORT; + } + + /* grant slave terminal */ + if (grantpt (fd[0]) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot grant access to slave terminal"); + close(fd[0]); + return PAM_ABORT; + } + + /* unlock slave terminal */ + if (unlockpt (fd[0]) < 0) { + pam_syslog(pamh, LOG_ERR, "Cannot unlock slave terminal"); + close(fd[0]); + return PAM_ABORT; + } + + /* find slave's name */ + terminal = ptsname(fd[0]); /* returned value should not be freed */ + + if (terminal == NULL) { + pam_syslog(pamh, LOG_ERR, + "Cannot get the name of the slave terminal: %m"); + close(fd[0]); + return PAM_ABORT; + } + + fd[1] = open(terminal, O_RDWR); + close(fd[0]); /* process is the child -- uses line fd[1] */ + + if (fd[1] < 0) { + pam_syslog(pamh, LOG_ERR, + "cannot open slave terminal: %s: %m", terminal); + return PAM_ABORT; + } + + /* initialize the child's terminal to be the way the + parent's was before we set it into RAW mode */ + + if ( tcsetattr(fd[1], TCSANOW, &stored_mode) < 0 ) { + pam_syslog(pamh, LOG_ERR, + "cannot set slave terminal mode: %s: %m", terminal); + close(fd[1]); + return PAM_ABORT; + } + } else { + + /* nothing else to do for a simple stream socket */ + close(fd[0]); + + } + + /* re-assign the stdin/out to fd[1] <- (talks to filter). */ + + if ( dup2(fd[1],STDIN_FILENO) != STDIN_FILENO || + dup2(fd[1],STDOUT_FILENO) != STDOUT_FILENO || + dup2(fd[1],STDERR_FILENO) != STDERR_FILENO ) { + pam_syslog(pamh, LOG_ERR, + "unable to re-assign STDIN/OUT/ERR: %m"); + close(fd[1]); + return PAM_ABORT; + } + + /* now the user input is read from the parent/filter: forget fd */ + + close(fd[1]); + + /* make sure that file descriptors survive 'exec's */ + + if ( fcntl(STDIN_FILENO, F_SETFD, 0) || + fcntl(STDOUT_FILENO,F_SETFD, 0) || + fcntl(STDERR_FILENO,F_SETFD, 0) ) { + pam_syslog(pamh, LOG_ERR, + "unable to re-assign STDIN/OUT/ERR: %m"); + return PAM_ABORT; + } + + /* the current process is now apparently working with filtered + stdio/stdout/stderr --- success! */ + + return PAM_SUCCESS; + } + + if (!aterminal) + close(fd[1]); + + /* Clear out passwords... there is a security problem here in + * that this process never executes pam_end. Consequently, any + * other sensitive data in this process is *not* explicitly + * overwritten, before the process terminates */ + + (void) pam_set_item(pamh, PAM_AUTHTOK, NULL); + (void) pam_set_item(pamh, PAM_OLDAUTHTOK, NULL); + + /* fork a copy of process to run the actual filter executable */ + + if ( (child2 = fork()) < 0 ) { + + pam_syslog(pamh, LOG_ERR, "filter fork failed: %m"); + child2 = 0; + + } else if ( child2 == 0 ) { /* exec the child filter */ + + if ( dup2(fd[0],APPIN_FILENO) != APPIN_FILENO || + dup2(fd[0],APPOUT_FILENO) != APPOUT_FILENO || + dup2(fd[0],APPERR_FILENO) != APPERR_FILENO ) { + pam_syslog(pamh, LOG_ERR, + "unable to re-assign APPIN/OUT/ERR: %m"); + close(fd[0]); + _exit(1); + } + + /* make sure that file descriptors survive 'exec's */ + + if ( fcntl(APPIN_FILENO, F_SETFD, 0) == -1 || + fcntl(APPOUT_FILENO,F_SETFD, 0) == -1 || + fcntl(APPERR_FILENO,F_SETFD, 0) == -1 ) { + pam_syslog(pamh, LOG_ERR, + "unable to retain APPIN/OUT/ERR: %m"); + close(APPIN_FILENO); + close(APPOUT_FILENO); + close(APPERR_FILENO); + _exit(1); + } + + /* now the user input is read from the parent through filter */ + + execle(filtername, "", NULL, evp); + + /* getting to here is an error */ + + pam_syslog(pamh, LOG_ERR, "filter: %s: %m", filtername); + _exit(1); + + } else { /* wait for either of the two children to exit */ + + while (child && child2) { /* loop if there are two children */ + int lstatus=0; + int chid; + + chid = wait(&lstatus); + if (chid == child) { + + if (WIFEXITED(lstatus)) { /* exited ? */ + status = WEXITSTATUS(lstatus); + } else if (WIFSIGNALED(lstatus)) { /* killed ? */ + status = -1; + } else + continue; /* just stopped etc.. */ + child = 0; /* the child has exited */ + + } else if (chid == child2) { + /* + * if the filter has exited, let the child die + * naturally below + */ + if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus)) + child2 = 0; + } else { + + pam_syslog(pamh, LOG_ERR, + "programming error " + "in file %s at line %d", + chid, lstatus, __FILE__, __LINE__); + child = child2 = 0; + status = -1; + + } + } + } + + close(fd[0]); + + /* if there is something running, wait for it to exit */ + + while (child || child2) { + int lstatus=0; + int chid; + + chid = wait(&lstatus); + + if (child && chid == child) { + + if (WIFEXITED(lstatus)) { /* exited ? */ + status = WEXITSTATUS(lstatus); + } else if (WIFSIGNALED(lstatus)) { /* killed ? */ + status = -1; + } else + continue; /* just stopped etc.. */ + child = 0; /* the child has exited */ + + } else if (child2 && chid == child2) { + + if (WIFEXITED(lstatus) || WIFSIGNALED(lstatus)) + child2 = 0; + + } else { + + pam_syslog(pamh, LOG_ERR, + "programming error " + "in file %s at line %d", + chid, lstatus, __FILE__, __LINE__); + child = child2 = 0; + status = -1; + + } + } + + if (aterminal) { + /* reset to initial terminal mode */ + (void) tcsetattr(STDIN_FILENO, TCSANOW, &stored_mode); + } + + if (ctrl & FILTER_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "parent process exited"); /* clock off */ + } + + /* quit the parent process, returning the child's exit status */ + + exit(status); + return status; /* never reached, to make gcc happy */ +} + +static int set_the_terminal(pam_handle_t *pamh) +{ + const void *tty; + + if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS + || tty == NULL) { + tty = ttyname(STDIN_FILENO); + if (tty == NULL) { + pam_syslog(pamh, LOG_ERR, "couldn't get the tty name"); + return PAM_ABORT; + } + if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "couldn't set tty name"); + return PAM_ABORT; + } + } + return PAM_SUCCESS; +} + +static int need_a_filter(pam_handle_t *pamh + , int flags, int argc, const char **argv + , const char *name, int which_run) +{ + int ctrl; + char **evp; + const char *filterfile; + int retval; + + ctrl = process_args(pamh, argc, argv, name, &evp, &filterfile); + if (ctrl == -1) { + return PAM_AUTHINFO_UNAVAIL; + } + + /* set the tty to the old or the new one? */ + + if (!(ctrl & NON_TERM) && !(ctrl & NEW_TERM)) { + retval = set_the_terminal(pamh); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "tried and failed to set PAM_TTY"); + } + } else { + retval = PAM_SUCCESS; /* nothing to do which is always a success */ + } + + if (retval == PAM_SUCCESS && (ctrl & which_run)) { + retval = set_filter(pamh, flags, ctrl, evp, filterfile); + } + + if (retval == PAM_SUCCESS + && !(ctrl & NON_TERM) && (ctrl & NEW_TERM)) { + retval = set_the_terminal(pamh); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, + "tried and failed to set new terminal as PAM_TTY"); + } + } + + free_evp(evp); + + if (ctrl & FILTER_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "filter/%s, returning %d", name, retval); + pam_syslog(pamh, LOG_DEBUG, "[%s]", pam_strerror(pamh, retval)); + } + + return retval; +} + +/* ----------------- public functions ---------------- */ + +/* + * here are the advertised access points ... + */ + +/* ------------------ authentication ----------------- */ + +int pam_sm_authenticate(pam_handle_t *pamh, + int flags, int argc, const char **argv) +{ + return need_a_filter(pamh, flags, argc, argv + , "authenticate", FILTER_RUN1); +} + +int pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + return need_a_filter(pamh, flags, argc, argv, "setcred", FILTER_RUN2); +} + +/* --------------- account management ---------------- */ + +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return need_a_filter(pamh, flags, argc, argv + , "setcred", FILTER_RUN1|FILTER_RUN2 ); +} + +/* --------------- session management ---------------- */ + +int pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + return need_a_filter(pamh, flags, argc, argv + , "open_session", FILTER_RUN1); +} + +int pam_sm_close_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + return need_a_filter(pamh, flags, argc, argv + , "close_session", FILTER_RUN2); +} + +/* --------- updating authentication tokens --------- */ + + +int pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int runN; + + if (flags & PAM_PRELIM_CHECK) + runN = FILTER_RUN1; + else if (flags & PAM_UPDATE_AUTHTOK) + runN = FILTER_RUN2; + else { + pam_syslog(pamh, LOG_ERR, "unknown flags for chauthtok (0x%X)", flags); + return PAM_TRY_AGAIN; + } + + return need_a_filter(pamh, flags, argc, argv, "chauthtok", runN); +} diff --git a/modules/pam_filter/pam_filter.h b/modules/pam_filter/pam_filter.h new file mode 100644 index 0000000..630198e --- /dev/null +++ b/modules/pam_filter/pam_filter.h @@ -0,0 +1,32 @@ +/* + * $Id$ + * + * this file is associated with the Linux-PAM filter module. + * it was written by Andrew G. Morgan + * + */ + +#ifndef PAM_FILTER_H +#define PAM_FILTER_H + +#include + +/* + * this will fail if there is some problem with these file descriptors + * being allocated by the pam_filter Linux-PAM module. The numbers + * here are thought safe, but the filter developer should use the + * macros, as these numbers are subject to change. + * + * The APPXXX_FILENO file descriptors are the STDIN/OUT/ERR_FILENO of the + * application. The filter uses the STDIN/OUT/ERR_FILENO's to converse + * with the user, passes (modified) user input to the application via + * APPIN_FILENO, and receives application output from APPOUT_FILENO/ERR. + */ + +#define APPIN_FILENO 3 /* write here to give application input */ +#define APPOUT_FILENO 4 /* read here to get application output */ +#define APPERR_FILENO 5 /* read here to get application errors */ + +#define APPTOP_FILE 6 /* used by select */ + +#endif diff --git a/modules/pam_filter/tst-pam_filter b/modules/pam_filter/tst-pam_filter new file mode 100755 index 0000000..56a5d08 --- /dev/null +++ b/modules/pam_filter/tst-pam_filter @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_filter.so diff --git a/modules/pam_filter/upperLOWER/meson.build b/modules/pam_filter/upperLOWER/meson.build new file mode 100644 index 0000000..8992fff --- /dev/null +++ b/modules/pam_filter/upperLOWER/meson.build @@ -0,0 +1,9 @@ +executable( + 'upperLOWER', + sources: ['upperLOWER.c'], + include_directories: ['..'], + link_args: exe_link_args, + dependencies: [libpam_internal_dep, libpam_dep], + install: true, + install_dir: securedir / 'pam_filter', +) diff --git a/modules/pam_filter/upperLOWER/upperLOWER.c b/modules/pam_filter/upperLOWER/upperLOWER.c new file mode 100644 index 0000000..700e0ed --- /dev/null +++ b/modules/pam_filter/upperLOWER/upperLOWER.c @@ -0,0 +1,141 @@ +/* + * This is a sample filter program, for use with pam_filter (a module + * provided with Linux-PAM). This filter simply transposes upper and + * lower case letters, it is intended for demonstration purposes and + * it serves no purpose other than to annoy the user... + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "pam_filter.h" +#include + +/* ---------------------------------------------------------------- */ + +static void do_transpose(char *buffer,int len) +{ + int i; + for (i=0; i %s\r\n",environ[i]); + } + fprintf(stderr,"]: end\r\n"); + } +#endif + + if (argc != 1) { +#ifdef DEBUG + fprintf(stderr,"filter invoked as conventional executable\n"); +#else + syslog(LOG_ERR, "filter invoked as conventional executable"); +#endif + exit(1); + } + + before_user = before_app = do_transpose; /* assign filter functions */ + + /* enter a loop that deals with the input and output of the + user.. passing it to and from the application */ + + FD_ZERO(&readers); /* initialize reading mask */ + + for (;;) { + + FD_SET(APPOUT_FILENO, &readers); /* wake for output */ + FD_SET(APPERR_FILENO, &readers); /* wake for error */ + FD_SET(STDIN_FILENO, &readers); /* wake for input */ + + if ( select(APPTOP_FILE,&readers,NULL,NULL,NULL) < 0 ) { +#ifdef DEBUG + fprintf(stderr,"select failed\n"); +#else + syslog(LOG_WARNING,"select failed"); +#endif + break; + } + + /* application errors */ + + if ( FD_ISSET(APPERR_FILENO,&readers) ) { + int got = read(APPERR_FILENO, buffer, BUFSIZ); + if (got <= 0) { + break; + } else { + /* translate to give to real terminal */ + if (before_user != NULL) + before_user(buffer, got); + if (pam_modutil_write(STDERR_FILENO, buffer, got) != got ) { + syslog(LOG_WARNING,"couldn't write %d bytes?!",got); + break; + } + } + } else if ( FD_ISSET(APPOUT_FILENO,&readers) ) { /* app output */ + int got = read(APPOUT_FILENO, buffer, BUFSIZ); + if (got <= 0) { + break; + } else { + /* translate to give to real terminal */ + if (before_user != NULL) + before_user(buffer, got); + if (pam_modutil_write(STDOUT_FILENO, buffer, got) != got ) { + syslog(LOG_WARNING,"couldn't write %d bytes!?",got); + break; + } + } + } + + if ( FD_ISSET(STDIN_FILENO, &readers) ) { /* user input */ + int got = read(STDIN_FILENO, buffer, BUFSIZ); + if (got < 0) { + syslog(LOG_WARNING,"user input junked"); + break; + } else if (got) { + /* translate to give to application */ + if (before_app != NULL) + before_app(buffer, got); + if (pam_modutil_write(APPIN_FILENO, buffer, got) != got ) { + syslog(LOG_WARNING,"couldn't pass %d bytes!?",got); + break; + } + } else { + /* nothing received -- an error? */ + syslog(LOG_WARNING,"user input null?"); + break; + } + } + } + + exit(0); +} diff --git a/modules/pam_ftp/README.xml b/modules/pam_ftp/README.xml new file mode 100644 index 0000000..f4606be --- /dev/null +++ b/modules/pam_ftp/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_ftp.8.xml" xpointer='xpointer(id("pam_ftp-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_ftp/meson.build b/modules/pam_ftp/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_ftp/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_ftp/pam_ftp.8.xml b/modules/pam_ftp/pam_ftp.8.xml new file mode 100644 index 0000000..03f3678 --- /dev/null +++ b/modules/pam_ftp/pam_ftp.8.xml @@ -0,0 +1,180 @@ + + + + pam_ftp + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_ftp + PAM module for anonymous access module + + + + + pam_ftp.so + + debug + + + ignore + + + users=XXX,YYY, + + + + + + + DESCRIPTION + + + pam_ftp is a PAM module which provides a pluggable + anonymous ftp mode of access. + + + This module intercepts the user's name and password. If the name is + ftp or anonymous, the + user's password is broken up at the @ delimiter + into a PAM_RUSER and a + PAM_RHOST part; these pam-items being set + accordingly. The username (PAM_USER) is set + to ftp. In this case the module succeeds. + Alternatively, the module sets the PAM_AUTHTOK + item with the entered password and fails. + + + This module is not safe and easily spoofable. + + + + + + OPTIONS + + + + + + debug + + + + Print debug information. + + + + + + + ignore + + + + Pay no attention to the email address of the user + (if supplied). + + + + + + + ftp=XXX,YYY,... + + + + Instead of ftp or + anonymous, provide anonymous login + to the comma separated list of users: + . + Should the applicant enter + one of these usernames the returned username is set to + the first in the list: XXX. + + + + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + + + PAM_SUCCESS + + + The authentication was successful. + + + + + + PAM_USER_UNKNOWN + + + User not known. + + + + + + + + + + EXAMPLES + + Add the following line to /etc/pam.d/ftpd to + handle ftp style anonymous login: + +# +# ftpd; add ftp-specifics. These lines enable anonymous ftp over +# standard UN*X access (the listfile entry blocks access to +# users listed in /etc/ftpusers) +# +auth sufficient pam_ftp.so +auth required pam_unix.so use_first_pass +auth required pam_listfile.so \ + onerr=succeed item=user sense=deny file=/etc/ftpusers + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_ftp was written by Andrew G. Morgan <morgan@kernel.org>. + + + + \ No newline at end of file diff --git a/modules/pam_ftp/pam_ftp.c b/modules/pam_ftp/pam_ftp.c new file mode 100644 index 0000000..cfb0f17 --- /dev/null +++ b/modules/pam_ftp/pam_ftp.c @@ -0,0 +1,215 @@ +/* + * pam_ftp module + * + * Written by Andrew Morgan 1996/3/11 + */ + +#define PLEASE_ENTER_PASSWORD "Password required for %s." +#define GUEST_LOGIN_PROMPT "Guest login ok, " \ +"send your complete e-mail address as password." + +/* the following is a password that "can't be correct" */ +#define BLOCK_PASSWORD "\177BAD PASSWPRD\177" + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "pam_inline.h" + +/* argument parsing */ + +#define PAM_DEBUG_ARG 01 +#define PAM_IGNORE_EMAIL 02 +#define PAM_NO_ANON 04 + +static int +_pam_parse(pam_handle_t *pamh, int argc, const char **argv, const char **users) +{ + int ctrl=0; + + /* step through arguments */ + for (ctrl=0; argc-- > 0; ++argv) { + const char *str; + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_DEBUG_ARG; + else if (!strcmp(*argv,"ignore")) + ctrl |= PAM_IGNORE_EMAIL; + else if ((str = pam_str_skip_prefix(*argv, "users=")) != NULL) + *users = str; + else + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + + return ctrl; +} + +/* + * check if name is in list or default list. place users name in *_user + * return 1 if listed 0 if not. + */ + +static int lookup(const char *name, const char *list, char **_user) +{ + int anon = 0; + + if (list && *list) { + const char *l; + char *list_copy, *x; + char *sptr = NULL; + + list_copy = strdup(list); + x = list_copy; + while (list_copy && (l = strtok_r(x, ",", &sptr))) { + x = NULL; + if (!strcmp(name, l)) { + *_user = list_copy; + anon = 1; + break; + } + } + if (*_user != list_copy) { + free(list_copy); + } + } else { +#define MAX_L 2 + static const char *const l[MAX_L] = { "ftp", "anonymous" }; + int i; + + for (i=0; i + + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_group.8.xml" xpointer='xpointer(id("pam_group-name")/*)'/> + + + + +
+ +
+ +
+ +
+ + \ No newline at end of file diff --git a/modules/pam_group/group.conf b/modules/pam_group/group.conf new file mode 100644 index 0000000..7c07a26 --- /dev/null +++ b/modules/pam_group/group.conf @@ -0,0 +1,106 @@ +# +# This is the configuration file for the pam_group module. +# + +# +# *** Please note that giving group membership on a session basis is +# *** NOT inherently secure. If a user can create an executable that +# *** is setgid a group that they are infrequently given membership +# *** of, they can basically obtain group membership any time they +# *** like. Example: games are allowed between the hours of 6pm and 6am +# *** user joe logs in at 7pm writes a small C-program toplay.c that +# *** invokes their favorite shell, compiles it and does +# *** "chgrp play toplay; chmod g+s toplay". They are basically able +# *** to play games any time... You have been warned. AGM +# + +# +# The syntax of the lines is as follows: +# +# services;ttys;users;times;groups +# +# white space is ignored and lines maybe extended with '\\n' (escaped +# newlines). From reading these comments, it is clear that +# text following a '#' is ignored to the end of the line. +# +# the combination of individual users/terminals etc is a logic list +# namely individual tokens that are optionally prefixed with '!' (logical +# not) and separated with '&' (logical and) and '|' (logical or). +# +# services +# is a logic list of PAM service names that the rule applies to. +# +# ttys +# is a logic list of terminal names that this rule applies to. +# +# users +# is a logic list of users or a netgroup of users to whom this +# rule applies. +# +# NB. For these items the simple wildcard '*' may be used only once. +# With netgroups no wildcards or logic operators are allowed. +# +# times +# It is used to indicate "when" these groups are to be given to the +# user. The format here is a logic list of day/time-range +# entries the days are specified by a sequence of two character +# entries, MoTuSa for example is Monday Tuesday and Saturday. Note +# that repeated days are unset MoMo = no day, and MoWk = all weekdays +# bar Monday. The two character combinations accepted are +# +# Mo Tu We Th Fr Sa Su Wk Wd Al +# +# the last two being week-end days and all 7 days of the week +# respectively. As a final example, AlFr means all days except Friday. +# +# Each day/time-range can be prefixed with a '!' to indicate "anything +# but" +# +# The time-range part is two 24-hour times HHMM separated by a hyphen +# indicating the start and finish time (if the finish time is smaller +# than the start time it is deemed to apply on the following day). +# +# groups +# The (comma or space separated) list of groups that the user +# inherits membership of. These groups are added if the previous +# fields are satisfied by the user's request +# +# For a rule to be active, ALL of service+ttys+users must be satisfied +# by the applying process. +# + +# +# Note, to get this to work as it is currently typed you need +# +# 1. to run an application as root +# 2. add the following groups to the /etc/group file: +# floppy, play, sound +# + +# +# Here is a simple example: running 'xsh' on tty* (any ttyXXX device), +# the user 'us' is given access to the floppy (through membership of +# the floppy group) +# + +#xsh;tty*&!ttyp*;us;Al0000-2400;floppy + +# +# another example: running 'xsh' on tty* (any ttyXXX device), +# the user 'sword' is given access to games (through membership of +# the sound and play group) after work hours. +# + +#xsh; tty* ;sword;!Wk0900-1800;sound, play +#xsh; tty* ;*;Al0900-1800;floppy + +# +# yet another example: any member of the group 'admin' running +# 'xsh' on tty*, is granted access (at any time) to the group 'plugdev' +# + +#xsh; tty* ;%admin;Al0000-2400;plugdev + +# +# End of group.conf file +# diff --git a/modules/pam_group/group.conf.5.xml b/modules/pam_group/group.conf.5.xml new file mode 100644 index 0000000..a8875b3 --- /dev/null +++ b/modules/pam_group/group.conf.5.xml @@ -0,0 +1,144 @@ + + + + group.conf + 5 + Linux-PAM + Linux-PAM Manual + + + + group.conf + configuration file for the pam_group module + + + + DESCRIPTION + + + The pam_group PAM module does not authenticate the user, but instead + it grants group memberships (in the credential setting phase of the + authentication module) to the user. Such memberships are based on the + service they are applying for. + + + For this module to function correctly there must be a correctly + formatted /etc/security/group.conf file present. + White spaces are ignored and lines maybe extended with '\' (escaped + newlines). Text following a '#' is ignored to the end of the line. + + + + The syntax of the lines is as follows: + + + + services;ttys;users;times;groups + + + + + The first field, the services field, is a logic list + of PAM service names that the rule applies to. + + + + The second field, the tty + field, is a logic list of terminal names that this rule applies to. + + + + The third field, the users + field, is a logic list of users, or a UNIX group, or a netgroup of + users to whom this rule applies. Group names are preceded by a '%' + symbol, while netgroup names are preceded by a '@' symbol. + + + + A logic list namely means individual tokens that are optionally prefixed + with '!' (logical not) and separated with '&' (logical and) and '|' + (logical or). + + + + For these items the simple wildcard '*' may be used only once. + With UNIX groups or netgroups no wildcards or logic operators + are allowed. + + + + The times field is used to indicate "when" + these groups are to be given to the user. The format here is a logic + list of day/time-range entries. The days are specified by a sequence of + two character entries, MoTuSa for example is Monday Tuesday and Saturday. + Note that repeated days are unset MoMo = no day, and MoWk = all weekdays + bar Monday. The two character combinations accepted are Mo Tu We Th Fr Sa + Su Wk Wd Al, the last two being week-end days and all 7 days of the week + respectively. As a final example, AlFr means all days except Friday. + + + Each day/time-range can be prefixed with a '!' to indicate "anything but". + The time-range part is two 24-hour times HHMM, separated by a hyphen, + indicating the start and finish time (if the finish time is smaller + than the start time it is deemed to apply on the following day). + + + + The groups field is a comma or space + separated list of groups that the user inherits membership of. These + groups are added if the previous fields are satisfied by the user's request. + + + + For a rule to be active, ALL of service+ttys+users must be satisfied + by the applying process. + + + + + EXAMPLES + + These are some example lines which might be specified in + /etc/security/group.conf. + + + + Running 'xsh' on tty* (any ttyXXX device), the user 'us' is given access + to the floppy (through membership of the floppy group) + + xsh;tty*&!ttyp*;us;Al0000-2400;floppy + + + Running 'xsh' on tty* (any ttyXXX device), the users 'sword', 'pike' and + 'shield' are given access to games (through membership of the floppy group) after work hours. + + +xsh; tty* ;sword|pike|shield;!Wk0900-1800;games, sound +xsh; tty* ;*;Al0900-1800;floppy + + + Any member of the group 'admin' running 'xsh' on tty*, + is granted access (at any time) to the group 'plugdev' + + +xsh; tty* ;%admin;Al0000-2400;plugdev + + + + + + SEE ALSO + + pam_group8, + pam.d5, + pam8 + + + + + AUTHOR + + pam_group was written by Andrew G. Morgan <morgan@kernel.org>. + + + \ No newline at end of file diff --git a/modules/pam_group/meson.build b/modules/pam_group/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_group/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_group/pam_group.8.xml b/modules/pam_group/pam_group.8.xml new file mode 100644 index 0000000..695a7ba --- /dev/null +++ b/modules/pam_group/pam_group.8.xml @@ -0,0 +1,163 @@ + + + + pam_group + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_group + + PAM module for group access + + + + + + + + pam_group.so + + + + + + DESCRIPTION + + The pam_group PAM module does not authenticate the user, but instead + it grants group memberships (in the credential setting phase of the + authentication module) to the user. Such memberships are based on the + service they are applying for. + + + By default rules for group memberships are taken from config file + /etc/security/group.conf. + + + If /etc/security/group.conf does not exist, + %vendordir%/security/group.conf is used. + + + This module's usefulness relies on the file-systems + accessible to the user. The point being that once granted the + membership of a group, the user may attempt to create a + setgid binary with a restricted group ownership. + Later, when the user is not given membership to this group, they can + recover group membership with the precompiled binary. The reason that + the file-systems that the user has access to are so significant, is the + fact that when a system is mounted nosuid the user + is unable to create or execute such a binary file. For this module to + provide any level of security, all file-systems that the user has write + access to should be mounted nosuid. + + + The pam_group module functions in parallel with the + /etc/group file. If the user is granted any groups + based on the behavior of this module, they are granted + in addition to those entries + /etc/group (or equivalent). + + + + + OPTIONS + This module does not recognise any options. + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + group membership was granted. + + + + + PAM_ABORT + + + Not all relevant data could be gotten. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_CRED_ERR + + + Group membership was not granted. + + + + + PAM_IGNORE + + + pam_sm_authenticate was called which does nothing. + + + + + PAM_USER_UNKNOWN + + + The user is not known to the system. + + + + + + + + FILES + + + /etc/security/group.conf + + Default configuration file + + + + + + + SEE ALSO + + + group.conf5 + , + + pam.d5 + , + + pam8 + . + + + + + AUTHORS + + pam_group was written by Andrew G. Morgan <morgan@kernel.org>. + + + \ No newline at end of file diff --git a/modules/pam_group/pam_group.c b/modules/pam_group/pam_group.c new file mode 100644 index 0000000..21c04d7 --- /dev/null +++ b/modules/pam_group/pam_group.c @@ -0,0 +1,833 @@ +/* + * pam_group module + * + * Written by Andrew Morgan 1996/7/6 + * Field parsing rewritten by Tomas Mraz + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define PAM_GROUP_CONF SCONFIG_DIR "/group.conf" +#ifdef VENDOR_SCONFIG_DIR +# define VENDOR_PAM_GROUP_CONF VENDOR_SCONFIG_DIR "/group.conf" +#endif +#define PAM_GROUP_BUFLEN 1000 +#define FIELD_SEPARATOR ';' /* this is new as of .02 */ + +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + +typedef enum { AND, OR } operator; + +#include +#include +#include +#include +#include "pam_inline.h" + +/* --- static functions for checking whether the user should be let in --- */ + +static char * +shift_buf(char *mem, int from) +{ + char *start = mem; + while ((*mem = mem[from]) != '\0') + ++mem; + pam_overwrite_n(mem, PAM_GROUP_BUFLEN - (mem - start)); + return mem; +} + +static void +trim_spaces(char *buf, char *from) +{ + while (from > buf) { + --from; + if (*from == ' ') + *from = '\0'; + else + break; + } +} + +#define STATE_NL 0 /* new line starting */ +#define STATE_COMMENT 1 /* inside comment */ +#define STATE_FIELD 2 /* field following */ +#define STATE_EOF 3 /* end of file or error */ + +static int +read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state, + const char *conf_filename) +{ + char *to; + char *src; + int i; + char c; + int onspace; + + /* is buf set ? */ + if (! *buf) { + *buf = calloc(1, PAM_GROUP_BUFLEN+1); + if (! *buf) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + D(("no memory")); + *state = STATE_EOF; + return -1; + } + *from = 0; + *state = STATE_NL; + fd = open(conf_filename, O_RDONLY); + if (fd < 0) { + pam_syslog(pamh, LOG_ERR, "error opening %s: %m", conf_filename); + _pam_drop(*buf); + *state = STATE_EOF; + return -1; + } + } + + if (*from > 0) + to = shift_buf(*buf, *from); + else + to = *buf; + + while (fd != -1 && to - *buf < PAM_GROUP_BUFLEN) { + i = pam_modutil_read(fd, to, PAM_GROUP_BUFLEN - (to - *buf)); + if (i < 0) { + pam_syslog(pamh, LOG_ERR, "error reading %s: %m", conf_filename); + close(fd); + pam_overwrite_n(*buf, PAM_GROUP_BUFLEN); + _pam_drop(*buf); + *state = STATE_EOF; + return -1; + } else if (!i) { + close(fd); + fd = -1; /* end of file reached */ + } + + to += i; + } + + if (to == *buf) { + /* nothing previously in buf, nothing read */ + _pam_drop(*buf); + *state = STATE_EOF; + return -1; + } + + pam_overwrite_n(to, PAM_GROUP_BUFLEN - (to - *buf)); + + to = *buf; + onspace = 1; /* delete any leading spaces */ + + for (src = to; (c=*src) != '\0'; ++src) { + if (*state == STATE_COMMENT && c != '\n') { + continue; + } + + switch (c) { + case '\n': + *state = STATE_NL; + *to = '\0'; + *from = (src - *buf) + 1; + trim_spaces(*buf, to); + return fd; + + case '\t': + case ' ': + if (!onspace) { + onspace = 1; + *to++ = ' '; + } + break; + + case '!': + onspace = 1; /* ignore following spaces */ + *to++ = '!'; + break; + + case '#': + *state = STATE_COMMENT; + break; + + case FIELD_SEPARATOR: + *state = STATE_FIELD; + *to = '\0'; + *from = (src - *buf) + 1; + trim_spaces(*buf, to); + return fd; + + case '\\': + if (src[1] == '\n') { + ++src; /* skip it */ + break; + } + /* fallthrough */ + default: + *to++ = c; + onspace = 0; + } + if (src > to) + *src = '\0'; /* clearing */ + } + + if (*state != STATE_COMMENT) { + *state = STATE_COMMENT; + pam_syslog(pamh, LOG_ERR, "field too long - ignored"); + **buf = '\0'; + } else { + *to = '\0'; + trim_spaces(*buf, to); + } + + *from = 0; + return fd; +} + +/* read a member from a field */ + +static int logic_member(const char *string, int *at) +{ + int c,to; + int done=0; + int token=0; + + to=*at; + do { + c = string[to++]; + + switch (c) { + + case '\0': + --to; + done = 1; + break; + + case '&': + case '|': + case '!': + if (token) { + --to; + } + done = 1; + break; + + default: + if (isalpha((unsigned char)c) || c == '*' || isdigit((unsigned char)c) || c == '_' + || c == '-' || c == '.' || c == '/' || c == ':') { + token = 1; + } else if (token) { + --to; + done = 1; + } else { + ++*at; + } + } + } while (!done); + + return to - *at; +} + +typedef enum { VAL, OP } expect; + +static int +logic_field (const pam_handle_t *pamh, const void *me, + const char *x, int rule, + int (*agrees)(const pam_handle_t *pamh, const void *, + const char *, int, int)) +{ + int left=FALSE, right, not=FALSE; + operator oper=OR; + int at=0, l; + expect next=VAL; + + while ((l = logic_member(x,&at))) { + int c = x[at]; + + if (next == VAL) { + if (c == '!') + not = !not; + else if (isalpha((unsigned char)c) || c == '*' || isdigit((unsigned char)c) || c == '_' + || c == '-' || c == '.' || c == '/' || c == ':') { + right = not ^ agrees(pamh, me, x+at, l, rule); + if (oper == AND) + left &= right; + else + left |= right; + next = OP; + } else { + pam_syslog(pamh, LOG_ERR, + "garbled syntax; expected name (rule #%d)", + rule); + return FALSE; + } + } else { /* OP */ + switch (c) { + case '&': + oper = AND; + break; + case '|': + oper = OR; + break; + default: + pam_syslog(pamh, LOG_ERR, + "garbled syntax; expected & or | (rule #%d)", + rule); + D(("%c at %d",c,at)); + return FALSE; + } + next = VAL; + not = FALSE; + } + at += l; + } + + return left; +} + +static int +is_same (const pam_handle_t *pamh UNUSED, + const void *A, const char *b, int len, int rule UNUSED) +{ + int i; + const char *a; + + a = A; + for (i=0; len > 0; ++i, --len) { + if (b[i] != a[i]) { + if (b[i++] == '*') { + return (!--len || !strncmp(b+i,a+strlen(a)-len,len)); + } else + return FALSE; + } + } + + /* Ok, we know that b is a substring from A and does not contain + wildcards, but now the length of both strings must be the same, + too. In this case it means, a[i] has to be the end of the string. */ + if (a[i] != '\0') + return FALSE; + + return ( !len ); +} + +typedef struct { + int day; /* array of 7 bits, one set for today */ + int minute; /* integer, hour*100+minute for now */ +} TIME; + +static struct day { + const char *d; + int bit; +} const days[11] = { + { "su", 01 }, + { "mo", 02 }, + { "tu", 04 }, + { "we", 010 }, + { "th", 020 }, + { "fr", 040 }, + { "sa", 0100 }, + { "wk", 076 }, + { "wd", 0101 }, + { "al", 0177 }, + { NULL, 0 } +}; + +static TIME time_now(void) +{ + struct tm *local; + time_t the_time; + TIME this; + + the_time = time((time_t *)0); /* get the current time */ + local = localtime(&the_time); + this.day = days[local->tm_wday].bit; + this.minute = local->tm_hour*100 + local->tm_min; + + D(("day: 0%o, time: %.4d", this.day, this.minute)); + return this; +} + +/* take the current date and see if the range "date" passes it */ +static int +check_time (const pam_handle_t *pamh, const void *AT, + const char *times, int len, int rule) +{ + int not,pass; + int marked_day, time_start, time_end; + const TIME *at; + int i,j=0; + + at = AT; + D(("checking: 0%o/%.4d vs. %s", at->day, at->minute, times)); + + if (times == NULL) { + /* this should not happen */ + pam_syslog(pamh, LOG_CRIT, "internal error in file %s at line %d", + __FILE__, __LINE__); + return FALSE; + } + + if (times[j] == '!') { + ++j; + not = TRUE; + } else { + not = FALSE; + } + + for (marked_day = 0; len > 0 && isalpha((unsigned char)times[j]); --len) { + int this_day=-1; + + D(("%c%c ?", times[j], times[j+1])); + for (i=0; days[i].d != NULL; ++i) { + if (tolower((unsigned char)times[j]) == days[i].d[0] + && tolower((unsigned char)times[j+1]) == days[i].d[1] ) { + this_day = days[i].bit; + break; + } + } + j += 2; + if (this_day == -1) { + pam_syslog(pamh, LOG_ERR, "bad day specified (rule #%d)", rule); + return FALSE; + } + marked_day ^= this_day; + } + if (marked_day == 0) { + pam_syslog(pamh, LOG_ERR, "no day specified"); + return FALSE; + } + D(("day range = 0%o", marked_day)); + + time_start = 0; + for (i=0; len > 0 && i < 4 && isdigit((unsigned char)times[i+j]); ++i, --len) { + time_start *= 10; + time_start += times[i+j]-'0'; /* is this portable? */ + } + j += i; + + if (times[j] == '-') { + time_end = 0; + for (i=1; len > 0 && i < 5 && isdigit((unsigned char)times[i+j]); ++i, --len) { + time_end *= 10; + time_end += times[i+j]-'0'; /* is this portable? */ + } + j += i; + } else + time_end = -1; + + D(("i=%d, time_end=%d, times[j]='%c'", i, time_end, times[j])); + if (i != 5 || time_end == -1) { + pam_syslog(pamh, LOG_ERR, "no/bad times specified (rule #%d)", rule); + return TRUE; + } + D(("times(%d to %d)", time_start,time_end)); + D(("marked_day = 0%o", marked_day)); + + /* compare with the actual time now */ + + pass = FALSE; + if (time_start < time_end) { /* start < end ? --> same day */ + if ((at->day & marked_day) && (at->minute >= time_start) + && (at->minute < time_end)) { + D(("time is listed")); + pass = TRUE; + } + } else { /* spans two days */ + if ((at->day & marked_day) && (at->minute >= time_start)) { + D(("caught on first day")); + pass = TRUE; + } else { + marked_day <<= 1; + marked_day |= (marked_day & 0200) ? 1:0; + D(("next day = 0%o", marked_day)); + if ((at->day & marked_day) && (at->minute <= time_end)) { + D(("caught on second day")); + pass = TRUE; + } + } + } + + return (not ^ pass); +} + +static int find_member(const char *string, int *at) +{ + int c,to; + int done=0; + int token=0; + + to=*at; + do { + c = string[to++]; + + switch (c) { + + case '\0': + --to; + done = 1; + break; + + case '&': + case '|': + case '!': + if (token) { + --to; + } + done = 1; + break; + + default: + if (isalpha((unsigned char)c) || isdigit((unsigned char)c) || c == '_' || c == '*' + || c == '-') { + token = 1; + } else if (token) { + --to; + done = 1; + } else { + ++*at; + } + } + } while (!done); + + return to - *at; +} + +#define GROUP_BLK 10 +#define blk_size(len) ((((len)-1 + GROUP_BLK)/GROUP_BLK)*GROUP_BLK) + +static int mkgrplist(pam_handle_t *pamh, char *buf, gid_t **list, int len) +{ + int l,at=0; + int blks; + + blks = blk_size(len); + D(("cf. blks=%d and len=%d", blks,len)); + + while ((l = find_member(buf,&at))) { + int edge; + + if (len >= blks) { + gid_t *tmp; + + D(("allocating new block")); + tmp = realloc((*list), sizeof(gid_t) * (blks += GROUP_BLK)); + if (tmp != NULL) { + (*list) = tmp; + } else { + pam_syslog(pamh, LOG_ERR, "out of memory for group list"); + free(*list); + (*list) = NULL; + return -1; + } + } + + /* '\0' terminate the entry */ + + edge = (buf[at+l]) ? 1:0; + buf[at+l] = '\0'; + D(("found group: %s",buf+at)); + + /* this is where we convert a group name to a gid_t */ + { + const struct group *grp; + + grp = pam_modutil_getgrnam(pamh, buf+at); + if (grp == NULL) { + pam_syslog(pamh, LOG_ERR, "bad group: %s", buf+at); + } else { + D(("group %s exists", buf+at)); + (*list)[len++] = grp->gr_gid; + } + } + + /* next entry along */ + + at += l + edge; + } + D(("returning with [%p/len=%d]->%p",list,len,*list)); + return len; +} + + +static int check_account(pam_handle_t *pamh, const char *service, + const char *tty, const char *user) +{ + int from=0, state=STATE_NL, fd=-1; + char *buffer=NULL; + int count=0; + TIME here_and_now; + int retval=PAM_SUCCESS; + gid_t *grps; + int no_grps; + const char *conf_filename = PAM_GROUP_CONF; + +#ifdef VENDOR_PAM_GROUP_CONF + /* + * Check whether PAM_GROUP_CONF file is available. + * If it does not exist, fall back to VENDOR_PAM_GROUP_CONF file. + */ + struct stat stat_buffer; + if (stat(conf_filename, &stat_buffer) != 0 && errno == ENOENT) { + conf_filename = VENDOR_PAM_GROUP_CONF; + } +#endif + + /* + * first we get the current list of groups - the application + * will have previously done an initgroups(), or equivalent. + */ + + D(("counting supplementary groups")); + no_grps = getgroups(0, NULL); /* find the current number of groups */ + if (no_grps > 0) { + grps = calloc( blk_size(no_grps) , sizeof(gid_t) ); + D(("copying current list into grps [%d big]",blk_size(no_grps))); + if (getgroups(no_grps, grps) < 0) { + D(("getgroups call failed")); + no_grps = 0; + _pam_drop(grps); + } +#ifdef PAM_DEBUG + { + int z; + for (z=0; z 0) { + D(("rule #%d passed, added %d groups", count, good)); + } else if (good < 0) { + retval = PAM_BUF_ERR; + } else { + D(("rule #%d failed", count)); + } + + } while (state != STATE_EOF); + + /* now set the groups for the user */ + + if (no_grps > 0) { +#ifdef PAM_DEBUG + int err; +#endif + D(("trying to set %d groups", no_grps)); +#ifdef PAM_DEBUG + for (err=0; err + + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_issue.8.xml" xpointer='xpointer(id("pam_issue-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + \ No newline at end of file diff --git a/modules/pam_issue/meson.build b/modules/pam_issue/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_issue/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_issue/pam_issue.8.xml b/modules/pam_issue/pam_issue.8.xml new file mode 100644 index 0000000..20d3245 --- /dev/null +++ b/modules/pam_issue/pam_issue.8.xml @@ -0,0 +1,231 @@ + + + + pam_issue + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_issue + PAM module to add issue file to user prompt + + + + + pam_issue.so + + noesc + + + issue=issue-file-name + + + + + + + DESCRIPTION + + + pam_issue is a PAM module to prepend an issue file to the username + prompt. It also by default parses escape codes in the issue file + similar to some common getty's (using \x format). + + + Recognized escapes: + + + + \d + + current day + + + + \l + + name of this tty + + + + \m + + machine architecture (uname -m) + + + + \n + + machine's network node hostname (uname -n) + + + + \o + + domain name of this system + + + + \r + + release number of operating system (uname -r) + + + + \t + + current time + + + + \s + + operating system name (uname -s) + + + + \u + + number of users currently logged in + + + + \U + + + same as \u except it is suffixed with "user" or + "users" (eg. "1 user" or "10 users") + + + + + \v + + operating system version and build date (uname -v) + + + + + + + + + OPTIONS + + + + + + noesc + + + + Turns off escape code parsing. + + + + + + + issue=issue-file-name + + + + The file to output if not using the default. + + + + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_IGNORE + + + The prompt was already changed. + + + + + + PAM_SERVICE_ERR + + + A service module error occurred. + + + + + + PAM_SUCCESS + + + The new prompt was set successfully. + + + + + + + + + + EXAMPLES + + Add the following line to /etc/pam.d/login to + set the user specific issue at login: + + auth optional pam_issue.so issue=/etc/issue + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_issue was written by Ben Collins <bcollins@debian.org>. + + + + \ No newline at end of file diff --git a/modules/pam_issue/pam_issue.c b/modules/pam_issue/pam_issue.c new file mode 100644 index 0000000..e2c555c --- /dev/null +++ b/modules/pam_issue/pam_issue.c @@ -0,0 +1,330 @@ +/* + * pam_issue module - a simple /etc/issue parser to set PAM_USER_PROMPT + * + * Copyright 1999 by Ben Collins + * + * Needs to be called before any other auth modules so we can setup the + * user prompt before it's first used. Allows one argument option, which + * is the full path to a file to be used for issue (uses /etc/issue as a + * default) such as "issue=/etc/issue.telnet". + * + * We can also parse escapes within the the issue file (enabled by + * default, but can be disabled with the "noesc" option). It's the exact + * same parsing as util-linux's agetty program performs. + * + * Released under the GNU LGPL version 2 or later + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_LOGIND +#include +#else +#include +#endif + +#include +#include +#include +#include "pam_inline.h" + +static int _user_prompt_set = 0; + +static int +read_issue_raw(pam_handle_t *pamh, FILE *fp, char **prompt) +{ + char *issue; + struct stat st; + + *prompt = NULL; + + if (fstat(fileno(fp), &st) < 0) { + pam_syslog(pamh, LOG_ERR, "stat error: %m"); + return PAM_SERVICE_ERR; + } + + if ((issue = malloc(st.st_size + 1)) == NULL) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return PAM_BUF_ERR; + } + + if ((off_t)fread(issue, 1, st.st_size, fp) != st.st_size) { + pam_syslog(pamh, LOG_ERR, "read error: %m"); + _pam_drop(issue); + return PAM_SERVICE_ERR; + } + + issue[st.st_size] = '\0'; + *prompt = issue; + return PAM_SUCCESS; +} + +static int +read_issue_quoted(pam_handle_t *pamh, FILE *fp, char **prompt) +{ + int c; + size_t size = 1024; + size_t issue_len = 0; + char *issue; + struct utsname uts; + + *prompt = NULL; + + if ((issue = malloc(size)) == NULL) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return PAM_BUF_ERR; + } + + (void) uname(&uts); + + while ((c = getc(fp)) != EOF) { + const char *src = NULL; + size_t len = 0; + char buf[1024] = ""; + + if (c == '\\') { + if ((c = getc(fp)) == EOF) + break; + switch (c) { + case 's': + src = uts.sysname; + len = strnlen(uts.sysname, sizeof(uts.sysname)); + break; + case 'n': + src = uts.nodename; + len = strnlen(uts.nodename, sizeof(uts.nodename)); + break; + case 'r': + src = uts.release; + len = strnlen(uts.release, sizeof(uts.release)); + break; + case 'v': + src = uts.version; + len = strnlen(uts.version, sizeof(uts.version)); + break; + case 'm': + src = uts.machine; + len = strnlen(uts.machine, sizeof(uts.machine)); + break; + case 'o': +#ifdef HAVE_GETDOMAINNAME + if (getdomainname(buf, sizeof(buf)) >= 0) + buf[sizeof(buf) - 1] = '\0'; +#endif + break; + case 'd': + case 't': + { + const char *weekday[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", + "Fri", "Sat" }; + const char *month[] = { + "Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec" }; + time_t now; + struct tm *tm; + + (void) time (&now); + tm = localtime(&now); + + if (c == 'd') + snprintf (buf, sizeof buf, "%s %s %d %d", + weekday[tm->tm_wday], month[tm->tm_mon], + tm->tm_mday, tm->tm_year + 1900); + else + snprintf (buf, sizeof buf, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + break; + case 'l': + { + const char *ttyn = ttyname(1); + if (ttyn) { + const char *str = pam_str_skip_prefix(ttyn, "/dev/"); + if (str != NULL) + ttyn = str; + src = ttyn; + len = strlen(ttyn); + } + } + break; + case 'u': + case 'U': + { + unsigned int users = 0; +#ifdef USE_LOGIND + char **sessions_list; + int sessions = sd_get_sessions(&sessions_list); + + if (sessions < 0) { + pam_syslog(pamh, LOG_ERR, "logind error: %s", + strerror(-sessions)); + _pam_drop(issue); + return PAM_SERVICE_ERR; + } else if (sessions > 0 && sessions_list != NULL) { + int i; + + for (i = 0; i < sessions; i++) { + char *class; + + if (sd_session_get_class(sessions_list[i], &class) < 0 || class == NULL) + continue; + + if (strncmp(class, "user", 4) == 0) // user, user-early, user-incomplete + users++; + free(class); + } + + for (i = 0; i < sessions; i++) + free(sessions_list[i]); + free(sessions_list); + } else { + users = sessions; + } +#else + struct utmp *ut; + setutent(); + while ((ut = getutent())) { + if (ut->ut_type == USER_PROCESS) + ++users; + } + endutent(); +#endif + if (c == 'U') + snprintf (buf, sizeof buf, "%u %s", users, + (users == 1) ? "user" : "users"); + else + snprintf (buf, sizeof buf, "%u", users); + break; + } + default: + buf[0] = c; buf[1] = '\0'; + } + } else { + buf[0] = c; buf[1] = '\0'; + } + + if (src == NULL) { + src = buf; + len = strlen(buf); + } + if (issue_len + len + 1 > size) { + char *new_issue; + + size += len + 1; + new_issue = realloc (issue, size); + if (new_issue == NULL) { + _pam_drop(issue); + return PAM_BUF_ERR; + } + issue = new_issue; + } + memcpy(issue + issue_len, src, len); + issue_len += len; + } + + issue[issue_len] = '\0'; + + if (ferror(fp)) { + pam_syslog(pamh, LOG_ERR, "read error: %m"); + _pam_drop(issue); + return PAM_SERVICE_ERR; + } + + *prompt = issue; + return PAM_SUCCESS; +} + +/* --- authentication management functions (only) --- */ + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int retval = PAM_SERVICE_ERR; + FILE *fp; + const char *issue_file = NULL; + int parse_esc = 1; + const void *item = NULL; + char *issue_prompt = NULL; + + /* If we've already set the prompt, don't set it again */ + if(_user_prompt_set) + return PAM_IGNORE; + + /* We set this here so if we fail below, we won't get further + than this next time around (only one real failure) */ + _user_prompt_set = 1; + + for ( ; argc-- > 0 ; ++argv ) { + const char *str; + + if ((str = pam_str_skip_prefix(*argv, "issue=")) != NULL) { + issue_file = str; + D(("set issue_file to: %s", issue_file)); + } else if (!strcmp(*argv,"noesc")) { + parse_esc = 0; + D(("turning off escape parsing by request")); + } else + D(("unknown option passed: %s", *argv)); + } + + if (issue_file == NULL) + issue_file = "/etc/issue"; + + if ((fp = fopen(issue_file, "r")) == NULL) { + pam_syslog(pamh, LOG_ERR, "error opening %s: %m", issue_file); + return PAM_SERVICE_ERR; + } + + if ((retval = pam_get_item(pamh, PAM_USER_PROMPT, &item)) != PAM_SUCCESS) { + fclose(fp); + return retval; + } + + if (parse_esc) + retval = read_issue_quoted(pamh, fp, &issue_prompt); + else + retval = read_issue_raw(pamh, fp, &issue_prompt); + + fclose(fp); + + if (retval != PAM_SUCCESS) + goto out; + + if (item != NULL) { + const char *cur_prompt = item; + char *new_prompt; + if (asprintf(&new_prompt, "%s%s", issue_prompt, cur_prompt) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + retval = PAM_BUF_ERR; + goto out; + } + issue_prompt = new_prompt; + } + + retval = pam_set_item(pamh, PAM_USER_PROMPT, + (const void *) issue_prompt); + out: + _pam_drop(issue_prompt); + return (retval == PAM_SUCCESS) ? PAM_IGNORE : retval; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} diff --git a/modules/pam_issue/tst-pam_issue b/modules/pam_issue/tst-pam_issue new file mode 100755 index 0000000..0fe4f76 --- /dev/null +++ b/modules/pam_issue/tst-pam_issue @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_issue.so diff --git a/modules/pam_keyinit/README.xml b/modules/pam_keyinit/README.xml new file mode 100644 index 0000000..33059c7 --- /dev/null +++ b/modules/pam_keyinit/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_keyinit.8.xml" xpointer='xpointer(id("pam_keyinit-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_keyinit/meson.build b/modules/pam_keyinit/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_keyinit/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_keyinit/pam_keyinit.8.xml b/modules/pam_keyinit/pam_keyinit.8.xml new file mode 100644 index 0000000..7b0a73b --- /dev/null +++ b/modules/pam_keyinit/pam_keyinit.8.xml @@ -0,0 +1,247 @@ + + + + pam_keyinit + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_keyinit + Kernel session keyring initialiser module + + + + + pam_keyinit.so + + debug + + + force + + + revoke + + + + + + DESCRIPTION + + The pam_keyinit PAM module ensures that the invoking process has a + session keyring other than the user default session keyring. + + + The module checks to see if the process's session keyring is the + + user-session-keyring7 + , + and, if it is, creates a new + + session-keyring7 + + with which to replace it. If a new session keyring is created, it will + install a link to the + + user-keyring7 + + in the session keyring so that keys common to the user will be + automatically accessible through it. The session keyring of the invoking + process will thenceforth be inherited by all its children unless they override it. + + + In order to allow other PAM modules to attach tokens to the keyring, this module + provides both an auth (limited to + + pam_setcred3 + + and a session component. The session keyring is created + in the module called. Moreover this module should be included as early as + possible in a PAM configuration. + + + This module is intended primarily for use by login processes. Be aware + that after the session keyring has been replaced, the old session keyring + and the keys it contains will no longer be accessible. + + + This module should not, generally, be invoked by programs like + su, since it is usually desirable for the + key set to percolate through to the alternate context. The keys have + their own permissions system to manage this. + + + The keyutils package is used to manipulate keys more directly. This + can be obtained from: + + + + Keyutils + + + + + + OPTIONS + + + + debug + + + + Log debug information with + syslog3 + . + + + + + + + force + + + + Causes the session keyring of the invoking process to be replaced + unconditionally. + + + + + + + revoke + + + + Causes the session keyring of the invoking process to be revoked + when the invoking process exits if the session keyring was created + for this process in the first place. + + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + This module will usually return this value + + + + + + PAM_AUTH_ERR + + + Authentication failure. + + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_IGNORE + + + The return value should be ignored by PAM dispatch. + + + + + + PAM_SERVICE_ERR + + + Cannot determine the user name. + + + + + + PAM_SESSION_ERR + + + This module will return this value if its arguments are invalid or + if a system error such as ENOMEM occurs. + + + + + + PAM_USER_UNKNOWN + + + User not known. + + + + + + + + + EXAMPLES + + Add this line to your login entries to start each login session with its + own session keyring: + +session required pam_keyinit.so + + + + This will prevent keys from one session leaking into another session for + the same user. + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + , + + keyctl1 + + + + + + AUTHOR + + pam_keyinit was written by David Howells, <dhowells@redhat.com>. + + + + \ No newline at end of file diff --git a/modules/pam_keyinit/pam_keyinit.c b/modules/pam_keyinit/pam_keyinit.c new file mode 100644 index 0000000..ad81b80 --- /dev/null +++ b/modules/pam_keyinit/pam_keyinit.c @@ -0,0 +1,326 @@ +/* + * pam_keyinit: Initialise the session keyring on login through a PAM module + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEY_SPEC_SESSION_KEYRING (-3) /* ID for session keyring */ +#define KEY_SPEC_USER_KEYRING (-4) /* ID for UID-specific keyring */ +#define KEY_SPEC_USER_SESSION_KEYRING (-5) /* - key ID for UID-session keyring */ + +#define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */ +#define KEYCTL_JOIN_SESSION_KEYRING 1 /* start named session keyring */ +#define KEYCTL_REVOKE 3 /* revoke a key */ +#define KEYCTL_LINK 8 /* link a key into a keyring */ + +static _Thread_local int my_session_keyring = 0; +static _Atomic int session_counter = 0; +static _Thread_local int do_revoke = 0; +static _Thread_local uid_t revoke_as_uid; +static _Thread_local gid_t revoke_as_gid; +static _Thread_local int xdebug = 0; + +static void debug(pam_handle_t *pamh, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +static void debug(pam_handle_t *pamh, const char *fmt, ...) +{ + va_list va; + + if (xdebug) { + va_start(va, fmt); + pam_vsyslog(pamh, LOG_DEBUG, fmt, va); + va_end(va); + } +} + +static void error(pam_handle_t *pamh, const char *fmt, ...) + __attribute__((format(printf, 2, 3))); + +static void error(pam_handle_t *pamh, const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + pam_vsyslog(pamh, LOG_ERR, fmt, va); + va_end(va); +} + +static int pam_setreuid(uid_t ruid, uid_t euid) +{ +#if defined(SYS_setreuid32) + return syscall(SYS_setreuid32, ruid, euid); +#else + return syscall(SYS_setreuid, ruid, euid); +#endif +} + +static int pam_setregid(gid_t rgid, gid_t egid) +{ +#if defined(SYS_setregid32) + return syscall(SYS_setregid32, rgid, egid); +#else + return syscall(SYS_setregid, rgid, egid); +#endif +} + +static int pam_setresuid(uid_t ruid, uid_t euid, uid_t suid) +{ +#if defined(SYS_setresuid32) + return syscall(SYS_setresuid32, ruid, euid, suid); +#else + return syscall(SYS_setresuid, ruid, euid, suid); +#endif +} + +/* + * initialise the session keyring for this process + */ +static int init_keyrings(pam_handle_t *pamh, int force, int error_ret) +{ + int session, usession, ret; + + if (!force) { + /* get the IDs of the session keyring and the user session + * keyring */ + session = syscall(__NR_keyctl, + KEYCTL_GET_KEYRING_ID, + KEY_SPEC_SESSION_KEYRING, + 0); + debug(pamh, "GET SESSION = %d", session); + if (session < 0) { + /* don't worry about keyrings if facility not + * installed */ + if (errno == ENOSYS) + return PAM_SUCCESS; + return error_ret; + } + + usession = syscall(__NR_keyctl, + KEYCTL_GET_KEYRING_ID, + KEY_SPEC_USER_SESSION_KEYRING, + 0); + debug(pamh, "GET SESSION = %d", usession); + if (usession < 0) + return error_ret; + + /* if the user session keyring is our keyring, then we don't + * need to do anything if we're not forcing */ + if (session != usession) + return PAM_SUCCESS; + } + + /* create a session keyring, discarding the old one */ + ret = syscall(__NR_keyctl, + KEYCTL_JOIN_SESSION_KEYRING, + NULL); + debug(pamh, "JOIN = %d", ret); + if (ret < 0) + return error_ret; + + my_session_keyring = ret; + + /* make a link from the session keyring to the user keyring */ + ret = syscall(__NR_keyctl, + KEYCTL_LINK, + KEY_SPEC_USER_KEYRING, + KEY_SPEC_SESSION_KEYRING); + + return ret < 0 ? error_ret : PAM_SUCCESS; +} + +/* + * revoke the session keyring for this process + */ +static int kill_keyrings(pam_handle_t *pamh, int error_ret) +{ + uid_t old_uid; + gid_t old_gid; + int ret = PAM_SUCCESS; + + /* revoke the session keyring we created earlier */ + if (my_session_keyring > 0) { + debug(pamh, "REVOKE %d", my_session_keyring); + + old_uid = geteuid(); + old_gid = getegid(); + debug(pamh, "UID:%d [%d] GID:%d [%d]", + revoke_as_uid, old_uid, revoke_as_gid, old_gid); + + /* switch to the real UID and GID so that we have permission to + * revoke the key */ + if (revoke_as_gid != old_gid && pam_setregid(-1, revoke_as_gid) < 0) { + error(pamh, "Unable to change GID to %d temporarily\n", revoke_as_gid); + return error_ret; + } + + if (revoke_as_uid != old_uid && pam_setresuid(-1, revoke_as_uid, old_uid) < 0) { + error(pamh, "Unable to change UID to %d temporarily\n", revoke_as_uid); + if (getegid() != old_gid && pam_setregid(-1, old_gid) < 0) + error(pamh, "Unable to change GID back to %d\n", old_gid); + return error_ret; + } + + if (syscall(__NR_keyctl, KEYCTL_REVOKE, my_session_keyring) < 0) { + ret = error_ret; + } + + /* return to the original UID and GID (probably root) */ + if (revoke_as_uid != old_uid && pam_setreuid(-1, old_uid) < 0) { + error(pamh, "Unable to change UID back to %d\n", old_uid); + ret = error_ret; + } + + if (revoke_as_gid != old_gid && pam_setregid(-1, old_gid) < 0) { + error(pamh, "Unable to change GID back to %d\n", old_gid); + ret = error_ret; + } + + my_session_keyring = 0; + } + return ret; +} + +static int do_keyinit(pam_handle_t *pamh, int argc, const char **argv, int error_ret) +{ + struct passwd *pw; + const char *username; + int ret, loop, force = 0; + uid_t old_uid, uid; + gid_t old_gid, gid; + + for (loop = 0; loop < argc; loop++) { + if (strcmp(argv[loop], "force") == 0) + force = 1; + else if (strcmp(argv[loop], "debug") == 0) + xdebug = 1; + else if (strcmp(argv[loop], "revoke") == 0) + do_revoke = 1; + } + + /* don't do anything if already created a keyring (will be called + * multiple times if mentioned more than once in a pam script) + */ + if (my_session_keyring > 0) + return PAM_SUCCESS; + + /* look up the target UID */ + ret = pam_get_user(pamh, &username, "key user"); + if (ret != PAM_SUCCESS) + return ret; + + pw = pam_modutil_getpwnam(pamh, username); + if (!pw) { + pam_syslog(pamh, LOG_NOTICE, "Unable to look up user \"%s\"\n", + username); + return PAM_USER_UNKNOWN; + } + + revoke_as_uid = uid = pw->pw_uid; + old_uid = getuid(); + revoke_as_gid = gid = pw->pw_gid; + old_gid = getgid(); + debug(pamh, "UID:%d [%d] GID:%d [%d]", uid, old_uid, gid, old_gid); + + /* switch to the real UID and GID so that the keyring ends up owned by + * the right user */ + if (gid != old_gid && pam_setregid(gid, -1) < 0) { + error(pamh, "Unable to change GID to %d temporarily\n", gid); + return error_ret; + } + + if (uid != old_uid && pam_setreuid(uid, -1) < 0) { + error(pamh, "Unable to change UID to %d temporarily\n", uid); + if (pam_setregid(old_gid, -1) < 0) + error(pamh, "Unable to change GID back to %d\n", old_gid); + return error_ret; + } + + ret = init_keyrings(pamh, force, error_ret); + + /* return to the original UID and GID (probably root) */ + if (uid != old_uid && pam_setreuid(old_uid, -1) < 0) { + error(pamh, "Unable to change UID back to %d\n", old_uid); + ret = error_ret; + } + + if (gid != old_gid && pam_setregid(old_gid, -1) < 0) { + error(pamh, "Unable to change GID back to %d\n", old_gid); + ret = error_ret; + } + + return ret; +} + +/* + * Dummy + */ +int pam_sm_authenticate(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +/* + * since setcred and open_session are called in different orders, a + * session ring is invoked by the first of these functions called. + */ +int pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + if (flags & PAM_ESTABLISH_CRED) { + debug(pamh, "ESTABLISH_CRED"); + return do_keyinit(pamh, argc, argv, PAM_CRED_ERR); + } + if (flags & PAM_DELETE_CRED && my_session_keyring > 0 && do_revoke) { + debug(pamh, "DELETE_CRED"); + return kill_keyrings(pamh, PAM_CRED_ERR); + } + return PAM_IGNORE; +} + +int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + session_counter++; + + debug(pamh, "OPEN %d", session_counter); + + return do_keyinit(pamh, argc, argv, PAM_SESSION_ERR); +} + +/* + * close a PAM session by revoking the session keyring if requested + */ +int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + debug(pamh, "CLOSE %d,%d,%d", + session_counter, my_session_keyring, do_revoke); + + session_counter--; + + if (session_counter <= 0 && my_session_keyring > 0 && do_revoke) + kill_keyrings(pamh, PAM_SESSION_ERR); + + return PAM_SUCCESS; +} diff --git a/modules/pam_keyinit/tst-pam_keyinit b/modules/pam_keyinit/tst-pam_keyinit new file mode 100755 index 0000000..f0a7b9b --- /dev/null +++ b/modules/pam_keyinit/tst-pam_keyinit @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_keyinit.so diff --git a/modules/pam_lastlog/README.xml b/modules/pam_lastlog/README.xml new file mode 100644 index 0000000..6b31243 --- /dev/null +++ b/modules/pam_lastlog/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_lastlog.8.xml" xpointer='xpointer(id("pam_lastlog-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_lastlog/meson.build b/modules/pam_lastlog/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_lastlog/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_lastlog/pam_lastlog.8.xml b/modules/pam_lastlog/pam_lastlog.8.xml new file mode 100644 index 0000000..d990978 --- /dev/null +++ b/modules/pam_lastlog/pam_lastlog.8.xml @@ -0,0 +1,340 @@ + + + + pam_lastlog + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_lastlog + PAM module to display date of last login and perform inactive account lock out + + + + + pam_lastlog.so + + debug + + + silent + + + never + + + nodate + + + nohost + + + noterm + + + nowtmp + + + noupdate + + + showfailed + + + inactive=<days> + + + unlimited + + + + + + + DESCRIPTION + + + pam_lastlog is a PAM module to display a line of information + about the last login of the user. In addition, the module maintains + the /var/log/lastlog file. + + + Some applications may perform this function themselves. In such + cases, this module is not necessary. + + + The module checks option in + /etc/login.defs and does not update or display + last login records for users with UID higher than its value. + If the option is not present or its value is invalid, no user ID + limit is applied. + + + If the module is called in the auth or account phase, the accounts that + were not used recently enough will be disallowed to log in. The + check is not performed for the root account so the root is never + locked out. It is also not performed for users with UID higher + than the value. + + + + + + OPTIONS + + + + debug + + + + Print debug information. + + + + + + silent + + + + Don't inform the user about any previous login, + just update the /var/log/lastlog file. + This option does not affect display of bad login attempts. + + + + + + never + + + + If the /var/log/lastlog file does + not contain any old entries for the user, indicate that + the user has never previously logged in with a welcome + message. + + + + + + nodate + + + + Don't display the date of the last login. + + + + + + noterm + + + + Don't display the terminal name on which the + last login was attempted. + + + + + + nohost + + + + Don't indicate from which host the last login was + attempted. + + + + + + nowtmp + + + + Don't update the wtmp entry. + + + + + + noupdate + + + + Don't update any file. + + + + + + showfailed + + + + Display number of failed login attempts and the date of the + last failed attempt from btmp. The date is not displayed + when is specified. + + + + + + inactive=<days> + + + + This option is specific for the auth or account phase. It + specifies the number of days after the last login of the user + when the user will be locked out by the module. The default + value is 90. + + + + + + unlimited + + + + If the fsize limit is set, this option can be + used to override it, preventing failures on systems with large UID + values that lead lastlog to become a huge sparse file. + + + + + + + + MODULE TYPES PROVIDED + + The and module type + allows one to lock out users who did not login recently enough. + The module type is provided for displaying + the information about the last login and/or updating the lastlog and + wtmp files. + + + + + RETURN VALUES + + + + + PAM_SUCCESS + + + Everything was successful. + + + + + + PAM_SERVICE_ERR + + + Internal service module error. + + + + + + PAM_USER_UNKNOWN + + + User not known. + + + + + + PAM_AUTH_ERR + + + User locked out in the auth or account phase due to + inactivity. + + + + + + PAM_IGNORE + + + There was an error during reading the lastlog file + in the auth or account phase and thus inactivity + of the user cannot be determined. + + + + + + + + + + EXAMPLES + + Add the following line to /etc/pam.d/login to + display the last login time of a user: + + + session required pam_lastlog.so nowtmp + + + To reject users if they did not login during the previous 50 days + the following line can be used: + + + auth required pam_lastlog.so inactive=50 + + + + + FILES + + + /var/log/lastlog + + Lastlog logging file + + + + + + + SEE ALSO + + + limits.conf5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_lastlog was written by Andrew G. Morgan <morgan@kernel.org>. + + + Inactive account lock out added by Tomáš Mráz <tm@t8m.info>. + + + + diff --git a/modules/pam_lastlog/pam_lastlog.c b/modules/pam_lastlog/pam_lastlog.c new file mode 100644 index 0000000..ded7676 --- /dev/null +++ b/modules/pam_lastlog/pam_lastlog.c @@ -0,0 +1,823 @@ +/* + * pam_lastlog module + * + * Written by Andrew Morgan 1996/3/11 + * + * This module does the necessary work to display the last login + * time+date for this user, it then updates this entry for the + * present (login) service. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(hpux) || defined(sunos) || defined(solaris) +# ifndef _PATH_LASTLOG +# define _PATH_LASTLOG "/usr/adm/lastlog" +# endif /* _PATH_LASTLOG */ +# ifndef UT_HOSTSIZE +# define UT_HOSTSIZE 16 +# endif /* UT_HOSTSIZE */ +# ifndef UT_LINESIZE +# define UT_LINESIZE 12 +# endif /* UT_LINESIZE */ +#endif +#if defined(hpux) +struct lastlog { + time_t ll_time; + char ll_line[UT_LINESIZE]; + char ll_host[UT_HOSTSIZE]; /* same as in utmp */ +}; +#endif /* hpux */ + +#ifndef _PATH_BTMP +# define _PATH_BTMP "/var/log/btmp" +#endif + +#ifndef PATH_LOGIN_DEFS +# define PATH_LOGIN_DEFS "/etc/login.defs" +#endif + +#define DEFAULT_HOST "" /* "[no.where]" */ +#define DEFAULT_TERM "" /* "tt???" */ + +#define DEFAULT_INACTIVE_DAYS 90 +#define MAX_INACTIVE_DAYS 100000 +#define LOCK_RETRIES 3 /* number of file lock retries */ +#define LOCK_RETRY_DELAY 1 /* seconds to wait between lock attempts */ + +#include +#include +#include +#include +#include "pam_inline.h" +#include "pam_i18n.h" + +/* argument parsing */ + +#define LASTLOG_DATE 01 /* display the date of the last login */ +#define LASTLOG_HOST 02 /* display the last host used (if set) */ +#define LASTLOG_LINE 04 /* display the last terminal used */ +#define LASTLOG_NEVER 010 /* display a welcome message for first login */ +#define LASTLOG_DEBUG 020 /* send info to syslog(3) */ +#define LASTLOG_QUIET 040 /* keep quiet about things */ +#define LASTLOG_WTMP 0100 /* log to wtmp as well as lastlog */ +#define LASTLOG_BTMP 0200 /* display failed login info from btmp */ +#define LASTLOG_UPDATE 0400 /* update the lastlog and wtmp files (default) */ +#define LASTLOG_UNLIMITED 01000 /* unlimited file size (ignore 'fsize' limit) */ + +static int +_pam_auth_parse(pam_handle_t *pamh, int flags, int argc, const char **argv, + time_t *inactive) +{ + int ctrl = 0; + + *inactive = DEFAULT_INACTIVE_DAYS; + + /* does the application require quiet? */ + if (flags & PAM_SILENT) { + ctrl |= LASTLOG_QUIET; + } + + /* step through arguments */ + for (; argc-- > 0; ++argv) { + const char *str; + char *ep = NULL; + long l; + + if (!strcmp(*argv,"debug")) { + ctrl |= LASTLOG_DEBUG; + } else if (!strcmp(*argv,"silent")) { + ctrl |= LASTLOG_QUIET; + } else if ((str = pam_str_skip_prefix(*argv, "inactive=")) != NULL) { + l = strtol(str, &ep, 10); + if (ep != str && l > 0 && l < MAX_INACTIVE_DAYS) + *inactive = l; + else { + pam_syslog(pamh, LOG_ERR, "bad option value: %s", *argv); + } + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + D(("ctrl = %o", ctrl)); + return ctrl; +} + +static int +_pam_session_parse(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + int ctrl=(LASTLOG_DATE|LASTLOG_HOST|LASTLOG_LINE|LASTLOG_WTMP|LASTLOG_UPDATE); + + /* step through arguments */ + for (; argc-- > 0; ++argv) { + + /* generic options */ + + if (!strcmp(*argv,"debug")) { + ctrl |= LASTLOG_DEBUG; + } else if (!strcmp(*argv,"nodate")) { + ctrl &= ~LASTLOG_DATE; + } else if (!strcmp(*argv,"noterm")) { + ctrl &= ~LASTLOG_LINE; + } else if (!strcmp(*argv,"nohost")) { + ctrl &= ~LASTLOG_HOST; + } else if (!strcmp(*argv,"silent")) { + ctrl |= LASTLOG_QUIET; + } else if (!strcmp(*argv,"never")) { + ctrl |= LASTLOG_NEVER; + } else if (!strcmp(*argv,"nowtmp")) { + ctrl &= ~LASTLOG_WTMP; + } else if (!strcmp(*argv,"noupdate")) { + ctrl &= ~(LASTLOG_WTMP|LASTLOG_UPDATE); + } else if (!strcmp(*argv,"showfailed")) { + ctrl |= LASTLOG_BTMP; + } else if (!strcmp(*argv,"unlimited")) { + ctrl |= LASTLOG_UNLIMITED; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + /* does the application require quiet? */ + if (flags & PAM_SILENT) { + ctrl |= LASTLOG_QUIET; + ctrl &= ~LASTLOG_BTMP; + } + + D(("ctrl = %o", ctrl)); + return ctrl; +} + +static const char * +get_tty(pam_handle_t *pamh) +{ + const void *void_terminal_line = NULL; + const char *terminal_line; + const char *str; + + if (pam_get_item(pamh, PAM_TTY, &void_terminal_line) != PAM_SUCCESS + || void_terminal_line == NULL) { + terminal_line = DEFAULT_TERM; + } else { + terminal_line = void_terminal_line; + } + + /* strip leading "/dev/" from tty. */ + str = pam_str_skip_prefix(terminal_line, "/dev/"); + if (str != NULL) + terminal_line = str; + + D(("terminal = %s", terminal_line)); + return terminal_line; +} + +#define MAX_UID_VALUE 0xFFFFFFFFUL + +static uid_t +get_lastlog_uid_max(pam_handle_t *pamh) +{ + uid_t uid_max = MAX_UID_VALUE; + unsigned long ul; + char *s, *ep; + + s = pam_modutil_search_key(pamh, PATH_LOGIN_DEFS, "LASTLOG_UID_MAX"); + if (s == NULL) + return uid_max; + + ep = s + strlen(s); + while (ep > s && isspace((unsigned char)*(--ep))) { + *ep = '\0'; + } + errno = 0; + ul = strtoul(s, &ep, 10); + if (!(ul >= MAX_UID_VALUE + || (uid_t)ul >= MAX_UID_VALUE + || (errno != 0 && ul == 0) + || s == ep + || *ep != '\0')) { + uid_max = (uid_t)ul; + } + free(s); + + return uid_max; +} + +static int +last_login_open(pam_handle_t *pamh, int announce, uid_t uid) +{ + int last_fd; + + /* obtain the last login date and all the relevant info */ + last_fd = open(_PATH_LASTLOG, announce&LASTLOG_UPDATE ? O_RDWR : O_RDONLY); + if (last_fd < 0) { + if (errno == ENOENT && (announce & LASTLOG_UPDATE)) { + last_fd = open(_PATH_LASTLOG, O_RDWR|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (last_fd < 0) { + pam_syslog(pamh, LOG_ERR, + "unable to create %s: %m", _PATH_LASTLOG); + D(("unable to create %s file", _PATH_LASTLOG)); + return -1; + } + pam_syslog(pamh, LOG_NOTICE, + "file %s created", _PATH_LASTLOG); + D(("file %s created", _PATH_LASTLOG)); + } else { + pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_LASTLOG); + D(("unable to open %s file", _PATH_LASTLOG)); + return -1; + } + } + + if (lseek(last_fd, sizeof(struct lastlog) * (off_t) uid, SEEK_SET) < 0) { + pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG); + D(("unable to lseek %s file", _PATH_LASTLOG)); + close(last_fd); + return -1; + } + + return last_fd; +} + + +static int +last_login_read(pam_handle_t *pamh, int announce, int last_fd, uid_t uid, time_t *lltime) +{ + struct flock last_lock; + struct lastlog last_login; + int lock_retries = LOCK_RETRIES; + int retval = PAM_SUCCESS; + char the_time[256]; + char *date = NULL; + char *host = NULL; + char *line = NULL; + + memset(&last_lock, 0, sizeof(last_lock)); + last_lock.l_type = F_RDLCK; + last_lock.l_whence = SEEK_SET; + last_lock.l_start = sizeof(last_login) * (off_t) uid; + last_lock.l_len = sizeof(last_login); + + while (fcntl(last_fd, F_SETLK, &last_lock) < 0) { + if (0 == --lock_retries) { + /* read lock failed, proceed anyway to avoid possible DoS */ + D(("locking %s failed", _PATH_LASTLOG)); + pam_syslog(pamh, LOG_INFO, + "file %s is locked/read, proceeding anyway", + _PATH_LASTLOG); + break; + } + D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); + pam_syslog(pamh, LOG_INFO, + "file %s is locked/read, retrying", _PATH_LASTLOG); + sleep(LOCK_RETRY_DELAY); + } + + if (pam_modutil_read(last_fd, (char *) &last_login, + sizeof(last_login)) != sizeof(last_login)) { + memset(&last_login, 0, sizeof(last_login)); + } + + last_lock.l_type = F_UNLCK; + (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ + + *lltime = last_login.ll_time; + if (!last_login.ll_time) { + if (announce & LASTLOG_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, + "first login for user with uid %lu", + (unsigned long int)uid); + } + } + + if (!(announce & LASTLOG_QUIET)) { + + if (last_login.ll_time) { + + /* we want the date? */ + if (announce & LASTLOG_DATE) { + struct tm *tm, tm_buf; + time_t ll_time; + + ll_time = last_login.ll_time; + if ((tm = localtime_r (&ll_time, &tm_buf)) != NULL) { + strftime (the_time, sizeof (the_time), + /* TRANSLATORS: "strftime options for date of last login" */ + _(" %a %b %e %H:%M:%S %Z %Y"), tm); + date = the_time; + } + } + + /* we want & have the host? */ + if ((announce & LASTLOG_HOST) + && (last_login.ll_host[0] != '\0')) { + /* TRANSLATORS: " from " */ + if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE, + last_login.ll_host) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + retval = PAM_BUF_ERR; + goto cleanup; + } + } + + /* we want and have the terminal? */ + if ((announce & LASTLOG_LINE) + && (last_login.ll_line[0] != '\0')) { + /* TRANSLATORS: " on " */ + if (asprintf(&line, _(" on %.*s"), UT_LINESIZE, + last_login.ll_line) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + retval = PAM_BUF_ERR; + goto cleanup; + } + } + + if (date != NULL || host != NULL || line != NULL) + /* TRANSLATORS: "Last login: from on " */ + retval = pam_info(pamh, _("Last login:%s%s%s"), + date ? date : "", + host ? host : "", + line ? line : ""); + } else if (announce & LASTLOG_NEVER) { + D(("this is the first time this user has logged in")); + retval = pam_info(pamh, "%s", _("Welcome to your new account!")); + } + } + + /* cleanup */ + cleanup: + pam_overwrite_object(&last_login); + pam_overwrite_string(date); + pam_overwrite_string(host); + _pam_drop(host); + pam_overwrite_string(line); + _pam_drop(line); + + return retval; +} + +static int +last_login_write(pam_handle_t *pamh, int announce, int last_fd, + uid_t uid, const char *user) +{ + static struct rlimit no_limit = { + RLIM_INFINITY, + RLIM_INFINITY + }; + struct rlimit old_limit; + int setrlimit_res; + struct flock last_lock; + struct lastlog last_login; + int lock_retries = LOCK_RETRIES; + time_t ll_time; + const void *void_remote_host = NULL; + const char *remote_host; + const char *terminal_line; + int retval = PAM_SUCCESS; + + /* rewind */ + if (lseek(last_fd, sizeof(last_login) * (off_t) uid, SEEK_SET) < 0) { + pam_syslog(pamh, LOG_ERR, "failed to lseek %s: %m", _PATH_LASTLOG); + return PAM_SERVICE_ERR; + } + + memset(&last_login, 0, sizeof(last_login)); + + /* set this login date */ + D(("set the most recent login time")); + (void) time(&ll_time); /* set the time */ + last_login.ll_time = ll_time; + + /* set the remote host */ + if (pam_get_item(pamh, PAM_RHOST, &void_remote_host) != PAM_SUCCESS + || void_remote_host == NULL) { + remote_host = DEFAULT_HOST; + } else { + remote_host = void_remote_host; + } + + /* copy to last_login */ + strncat(last_login.ll_host, remote_host, sizeof(last_login.ll_host)-1); + + /* set the terminal line */ + terminal_line = get_tty(pamh); + + /* copy to last_login */ + strncat(last_login.ll_line, terminal_line, sizeof(last_login.ll_line)-1); + terminal_line = NULL; + + D(("locking lastlog file")); + + /* now we try to lock this file-record exclusively; non-blocking */ + memset(&last_lock, 0, sizeof(last_lock)); + last_lock.l_type = F_WRLCK; + last_lock.l_whence = SEEK_SET; + last_lock.l_start = sizeof(last_login) * (off_t) uid; + last_lock.l_len = sizeof(last_login); + + while (fcntl(last_fd, F_SETLK, &last_lock) < 0) { + if (0 == --lock_retries) { + D(("locking %s failed", _PATH_LASTLOG)); + pam_syslog(pamh, LOG_ERR, + "file %s is locked/write", _PATH_LASTLOG); + return PAM_SERVICE_ERR; + } + D(("locking %s failed..(waiting a little)", _PATH_LASTLOG)); + pam_syslog(pamh, LOG_INFO, + "file %s is locked/write, retrying", _PATH_LASTLOG); + sleep(LOCK_RETRY_DELAY); + } + + /* + * Failing to set the 'fsize' limit is not a fatal error. We try to write + * lastlog anyway, under the risk of dying due to a SIGXFSZ. + */ + D(("setting limit for 'fsize'")); + + if ((announce & LASTLOG_UNLIMITED) == 0) { /* don't set to unlimited */ + setrlimit_res = -1; + } else if (getrlimit(RLIMIT_FSIZE, &old_limit) == 0) { + if (old_limit.rlim_cur == RLIM_INFINITY) { /* already unlimited */ + setrlimit_res = -1; + } else { + setrlimit_res = setrlimit(RLIMIT_FSIZE, &no_limit); + if (setrlimit_res != 0) + pam_syslog(pamh, LOG_WARNING, "Could not set limit for 'fsize': %m"); + } + } else { + setrlimit_res = -1; + if (errno == EINVAL) { + pam_syslog(pamh, LOG_INFO, "Limit for 'fsize' not supported: %m"); + } else { + pam_syslog(pamh, LOG_WARNING, "Could not get limit for 'fsize': %m"); + } + } + + D(("writing to the lastlog file")); + if (pam_modutil_write (last_fd, (char *) &last_login, + sizeof (last_login)) != sizeof(last_login)) { + pam_syslog(pamh, LOG_ERR, "failed to write %s: %m", _PATH_LASTLOG); + retval = PAM_SERVICE_ERR; + } + + /* + * Failing to restore the 'fsize' limit is a fatal error. + */ + D(("restoring limit for 'fsize'")); + if (setrlimit_res == 0) { + setrlimit_res = setrlimit(RLIMIT_FSIZE, &old_limit); + if (setrlimit_res != 0) { + pam_syslog(pamh, LOG_ERR, "Could not restore limit for 'fsize': %m"); + retval = PAM_SERVICE_ERR; + } + } + + last_lock.l_type = F_UNLCK; + (void) fcntl(last_fd, F_SETLK, &last_lock); /* unlock */ + D(("unlocked")); + + if (announce & LASTLOG_WTMP) { + /* write wtmp entry for user */ + logwtmp(last_login.ll_line, user, remote_host); + } + + /* cleanup */ + pam_overwrite_object(&last_login); + + return retval; +} + +static int +last_login_date(pam_handle_t *pamh, int announce, uid_t uid, const char *user, time_t *lltime) +{ + int retval; + int last_fd; + + if (uid > get_lastlog_uid_max(pamh)) { + return PAM_SUCCESS; + } + + /* obtain the last login date and all the relevant info */ + last_fd = last_login_open(pamh, announce, uid); + if (last_fd < 0) { + return PAM_SERVICE_ERR; + } + + retval = last_login_read(pamh, announce, last_fd, uid, lltime); + if (retval != PAM_SUCCESS) + { + close(last_fd); + D(("error while reading lastlog file")); + return retval; + } + + if (announce & LASTLOG_UPDATE) { + retval = last_login_write(pamh, announce, last_fd, uid, user); + } + + close(last_fd); + D(("all done with last login")); + + return retval; +} + +static int +last_login_failed(pam_handle_t *pamh, int announce, const char *user, time_t lltime) +{ + int retval; + int fd; + struct utmp ut; + struct utmp utuser; + int failed = 0; + char the_time[256]; + char *date = NULL; + char *host = NULL; + char *line = NULL; + + if (strlen(user) > UT_NAMESIZE) { + pam_syslog(pamh, LOG_WARNING, "username too long, output might be inaccurate"); + } + + /* obtain the failed login attempt records from btmp */ + fd = open(_PATH_BTMP, O_RDONLY); + if (fd < 0) { + int save_errno = errno; + pam_syslog(pamh, LOG_ERR, "unable to open %s: %m", _PATH_BTMP); + D(("unable to open %s file", _PATH_BTMP)); + if (save_errno == ENOENT) + return PAM_SUCCESS; + else + return PAM_SERVICE_ERR; + } + + while ((retval=pam_modutil_read(fd, (void *)&ut, + sizeof(ut))) == sizeof(ut)) { + if (ut.ut_tv.tv_sec >= lltime && strncmp(ut.ut_user, user, UT_NAMESIZE) == 0) { + memcpy(&utuser, &ut, sizeof(utuser)); + failed++; + } + } + + if (retval != 0) + pam_syslog(pamh, LOG_ERR, "corruption detected in %s", _PATH_BTMP); + retval = PAM_SUCCESS; + + if (failed) { + /* we want the date? */ + if (announce & LASTLOG_DATE) { + struct tm *tm, tm_buf; + time_t lf_time; + + lf_time = utuser.ut_tv.tv_sec; + if ((tm = localtime_r (&lf_time, &tm_buf)) != NULL) { + strftime (the_time, sizeof (the_time), + /* TRANSLATORS: "strftime options for date of last login" */ + _(" %a %b %e %H:%M:%S %Z %Y"), tm); + date = the_time; + } + } + + /* we want & have the host? */ + if ((announce & LASTLOG_HOST) + && (utuser.ut_host[0] != '\0')) { + /* TRANSLATORS: " from " */ + if (asprintf(&host, _(" from %.*s"), UT_HOSTSIZE, + utuser.ut_host) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + retval = PAM_BUF_ERR; + goto cleanup; + } + } + + /* we want and have the terminal? */ + if ((announce & LASTLOG_LINE) + && (utuser.ut_line[0] != '\0')) { + /* TRANSLATORS: " on " */ + if (asprintf(&line, _(" on %.*s"), UT_LINESIZE, + utuser.ut_line) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + retval = PAM_BUF_ERR; + goto cleanup; + } + } + + if (line != NULL || date != NULL || host != NULL) { + /* TRANSLATORS: "Last failed login: from on " */ + pam_info(pamh, _("Last failed login:%s%s%s"), + date ? date : "", + host ? host : "", + line ? line : ""); + } + + _pam_drop(line); +#if defined HAVE_DNGETTEXT && defined ENABLE_NLS + retval = asprintf (&line, dngettext(PACKAGE, + "There was %d failed login attempt since the last successful login.", + "There were %d failed login attempts since the last successful login.", + failed), + failed); +#else + if (failed == 1) + retval = asprintf(&line, + _("There was %d failed login attempt since the last successful login."), + failed); + else + retval = asprintf(&line, + /* TRANSLATORS: only used if dngettext is not supported */ + _("There were %d failed login attempts since the last successful login."), + failed); +#endif + if (retval >= 0) + retval = pam_info(pamh, "%s", line); + else { + retval = PAM_BUF_ERR; + line = NULL; + } + } + +cleanup: + free(host); + free(line); + close(fd); + D(("all done with btmp")); + + return retval; +} + +/* --- authentication (locking out inactive users) functions --- */ +static int +pam_auth(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + int retval, ctrl; + const char *user = NULL; + const struct passwd *pwd; + uid_t uid; + time_t lltime = 0; + time_t inactive_days = 0; + int last_fd; + + /* + * Lock out users if they did not login recently enough. + */ + + ctrl = _pam_auth_parse(pamh, flags, argc, argv, &inactive_days); + + /* which user? */ + + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name"); + return PAM_USER_UNKNOWN; + } + + /* what uid? */ + + pwd = pam_modutil_getpwnam (pamh, user); + if (pwd == NULL) { + pam_syslog(pamh, LOG_NOTICE, "user unknown"); + return PAM_USER_UNKNOWN; + } + uid = pwd->pw_uid; + pwd = NULL; /* tidy up */ + + if (uid == 0 || uid > get_lastlog_uid_max(pamh)) + return PAM_SUCCESS; + + /* obtain the last login date and all the relevant info */ + last_fd = last_login_open(pamh, ctrl, uid); + if (last_fd < 0) { + return PAM_IGNORE; + } + + retval = last_login_read(pamh, ctrl|LASTLOG_QUIET, last_fd, uid, &lltime); + close(last_fd); + + if (retval != PAM_SUCCESS) { + D(("error while reading lastlog file")); + return PAM_IGNORE; + } + + if (lltime == 0) { /* user never logged in before */ + if (ctrl & LASTLOG_DEBUG) + pam_syslog(pamh, LOG_DEBUG, "user never logged in - pass"); + return PAM_SUCCESS; + } + + lltime = (time(NULL) - lltime) / (24*60*60); + + if (lltime > inactive_days) { + pam_syslog(pamh, LOG_INFO, "user %s inactive for %ld days - denied", + user, (long) lltime); + return PAM_AUTH_ERR; + } + + return PAM_SUCCESS; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + return pam_auth(pamh, flags, argc, argv); +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + return pam_auth(pamh, flags, argc, argv); +} + +/* --- session management functions --- */ + +int +pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int retval, ctrl; + const void *user; + const struct passwd *pwd; + uid_t uid; + time_t lltime = 0; + + /* + * this module gets the uid of the PAM_USER. Uses it to display + * last login info and then updates the lastlog for that user. + */ + + ctrl = _pam_session_parse(pamh, flags, argc, argv); + + /* which user? */ + + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') { + pam_syslog(pamh, LOG_NOTICE, "user unknown"); + return PAM_USER_UNKNOWN; + } + + /* what uid? */ + + pwd = pam_modutil_getpwnam (pamh, user); + if (pwd == NULL) { + D(("couldn't identify user %s", (const char *) user)); + return PAM_USER_UNKNOWN; + } + uid = pwd->pw_uid; + pwd = NULL; /* tidy up */ + + /* process the current login attempt (indicate last) */ + + retval = last_login_date(pamh, ctrl, uid, user, &lltime); + + if ((ctrl & LASTLOG_BTMP) && retval == PAM_SUCCESS) { + retval = last_login_failed(pamh, ctrl, user, lltime); + } + + /* indicate success or failure */ + + uid = -1; /* forget this */ + + return retval; +} + +int +pam_sm_close_session (pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + const char *terminal_line; + + if (!(_pam_session_parse(pamh, flags, argc, argv) & LASTLOG_WTMP)) + return PAM_SUCCESS; + + terminal_line = get_tty(pamh); + + /* Wipe out utmp logout entry */ + logwtmp(terminal_line, "", ""); + + return PAM_SUCCESS; +} + +/* end of module definition */ diff --git a/modules/pam_lastlog/tst-pam_lastlog b/modules/pam_lastlog/tst-pam_lastlog new file mode 100755 index 0000000..ea9a5eb --- /dev/null +++ b/modules/pam_lastlog/tst-pam_lastlog @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_lastlog.so diff --git a/modules/pam_limits/README.xml b/modules/pam_limits/README.xml new file mode 100644 index 0000000..25a463c --- /dev/null +++ b/modules/pam_limits/README.xml @@ -0,0 +1,23 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_limits.8.xml" xpointer='xpointer(id("pam_limits-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_limits/limits.conf b/modules/pam_limits/limits.conf new file mode 100644 index 0000000..e8a746c --- /dev/null +++ b/modules/pam_limits/limits.conf @@ -0,0 +1,61 @@ +# /etc/security/limits.conf +# +#This file sets the resource limits for the users logged in via PAM. +#It does not affect resource limits of the system services. +# +#Also note that configuration files in /etc/security/limits.d directory, +#which are read in alphabetical order, override the settings in this +#file in case the domain is the same or more specific. +#That means, for example, that setting a limit for wildcard domain here +#can be overridden with a wildcard setting in a config file in the +#subdirectory, but a user specific setting here can be overridden only +#with a user specific setting in the subdirectory. +# +#Each line describes a limit for a user in the form: +# +# +# +#Where: +# can be: +# - a user name +# - a group name, with @group syntax +# - the wildcard *, for default entry +# - the wildcard %, can be also used with %group syntax, +# for maxlogin limit +# +# can have the two values: +# - "soft" for enforcing the soft limits +# - "hard" for enforcing hard limits +# +# can be one of the following: +# - core - limits the core file size (KB) +# - data - max data size (KB) +# - fsize - maximum filesize (KB) +# - memlock - max locked-in-memory address space (KB) +# - nofile - max number of open file descriptors +# - rss - max resident set size (KB) +# - stack - max stack size (KB) +# - cpu - max CPU time (MIN) +# - nproc - max number of processes +# - as - address space limit (KB) +# - maxlogins - max number of logins for this user +# - maxsyslogins - max number of logins on the system +# - priority - the priority to run user process with +# - locks - max number of file locks the user can hold +# - sigpending - max number of pending signals +# - msgqueue - max memory used by POSIX message queues (bytes) +# - nice - max nice priority allowed to raise to values: [-20, 19] +# - rtprio - max realtime priority +# +# +# + +#* soft core 0 +#* hard rss 10000 +#@student hard nproc 20 +#@faculty soft nproc 20 +#@faculty hard nproc 50 +#ftp hard nproc 0 +#@student - maxlogins 4 + +# End of file diff --git a/modules/pam_limits/limits.conf.5.xml b/modules/pam_limits/limits.conf.5.xml new file mode 100644 index 0000000..dd8d68b --- /dev/null +++ b/modules/pam_limits/limits.conf.5.xml @@ -0,0 +1,353 @@ + + + + limits.conf + 5 + Linux-PAM + Linux-PAM Manual + + + + limits.conf + configuration file for the pam_limits module + + + + DESCRIPTION + + The pam_limits.so module applies ulimit limits, + nice priority and number of simultaneous login sessions limit to user + login sessions. This description of the configuration file syntax + applies to the /etc/security/limits.conf file and + *.conf files in the + /etc/security/limits.d directory. + + + The syntax of the lines is as follows: + + + <domain> <type> + <item> <value> + + + The fields listed above should be filled as follows: + + + + + <domain> + + + + + + a username + + + + + a groupname, with @group syntax. + This should not be confused with netgroups. + + + + + the wildcard *, for default entry. + + + + + the wildcard %, for maxlogins limit only, + can also be used with %group syntax. If the + % wildcard is used alone it is identical + to using * with maxsyslogins limit. With + a group specified after % it limits the total + number of logins of all users that are member of the group. + + + + + an uid range specified as <min_uid>:<max_uid>. If min_uid + is omitted, the match is exact for the max_uid. If max_uid is omitted, all + uids greater than or equal min_uid match. + + + + + a gid range specified as @<min_gid>:<max_gid>. If min_gid + is omitted, the match is exact for the max_gid. If max_gid is omitted, all + gids greater than or equal min_gid match. For the exact match all groups including + the user's supplementary groups are examined. For the range matches only + the user's primary group is examined. + + + + + a gid specified as %:<gid> applicable + to maxlogins limit only. It limits the total number of logins of all users + that are member of the group with the specified gid. + + + + + + + + + <type> + + + + + hard + + + for enforcing hard resource limits. + These limits are set by the superuser and enforced by the Kernel. + Users cannot raise their own requirement of system resources above such values. + + + + + soft + + + for enforcing soft resource limits. + These limits are ones that the user can move up or down within the + permitted range by any pre-existing hard + limits. The values specified with this token can be thought of as + default values, for normal system usage. + + + + + - + + + for enforcing both soft and + hard resource limits together. + + + Note, if you specify a type of '-' but neglect to supply the + item and value fields then the module will never enforce any + limits on the specified user/group etc. . + + + + + + + + + + <item> + + + + + core + + limits the core file size (KB) + + + + data + + maximum data size (KB) + + + + fsize + + maximum filesize (KB) + + + + memlock + + maximum locked-in-memory address space (KB) + + + + nofile + + maximum number of open file descriptors + + + + rss + + maximum resident set size (KB) (Ignored in Linux 2.4.30 and higher) + + + + stack + + maximum stack size (KB) + + + + cpu + + maximum CPU time (minutes) + + + + nproc + + maximum number of processes + + + + as + + address space limit (KB) + + + + maxlogins + + maximum number of logins for this user (this limit does + not apply to user with uid=0) + + + + maxsyslogins + + maximum number of all logins on system; user is not + allowed to log-in if total number of all user logins is + greater than specified number (this limit does not apply to + user with uid=0) + + + + nonewprivs + + value of 0 or 1; if set to 1 disables acquiring new + privileges by invoking prctl(PR_SET_NO_NEW_PRIVS) + + + + priority + + the priority to run user process with (negative + values boost process priority) + + + + locks + + maximum locked files (Linux 2.4 and higher) + + + + sigpending + + maximum number of pending signals (Linux 2.6 and higher) + + + + msgqueue + + maximum memory used by POSIX message queues (bytes) + (Linux 2.6 and higher) + + + + nice + + maximum nice priority allowed to raise to (Linux 2.6.12 and higher) values: [-20,19] + + + + rtprio + + maximum realtime priority allowed for non-privileged processes + (Linux 2.6.12 and higher) + + + + + + + + + All items support the values -1, + unlimited or infinity indicating no limit, + except for priority, nice, + and nonewprivs. + If nofile is to be set to one of these values, + it will be set to the contents of /proc/sys/fs/nr_open instead (see setrlimit(3)). + + + If a hard limit or soft limit of a resource is set to a valid value, + but outside of the supported range of the local system, the system + may reject the new limit or unexpected behavior may occur. If the + control value required is used, the module will + reject the login if a limit could not be set. + + + In general, individual limits have priority over group limits, so if + you impose no limits for admin group, but one of + the members in this group have a limits line, the user will have its + limits set according to this line. + + + Also, please note that all limit settings are set + per login. They are not global, nor are they + permanent; existing only for the duration of the session. + One exception is the maxlogin option, this one + is system wide. But there is a race, concurrent logins at the same + time will not always be detect as such but only counted as one. + + + In the limits configuration file, the + '#' character introduces a comment + - after which the rest of the line is ignored. + + + The pam_limits module does report configuration problems + found in its configuration file and errors via + syslog3. + + + + + EXAMPLES + + These are some example lines which might be specified in + /etc/security/limits.conf. + + +* soft core 0 +* hard nofile 512 +@student hard nproc 20 +@faculty soft nproc 20 +@faculty hard nproc 50 +ftp hard nproc 0 +@student - maxlogins 4 +@student - nonewprivs 1 +:123 hard cpu 5000 +@500: soft cpu 10000 +600:700 hard locks 10 + + + + + SEE ALSO + + pam_limits8, + pam.d5, + pam8, + getrlimit2, + getrlimit3p + + + + + AUTHOR + + pam_limits was initially written by Cristian Gafton <gafton@redhat.com> + + + diff --git a/modules/pam_limits/meson.build b/modules/pam_limits/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_limits/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_limits/pam_limits.8.xml b/modules/pam_limits/pam_limits.8.xml new file mode 100644 index 0000000..cca046c --- /dev/null +++ b/modules/pam_limits/pam_limits.8.xml @@ -0,0 +1,278 @@ + + + + pam_limits + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_limits + + PAM module to limit resources + + + + + + + + pam_limits.so + + conf=/path/to/limits.conf + + + debug + + + set_all + + + utmp_early + + + noaudit + + + + + + + DESCRIPTION + + The pam_limits PAM module sets limits on the system resources that can be + obtained in a user-session. Users of uid=0 are affected + by this limits, too. + + + By default limits are taken from the /etc/security/limits.conf + config file. Then individual *.conf files from the /etc/security/limits.d/ + directory are read. The files are parsed one after another in the order of "C" locale. + The effect of the individual files is the same as if all the files were + concatenated together in the order of parsing. + If a config file is explicitly specified with a module option then the + files in the above directory are not parsed. + + + By default limits are taken from the /etc/security/limits.conf + config file or, if that one is not present, the file + %vendordir%/security/limits.conf. + Then individual *.conf files from the + /etc/security/limits.d/ and + %vendordir%/security/limits.d directories are read. + If /etc/security/limits.d/@filename@.conf exists, then + %vendordir%/security/limits.d/@filename@.conf will not be used. + All limits.d/*.conf files are sorted by their + @filename@.conf in lexicographic order regardless of which + of the directories they reside in. + The effect of the individual files is the same as if all the files were + concatenated together in the order of parsing. + If a config file is explicitly specified with the + option the files in the above directories are not parsed. + + + The module must not be called by a multithreaded application. + + + If Linux PAM is compiled with audit support the module will report + when it denies access based on limit of maximum number of concurrent + login sessions. + + + + + OPTIONS + + + + conf=/path/to/limits.conf + + + + Indicate an alternative limits.conf style configuration file to + override the default. + + + + + + debug + + + + Print debug information. + + + + + + set_all + + + + Set the limits for which no value is specified in the + configuration file to the one from the process with the + PID 1. Please note that if the init process is systemd + these limits will not be the kernel default limits and + this option should not be used. + + + + + + utmp_early + + + + Some broken applications actually allocate a utmp entry for + the user before the user is admitted to the system. If some + of the services you are configuring PAM for do this, you can + selectively use this module argument to compensate for this + behavior and at the same time maintain system-wide consistency + with a single limits.conf file. + + + + + + noaudit + + + + Do not report exceeded maximum logins count to the audit subsystem. + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_ABORT + + + Cannot get current limits. + + + + + PAM_IGNORE + + + No limits found for this user. + + + + + PAM_PERM_DENIED + + + New limits could not be set. + + + + + PAM_SERVICE_ERR + + + Cannot read config file. + + + + + PAM_SESSION_ERR + + + Error recovering account name. + + + + + PAM_SUCCESS + + + Limits were changed. + + + + + PAM_USER_UNKNOWN + + + The user is not known to the system. + + + + + + + + FILES + + + /etc/security/limits.conf + + Default configuration file + + + + %vendordir%/security/limits.conf + + Default configuration file if + /etc/security/limits.conf does not exist. + + + + + + + EXAMPLES + + For the services you need resources limits (login for example) put a + the following line in /etc/pam.d/login as the last + line for that service (usually after the pam_unix session line): + + +#%PAM-1.0 +# +# Resource limits imposed on login sessions via pam_limits +# +session required pam_limits.so + + + Replace "login" for each service you are using this module. + + + + + SEE ALSO + + + limits.conf5 + , + + pam.d5 + , + + pam8 + . + + + + + AUTHORS + + pam_limits was initially written by Cristian Gafton <gafton@redhat.com> + + + \ No newline at end of file diff --git a/modules/pam_limits/pam_limits.c b/modules/pam_limits/pam_limits.c new file mode 100644 index 0000000..1e4dfa3 --- /dev/null +++ b/modules/pam_limits/pam_limits.c @@ -0,0 +1,1475 @@ +/* + * pam_limits - impose resource limits when opening a user session + * + * 1.6 - modified for PLD (added process priority settings) + * by Marcin Korzonek + * 1.5 - Elliot Lee's "max system logins patch" + * 1.4 - addressed bug in configuration file parser + * 1.3 - modified the configuration file format + * 1.2 - added 'debug' and 'conf=' arguments + * 1.1 - added @group support + * 1.0 - initial release - Linux ONLY + * + * See end for Copyright information + */ + +#ifndef __linux__ +#warning THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!! +#endif + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include +#include +#include +#include +#include +#ifdef USE_LOGIND +#include +#else +#include +#endif + +#ifndef UT_USER /* some systems have ut_name instead of ut_user */ +#define UT_USER ut_user +#endif + +#include +#include +#include + +#ifdef HAVE_LIBAUDIT +#include +#endif + +#ifndef PR_SET_NO_NEW_PRIVS +# define PR_SET_NO_NEW_PRIVS 38 /* from */ +#endif + +/* Module defines */ +#define LIMITS_DEF_USER 0 /* limit was set by a user entry */ +#define LIMITS_DEF_GROUP 1 /* limit was set by a group entry */ +#define LIMITS_DEF_ALLGROUP 2 /* limit was set by a group entry */ +#define LIMITS_DEF_ALL 3 /* limit was set by an all entry */ +#define LIMITS_DEF_DEFAULT 4 /* limit was set by a default entry */ +#define LIMITS_DEF_KERNEL 5 /* limit was set from /proc/1/limits */ +#define LIMITS_DEF_NONE 6 /* this limit was not set yet */ + +#define LIMIT_RANGE_ERR (-1) /* error in specified uid/gid range */ +#define LIMIT_RANGE_NONE 0 /* no range specified */ +#define LIMIT_RANGE_ONE 1 /* exact uid/gid specified (:max_uid)*/ +#define LIMIT_RANGE_MIN 2 /* only minimum uid/gid specified (min_uid:) */ +#define LIMIT_RANGE_MM 3 /* both min and max uid/gid specified (min_uid:max_uid) */ + +static const char *const limits_def_names[] = { + "USER", + "GROUP", + "ALLGROUP", + "ALL", + "DEFAULT", + "KERNEL", + "NONE", + NULL +}; + +struct user_limits_struct { + int supported; + int src_soft; + int src_hard; + struct rlimit limit; +}; + +/* internal data */ +struct pam_limit_s { + int login_limit; /* the max logins limit */ + int login_limit_def; /* which entry set the login limit */ + int flag_numsyslogins; /* whether to limit logins only for a + specific user or to count all logins */ + int priority; /* the priority to run user process with */ + int nonewprivs; /* whether to prctl(PR_SET_NO_NEW_PRIVS) */ + struct user_limits_struct limits[RLIM_NLIMITS]; + const char *conf_file; + int utmp_after_pam_call; + char *login_group; +}; + +#define LIMIT_LOGIN (RLIM_NLIMITS+1) +#define LIMIT_NUMSYSLOGINS (RLIM_NLIMITS+2) + +#define LIMIT_PRI (RLIM_NLIMITS+3) +#define LIMIT_NONEWPRIVS (RLIM_NLIMITS+4) + +#define LIMIT_SOFT 1 +#define LIMIT_HARD 2 + +#include +#include +#include +#include +#include "pam_inline.h" +#include "pam_i18n.h" + +/* argument parsing */ + +#define PAM_DEBUG_ARG 0x0001 +#define PAM_UTMP_EARLY 0x0004 +#define PAM_NO_AUDIT 0x0008 +#define PAM_SET_ALL 0x0010 + +/* Limits from globbed files. */ +#define LIMITS_CONF_GLOB (LIMITS_FILE_DIR "/*.conf") + +#define LIMITS_FILE (SCONFIG_DIR "/limits.conf") + +#ifdef VENDOR_SCONFIG_DIR +#define VENDOR_LIMITS_FILE (VENDOR_SCONFIG_DIR "/limits.conf") +#define VENDOR_LIMITS_CONF_GLOB (VENDOR_SCONFIG_DIR "/limits.d/*.conf") +#endif + +static int +_pam_parse (const pam_handle_t *pamh, int argc, const char **argv, + struct pam_limit_s *pl) +{ + int ctrl=0; + + /* step through arguments */ + for (ctrl=0; argc-- > 0; ++argv) { + const char *str; + + /* generic options */ + + if (!strcmp(*argv,"debug")) { + ctrl |= PAM_DEBUG_ARG; + } else if ((str = pam_str_skip_prefix(*argv, "conf=")) != NULL) { + pl->conf_file = str; + } else if (!strcmp(*argv,"utmp_early")) { + ctrl |= PAM_UTMP_EARLY; + } else if (!strcmp(*argv,"noaudit")) { + ctrl |= PAM_NO_AUDIT; + } else if (!strcmp(*argv,"set_all")) { + ctrl |= PAM_SET_ALL; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + return ctrl; +} + +static const char * +rlimit2str (int i) +{ + switch (i) { + case RLIMIT_CPU: + return "cpu"; + break; + case RLIMIT_FSIZE: + return "fsize"; + break; + case RLIMIT_DATA: + return "data"; + break; + case RLIMIT_STACK: + return "stack"; + break; + case RLIMIT_CORE: + return "core"; + break; + case RLIMIT_RSS: + return "rss"; + break; + case RLIMIT_NPROC: + return "nproc"; + break; + case RLIMIT_NOFILE: + return "nofile"; + break; + case RLIMIT_MEMLOCK: + return "memlock"; + break; +#ifdef RLIMIT_AS + case RLIMIT_AS: + return "as"; + break; +#endif +#ifdef RLIMIT_LOCKS + case RLIMIT_LOCKS: + return "locks"; + break; +#endif +#ifdef RLIMIT_SIGPENDING + case RLIMIT_SIGPENDING: + return "sigpending"; + break; +#endif +#ifdef RLIMIT_MSGQUEUE + case RLIMIT_MSGQUEUE: + return "msgqueue"; + break; +#endif +#ifdef RLIMIT_NICE + case RLIMIT_NICE: + return "nice"; + break; +#endif +#ifdef RLIMIT_RTPRIO + case RLIMIT_RTPRIO: + return "rtprio"; + break; +#endif + default: + return "UNKNOWN"; + break; + } +} + + +#define LIMITED_OK 0 /* limit setting appeared to work */ +#define LIMIT_ERR 1 /* error setting a limit */ +#define LOGIN_ERR 2 /* too many logins err */ + +/* Counts the number of user logins and check against the limit*/ +static int +check_logins (pam_handle_t *pamh, const char *name, int limit, int ctrl, + struct pam_limit_s *pl) +{ + int count; + + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, + "checking logins for '%s' (maximum of %d)", name, limit); + } + + if (limit < 0) + return 0; /* no limits imposed */ + if (limit == 0) /* maximum 0 logins ? */ { + pam_syslog(pamh, LOG_WARNING, "No logins allowed for '%s'", name); + return LOGIN_ERR; + } + + /* Because there is no definition about when an application + actually adds a utmp entry, some applications bizarrely do the + utmp call before the have PAM authenticate them to the system: + you're logged it, sort of...? Anyway, you can use the + "utmp_early" module argument in your PAM config file to make + allowances for this sort of problem. (There should be a PAM + standard for this, since if a module wants to actually map a + username then any early utmp entry will be for the unmapped + name = broken.) */ + + if (ctrl & PAM_UTMP_EARLY) { + count = 0; + } else { + count = 1; + } + +#ifdef USE_LOGIND + char **sessions_list; + int sessions = sd_get_sessions(&sessions_list); + + /* maxlogins needs to be 2 with systemd-logind because + of the systemd --user process started with first login by + pam_systemd. + Which is also calling pam_limits, but in this very first special + case the session does already exist and is counted twice. + With start of the second session, session manager is already running + and no longer counted. */ + if (limit == 1) { + pam_syslog(pamh, LOG_WARNING, "Maxlogin limit needs to be 2 or higher with systemd-logind"); + return LIMIT_ERR; + } + + if (sessions < 0) { + pam_syslog(pamh, LOG_ERR, "logind error getting session list: %s", + strerror(-sessions)); + return LIMIT_ERR; + } else if (sessions > 0 && sessions_list != NULL && !pl->flag_numsyslogins) { + int i; + + for (i = 0; i < sessions; i++) { + char *user = NULL; + char *class = NULL; + + if (sd_session_get_class(sessions_list[i], &class) < 0 || class == NULL) + continue; + + if (strncmp(class, "user", 4) != 0) { /* user, user-early, user-incomplete */ + free (class); + continue; + } + free (class); + + if (sd_session_get_username(sessions_list[i], &user) < 0 || user == NULL) { + pam_syslog(pamh, LOG_ERR, "logind error getting username: %s", + strerror(-sessions)); + return LIMIT_ERR; + } + + if (((pl->login_limit_def == LIMITS_DEF_USER) + || (pl->login_limit_def == LIMITS_DEF_GROUP) + || (pl->login_limit_def == LIMITS_DEF_DEFAULT)) + && strcmp(name, user) != 0) { + free(user); + continue; + } + if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP) + && pl->login_group != NULL + && !pam_modutil_user_in_group_nam_nam(pamh, user, pl->login_group)) { + free(user); + continue; + } + free(user); + + if (++count > limit) { + break; + } + } + for (i = 0; i < sessions; i++) + free(sessions_list[i]); + free(sessions_list); + } else { + count = sessions; + } +#else + struct utmp *ut; + + setutent(); + + while((ut = getutent())) { +#ifdef USER_PROCESS + if (ut->ut_type != USER_PROCESS) { + continue; + } +#endif + if (ut->UT_USER[0] == '\0') { + continue; + } + if (!pl->flag_numsyslogins) { + char user[sizeof(ut->UT_USER) + 1]; + memcpy(user, ut->UT_USER, sizeof(ut->UT_USER)); + user[sizeof(ut->UT_USER)] = '\0'; + + if (((pl->login_limit_def == LIMITS_DEF_USER) + || (pl->login_limit_def == LIMITS_DEF_GROUP) + || (pl->login_limit_def == LIMITS_DEF_DEFAULT)) + && strcmp(name, user) != 0) { + continue; + } + if ((pl->login_limit_def == LIMITS_DEF_ALLGROUP) + && pl->login_group != NULL + && !pam_modutil_user_in_group_nam_nam(pamh, user, pl->login_group)) { + continue; + } + if (kill(ut->ut_pid, 0) == -1 && errno == ESRCH) { + /* process does not exist anymore */ + pam_syslog(pamh, LOG_INFO, + "Stale utmp entry (pid %d) for '%s' ignored", + ut->ut_pid, user); + continue; + } + } + if (++count > limit) { + break; + } + } + endutent(); +#endif + if (count > limit) { + if (name) { + pam_syslog(pamh, LOG_NOTICE, + "Too many logins (max %d) for %s", limit, name); + } else { + pam_syslog(pamh, LOG_NOTICE, "Too many system logins (max %d)", limit); + } + return LOGIN_ERR; + } + return 0; +} + +#ifdef __linux__ +static const char *const lnames[RLIM_NLIMITS] = { + [RLIMIT_CPU] = "Max cpu time", + [RLIMIT_FSIZE] = "Max file size", + [RLIMIT_DATA] = "Max data size", + [RLIMIT_STACK] = "Max stack size", + [RLIMIT_CORE] = "Max core file size", + [RLIMIT_RSS] = "Max resident set", + [RLIMIT_NPROC] = "Max processes", + [RLIMIT_NOFILE] = "Max open files", + [RLIMIT_MEMLOCK] = "Max locked memory", +#ifdef RLIMIT_AS + [RLIMIT_AS] = "Max address space", +#endif +#ifdef RLIMIT_LOCKS + [RLIMIT_LOCKS] = "Max file locks", +#endif +#ifdef RLIMIT_SIGPENDING + [RLIMIT_SIGPENDING] = "Max pending signals", +#endif +#ifdef RLIMIT_MSGQUEUE + [RLIMIT_MSGQUEUE] = "Max msgqueue size", +#endif +#ifdef RLIMIT_NICE + [RLIMIT_NICE] = "Max nice priority", +#endif +#ifdef RLIMIT_RTPRIO + [RLIMIT_RTPRIO] = "Max realtime priority", +#endif +#ifdef RLIMIT_RTTIME + [RLIMIT_RTTIME] = "Max realtime timeout", +#endif +}; + +static int str2rlimit(char *name) { + int i; + if (!name || *name == '\0') + return -1; + for(i = 0; i < RLIM_NLIMITS; i++) { + if (strcmp(name, lnames[i]) == 0) return i; + } + return -1; +} + +static rlim_t str2rlim_t(char *value) { + unsigned long long rlimit = 0; + + if (!value) return (rlim_t)rlimit; + if (strcmp(value, "unlimited") == 0) { + return RLIM_INFINITY; + } + rlimit = strtoull(value, NULL, 10); + return (rlim_t)rlimit; +} + +#define LIMITS_SKIP_WHITESPACE { \ + /* step backwards over spaces */ \ + pos--; \ + while (pos && line[pos] == ' ') pos--; \ + if (!pos) continue; \ + line[pos+1] = '\0'; \ +} +#define LIMITS_MARK_ITEM(item) { \ + /* step backwards over non-spaces */ \ + pos--; \ + while (pos && line[pos] != ' ') pos--; \ + if (!pos) continue; \ + (item) = line + pos + 1; \ +} + +static void parse_kernel_limits(pam_handle_t *pamh, struct pam_limit_s *pl, int ctrl) +{ + int i; + FILE *limitsfile; + const char *proclimits = "/proc/1/limits"; + char *line = NULL; + size_t maxlen = 0, n = 0; + char *hard, *soft, *name; + + if (!(limitsfile = fopen(proclimits, "r"))) { + pam_syslog(pamh, LOG_WARNING, "Could not read %s (%s), using PAM defaults", proclimits, strerror(errno)); + return; + } + + while (getline(&line, &n, limitsfile) != -1) { + size_t pos = strlen(line); + if (pos < 2) continue; + + /* drop trailing newline */ + if (line[pos-1] == '\n') { + pos--; + line[pos] = '\0'; + } + + /* determine formatting boundary of limits report */ + if (!maxlen && pam_str_skip_prefix(line, "Limit") != NULL) { + maxlen = pos; + continue; + } + + if (pos == maxlen) { + /* step backwards over "Units" name */ + LIMITS_SKIP_WHITESPACE; + LIMITS_MARK_ITEM(hard); /* not a typo, units unused */ + } + + /* step backwards over "Hard Limit" value */ + LIMITS_SKIP_WHITESPACE; + LIMITS_MARK_ITEM(hard); + + /* step backwards over "Soft Limit" value */ + LIMITS_SKIP_WHITESPACE; + LIMITS_MARK_ITEM(soft); + + /* step backwards over name of limit */ + LIMITS_SKIP_WHITESPACE; + name = line; + + i = str2rlimit(name); + if (i < 0 || i >= RLIM_NLIMITS) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_DEBUG, "Unknown kernel rlimit '%s' ignored", name); + continue; + } + pl->limits[i].limit.rlim_cur = str2rlim_t(soft); + pl->limits[i].limit.rlim_max = str2rlim_t(hard); + pl->limits[i].src_soft = LIMITS_DEF_KERNEL; + pl->limits[i].src_hard = LIMITS_DEF_KERNEL; + } + free(line); + fclose(limitsfile); +} +#endif + +static int init_limits(pam_handle_t *pamh, struct pam_limit_s *pl, int ctrl) +{ + int i; + int retval = PAM_SUCCESS; + + D(("called.")); + + for(i = 0; i < RLIM_NLIMITS; i++) { + int r = getrlimit(i, &pl->limits[i].limit); + if (r == -1) { + pl->limits[i].supported = 0; + if (errno != EINVAL) { + retval = !PAM_SUCCESS; + } + } else { + pl->limits[i].supported = 1; + pl->limits[i].src_soft = LIMITS_DEF_NONE; + pl->limits[i].src_hard = LIMITS_DEF_NONE; + } + } + +#ifdef __linux__ + if (ctrl & PAM_SET_ALL) { + parse_kernel_limits(pamh, pl, ctrl); + + for(i = 0; i < RLIM_NLIMITS; i++) { + if (pl->limits[i].supported && + (pl->limits[i].src_soft == LIMITS_DEF_NONE || + pl->limits[i].src_hard == LIMITS_DEF_NONE)) { + pam_syslog(pamh, LOG_WARNING, "Did not find kernel RLIMIT for %s, using PAM default", rlimit2str(i)); + } + } + } +#endif + + errno = 0; + pl->priority = getpriority (PRIO_PROCESS, 0); + if (pl->priority == -1 && errno != 0) + retval = !PAM_SUCCESS; + pl->login_limit = -2; + pl->login_limit_def = LIMITS_DEF_NONE; + pl->login_group = NULL; + + return retval; +} + +/* + * Read the contents of and return it in *valuep + * return 1 if conversion succeeds, result is in *valuep + * return 0 if conversion fails, *valuep is untouched. + */ +static int +value_from_file(const char *pathname, rlim_t *valuep) +{ + FILE *fp; + int retval; + + retval = 0; + + if ((fp = fopen(pathname, "r")) != NULL) { + char *buf = NULL; + size_t n = 0; + + if (getline(&buf, &n, fp) != -1) { + char *endptr; + unsigned long long value; + + errno = 0; + value = strtoull(buf, &endptr, 10); + if (endptr != buf && + (value != ULLONG_MAX || errno == 0) && + (unsigned long long) (rlim_t) value == value) { + *valuep = (rlim_t) value; + retval = 1; + } + } + + free(buf); + fclose(fp); + } + + return retval; +} + +static void +process_limit (const pam_handle_t *pamh, int source, const char *lim_type, + const char *lim_item, const char *lim_value, + int ctrl, struct pam_limit_s *pl) +{ + int limit_item; + int limit_type = 0; + int int_value = 0; + rlim_t rlimit_value = 0; + char *endptr; + const char *value_orig = lim_value; + + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_DEBUG, "%s: processing %s %s %s for %s", + __FUNCTION__, lim_type, lim_item, lim_value, + limits_def_names[source]); + + if (strcmp(lim_item, "cpu") == 0) + limit_item = RLIMIT_CPU; + else if (strcmp(lim_item, "fsize") == 0) + limit_item = RLIMIT_FSIZE; + else if (strcmp(lim_item, "data") == 0) + limit_item = RLIMIT_DATA; + else if (strcmp(lim_item, "stack") == 0) + limit_item = RLIMIT_STACK; + else if (strcmp(lim_item, "core") == 0) + limit_item = RLIMIT_CORE; + else if (strcmp(lim_item, "rss") == 0) + limit_item = RLIMIT_RSS; + else if (strcmp(lim_item, "nproc") == 0) + limit_item = RLIMIT_NPROC; + else if (strcmp(lim_item, "nofile") == 0) + limit_item = RLIMIT_NOFILE; + else if (strcmp(lim_item, "memlock") == 0) + limit_item = RLIMIT_MEMLOCK; +#ifdef RLIMIT_AS + else if (strcmp(lim_item, "as") == 0) + limit_item = RLIMIT_AS; +#endif /*RLIMIT_AS*/ +#ifdef RLIMIT_LOCKS + else if (strcmp(lim_item, "locks") == 0) + limit_item = RLIMIT_LOCKS; +#endif +#ifdef RLIMIT_SIGPENDING + else if (strcmp(lim_item, "sigpending") == 0) + limit_item = RLIMIT_SIGPENDING; +#endif +#ifdef RLIMIT_MSGQUEUE + else if (strcmp(lim_item, "msgqueue") == 0) + limit_item = RLIMIT_MSGQUEUE; +#endif +#ifdef RLIMIT_NICE + else if (strcmp(lim_item, "nice") == 0) + limit_item = RLIMIT_NICE; +#endif +#ifdef RLIMIT_RTPRIO + else if (strcmp(lim_item, "rtprio") == 0) + limit_item = RLIMIT_RTPRIO; +#endif + else if (strcmp(lim_item, "maxlogins") == 0) { + limit_item = LIMIT_LOGIN; + pl->flag_numsyslogins = 0; + } else if (strcmp(lim_item, "maxsyslogins") == 0) { + limit_item = LIMIT_NUMSYSLOGINS; + pl->flag_numsyslogins = 1; + } else if (strcmp(lim_item, "priority") == 0) { + limit_item = LIMIT_PRI; + } else if (strcmp(lim_item, "nonewprivs") == 0) { + limit_item = LIMIT_NONEWPRIVS; + } else { + pam_syslog(pamh, LOG_DEBUG, "unknown limit item '%s'", lim_item); + return; + } + + if (strcmp(lim_type,"soft")==0) + limit_type=LIMIT_SOFT; + else if (strcmp(lim_type, "hard")==0) + limit_type=LIMIT_HARD; + else if (strcmp(lim_type,"-")==0) + limit_type=LIMIT_SOFT | LIMIT_HARD; + else if (limit_item != LIMIT_LOGIN && limit_item != LIMIT_NUMSYSLOGINS + && limit_item != LIMIT_NONEWPRIVS) { + pam_syslog(pamh, LOG_DEBUG, "unknown limit type '%s'", lim_type); + return; + } + if (limit_item == LIMIT_NONEWPRIVS) { + /* just require a bool-style 0 or 1 */ + if (strcmp(lim_value, "0") == 0) { + int_value = 0; + } else if (strcmp(lim_value, "1") == 0) { + int_value = 1; + } else { + pam_syslog(pamh, LOG_DEBUG, + "wrong limit value '%s' for limit type '%s'", + lim_value, lim_type); + } + } else if (limit_item != LIMIT_PRI +#ifdef RLIMIT_NICE + && limit_item != RLIMIT_NICE +#endif + && (strcmp(lim_value, "-1") == 0 + || strcmp(lim_value, "-") == 0 || strcmp(lim_value, "unlimited") == 0 + || strcmp(lim_value, "infinity") == 0)) { + int_value = -1; + rlimit_value = RLIM_INFINITY; + } else if (limit_item == LIMIT_PRI || limit_item == LIMIT_LOGIN || +#ifdef RLIMIT_NICE + limit_item == RLIMIT_NICE || +#endif + limit_item == LIMIT_NUMSYSLOGINS) { + long temp; + temp = strtol (lim_value, &endptr, 10); + temp = temp < INT_MAX ? temp : INT_MAX; + int_value = temp > INT_MIN ? temp : INT_MIN; + if (int_value == 0 && value_orig == endptr) { + pam_syslog(pamh, LOG_DEBUG, + "wrong limit value '%s' for limit type '%s'", + lim_value, lim_type); + return; + } + } else { +#ifdef __USE_FILE_OFFSET64 + rlimit_value = strtoull (lim_value, &endptr, 10); +#else + rlimit_value = strtoul (lim_value, &endptr, 10); +#endif + if (rlimit_value == 0 && value_orig == endptr) { + pam_syslog(pamh, LOG_DEBUG, + "wrong limit value '%s' for limit type '%s'", + lim_value, lim_type); + return; + } + } + + /* one more special case when limiting logins */ + if ((source == LIMITS_DEF_ALL || source == LIMITS_DEF_ALLGROUP) + && (limit_item != LIMIT_LOGIN)) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_DEBUG, + "'%%' domain valid for maxlogins type only"); + return; + } + + switch(limit_item) { + case RLIMIT_CPU: + if (rlimit_value != RLIM_INFINITY) + { + if (rlimit_value >= RLIM_INFINITY/60) + rlimit_value = RLIM_INFINITY; + else + rlimit_value *= 60; + } + break; + case RLIMIT_FSIZE: + case RLIMIT_DATA: + case RLIMIT_STACK: + case RLIMIT_CORE: + case RLIMIT_RSS: + case RLIMIT_MEMLOCK: +#ifdef RLIMIT_AS + case RLIMIT_AS: +#endif + if (rlimit_value != RLIM_INFINITY) + { + if (rlimit_value >= RLIM_INFINITY/1024) + rlimit_value = RLIM_INFINITY; + else + rlimit_value *= 1024; + } + break; +#ifdef RLIMIT_NICE + case RLIMIT_NICE: + if (int_value > 19) + int_value = 19; + if (int_value < -20) + int_value = -20; + rlimit_value = 20 - int_value; + break; +#endif + case RLIMIT_NOFILE: + /* + * If nofile is to be set to "unlimited", try to set it to + * the value in /proc/sys/fs/nr_open instead. + */ + if (rlimit_value == RLIM_INFINITY) { + if (!value_from_file("/proc/sys/fs/nr_open", &rlimit_value)) + pam_syslog(pamh, LOG_WARNING, + "Cannot set \"nofile\" to a sensible value"); + else if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_DEBUG, "Setting \"nofile\" limit to %llu", + (unsigned long long) rlimit_value); + } + break; + } + + if ( (limit_item != LIMIT_LOGIN) + && (limit_item != LIMIT_NUMSYSLOGINS) + && (limit_item != LIMIT_PRI) + && (limit_item != LIMIT_NONEWPRIVS) ) { + if (limit_type & LIMIT_SOFT) { + if (pl->limits[limit_item].src_soft < source) { + return; + } else { + pl->limits[limit_item].limit.rlim_cur = rlimit_value; + pl->limits[limit_item].src_soft = source; + } + } + if (limit_type & LIMIT_HARD) { + if (pl->limits[limit_item].src_hard < source) { + return; + } else { + pl->limits[limit_item].limit.rlim_max = rlimit_value; + pl->limits[limit_item].src_hard = source; + } + } + } else { + /* recent kernels support negative priority limits (=raise priority) */ + + if (limit_item == LIMIT_PRI) { + pl->priority = int_value; + } else if (limit_item == LIMIT_NONEWPRIVS) { + pl->nonewprivs = int_value; + } else { + if (pl->login_limit_def < source) { + return; + } else { + pl->login_limit = int_value; + pl->login_limit_def = source; + } + } + } +} + +static int +parse_uid_range(pam_handle_t *pamh, const char *domain, + uid_t *min_uid, uid_t *max_uid) +{ + const char *range = domain; + char *pmax; + char *endptr; + int rv = LIMIT_RANGE_MM; + + if ((pmax=strchr(range, ':')) == NULL) + return LIMIT_RANGE_NONE; + ++pmax; + + if (range[0] == '@' || range[0] == '%') + ++range; + + if (range[0] == ':') + rv = LIMIT_RANGE_ONE; + else { + errno = 0; + *min_uid = strtoul (range, &endptr, 10); + if (errno != 0 || (range == endptr) || *endptr != ':') { + pam_syslog(pamh, LOG_DEBUG, + "wrong min_uid/gid value in '%s'", domain); + return LIMIT_RANGE_ERR; + } + } + + if (*pmax == '\0') { + if (rv == LIMIT_RANGE_ONE) + return LIMIT_RANGE_ERR; + else + return LIMIT_RANGE_MIN; + } + + errno = 0; + *max_uid = strtoul (pmax, &endptr, 10); + if (errno != 0 || (pmax == endptr) || *endptr != '\0') { + pam_syslog(pamh, LOG_DEBUG, + "wrong max_uid/gid value in '%s'", domain); + return LIMIT_RANGE_ERR; + } + + if (rv == LIMIT_RANGE_ONE) + *min_uid = *max_uid; + return rv; +} + +static int +set_if_null(char **dest, char *def) +{ + if (*dest == NULL) { + *dest = def; + return 0; + } + return 1; +} + +static char * +trim(char *s) +{ + char *p; + + if (s == NULL) + return NULL; + + while (*s == ' ' || *s == '\t') + s++; + + if (*s == '\0') + return NULL; + + p = s + strlen(s) - 1; + while (p >= s && (*p == ' ' || *p == '\t')) + *p-- = '\0'; + return s; +} + +static int +split(char *line, char **domain, char **ltype, char **item, char **value) +{ + char *blank, *saveptr; + int count; + + blank = line + strlen(line); + saveptr = NULL; + + *domain = strtok_r(line, " \t", &saveptr); + *ltype = strtok_r(NULL, " \t", &saveptr); + *item = strtok_r(NULL, " \t", &saveptr); + *value = trim(strtok_r(NULL, "", &saveptr)); + + count = 0; + count += set_if_null(domain, blank); + count += set_if_null(ltype, blank); + count += set_if_null(item, blank); + count += set_if_null(value, blank); + + return count; +} + +static int +parse_config_file(pam_handle_t *pamh, const char *uname, uid_t uid, gid_t gid, + int ctrl, struct pam_limit_s *pl, const int conf_file_set_by_user) +{ + FILE *fil; + char *buf = NULL; + size_t n = 0; + unsigned long long lineno = 0; + + /* check for the conf_file */ + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_DEBUG, "reading settings from '%s'", pl->conf_file); + fil = fopen(pl->conf_file, "r"); + if (fil == NULL) { + if (errno == ENOENT && !conf_file_set_by_user) + return PAM_SUCCESS; /* file is not there and it has not been set by the conf= argument */ + + pam_syslog(pamh, LOG_WARNING, + "cannot read settings from %s: %s", pl->conf_file, + strerror(errno)); + return PAM_SERVICE_ERR; + } + + /* start the show */ + while (getline(&buf, &n, fil) != -1) { + char *domain, *ltype, *item, *value, *tptr, *line; + int i; + int rngtype; + size_t j; + uid_t min_uid = (uid_t)-1, max_uid = (uid_t)-1; + + lineno++; + + line = buf; + /* skip the leading white space */ + while (*line && isspace((unsigned char)*line)) + line++; + + /* Rip off the comments */ + tptr = strchr(line,'#'); + if (tptr) + *tptr = '\0'; + /* Rip off the newline char */ + tptr = strchr(line,'\n'); + if (tptr) + *tptr = '\0'; + /* Anything left ? */ + if (!strlen(line)) + continue; + + i = split(line, &domain, <ype, &item, &value); + D(("scanned line[%d]: domain[%s], ltype[%s], item[%s], value[%s]", + i, domain, ltype, item, value)); + + for(j=0; j < strlen(ltype); j++) + ltype[j]=tolower((unsigned char)ltype[j]); + + if ((rngtype=parse_uid_range(pamh, domain, &min_uid, &max_uid)) < 0) { + pam_syslog(pamh, LOG_WARNING, "invalid uid range '%s' - skipped", domain); + continue; + } + + if (i == 4) { /* a complete line */ + for(j=0; j < strlen(item); j++) + item[j]=tolower((unsigned char)item[j]); + for(j=0; j < strlen(value); j++) + value[j]=tolower((unsigned char)value[j]); + + if (strcmp(uname, domain) == 0) /* this user has a limit */ + process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl); + else if (domain[0]=='@') { + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, + "checking if %s is in group %s", + uname, domain + 1); + } + switch(rngtype) { + case LIMIT_RANGE_NONE: + if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) + process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl, + pl); + break; + case LIMIT_RANGE_ONE: + if (pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid)) + process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl, + pl); + break; + case LIMIT_RANGE_MM: + if (gid > (gid_t)max_uid) + break; + /* fallthrough */ + case LIMIT_RANGE_MIN: + if (gid >= (gid_t)min_uid) + process_limit(pamh, LIMITS_DEF_GROUP, ltype, item, value, ctrl, + pl); + } + } else if (domain[0]=='%') { + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, + "checking if %s is in group %s", + uname, domain + 1); + } + switch(rngtype) { + case LIMIT_RANGE_NONE: + if (strcmp(domain,"%") == 0) + process_limit(pamh, LIMITS_DEF_ALL, ltype, item, value, ctrl, + pl); + else if (pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) { + free(pl->login_group); + pl->login_group = strdup(domain+1); + process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl, + pl); + } + break; + case LIMIT_RANGE_ONE: + if (pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid)) { + struct group *grp; + grp = pam_modutil_getgrgid(pamh, (gid_t)max_uid); + free(pl->login_group); + pl->login_group = strdup(grp->gr_name); + process_limit(pamh, LIMITS_DEF_ALLGROUP, ltype, item, value, ctrl, + pl); + } + break; + case LIMIT_RANGE_MIN: + case LIMIT_RANGE_MM: + pam_syslog(pamh, LOG_WARNING, "range unsupported for %%group matching - ignored"); + } + } else { + switch(rngtype) { + case LIMIT_RANGE_NONE: + if (strcmp(domain, "*") == 0) + process_limit(pamh, LIMITS_DEF_DEFAULT, ltype, item, value, ctrl, + pl); + break; + case LIMIT_RANGE_ONE: + if (uid != max_uid) + break; + /* fallthrough */ + case LIMIT_RANGE_MM: + if (uid > max_uid) + break; + /* fallthrough */ + case LIMIT_RANGE_MIN: + if (uid >= min_uid) + process_limit(pamh, LIMITS_DEF_USER, ltype, item, value, ctrl, pl); + } + } + } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */ + if (strcmp(uname, domain) == 0) { + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "no limits for '%s'", uname); + } + } else if (domain[0] == '@') { + switch(rngtype) { + case LIMIT_RANGE_NONE: + if (!pam_modutil_user_in_group_nam_nam(pamh, uname, domain+1)) + continue; /* next line */ + break; + case LIMIT_RANGE_ONE: + if (!pam_modutil_user_in_group_nam_gid(pamh, uname, (gid_t)max_uid)) + continue; /* next line */ + break; + case LIMIT_RANGE_MM: + if (gid > (gid_t)max_uid) + continue; /* next line */ + /* fallthrough */ + case LIMIT_RANGE_MIN: + if (gid < (gid_t)min_uid) + continue; /* next line */ + } + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, + "no limits for '%s' in group '%s'", + uname, domain+1); + } + } else { + switch(rngtype) { + case LIMIT_RANGE_NONE: + continue; /* next line */ + case LIMIT_RANGE_ONE: + if (uid != max_uid) + continue; /* next line */ + break; + case LIMIT_RANGE_MM: + if (uid > max_uid) + continue; /* next line */ + /* fallthrough */ + case LIMIT_RANGE_MIN: + if (uid >= min_uid) + break; + continue; /* next line */ + } + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "no limits for '%s'", uname); + } + } + free(buf); + fclose(fil); + return PAM_IGNORE; + } else { + pam_syslog(pamh, LOG_WARNING, "invalid line %llu in '%s' - skipped", + lineno, pl->conf_file); + } + } + free(buf); + fclose(fil); + return PAM_SUCCESS; +} + +static int setup_limits(pam_handle_t *pamh, + const char *uname, uid_t uid, int ctrl, + struct pam_limit_s *pl) +{ + int i; + int status; + int retval = LIMITED_OK; + + for (i=0, status=LIMITED_OK; ilimits[i].supported) { + /* skip it if its not known to the system */ + continue; + } + if (pl->limits[i].src_soft == LIMITS_DEF_NONE && + pl->limits[i].src_hard == LIMITS_DEF_NONE) { + /* skip it if its not initialized */ + continue; + } + if (pl->limits[i].limit.rlim_cur > pl->limits[i].limit.rlim_max) + pl->limits[i].limit.rlim_cur = pl->limits[i].limit.rlim_max; + res = setrlimit(i, &pl->limits[i].limit); + if (res != 0) + pam_syslog(pamh, LOG_ERR, "Could not set limit for '%s': %m", + rlimit2str(i)); + status |= res; + } + + if (status) { + retval = LIMIT_ERR; + } + + status = setpriority(PRIO_PROCESS, 0, pl->priority); + if (status != 0) { + pam_syslog(pamh, LOG_ERR, "Could not set limit for PRIO_PROCESS: %m"); + retval = LIMIT_ERR; + } + + if (uid == 0) { + D(("skip login limit check for uid=0")); + } else if (pl->login_limit > 0) { + if (check_logins(pamh, uname, pl->login_limit, ctrl, pl) == LOGIN_ERR) { +#ifdef HAVE_LIBAUDIT + if (!(ctrl & PAM_NO_AUDIT)) { + pam_modutil_audit_write(pamh, AUDIT_ANOM_LOGIN_SESSIONS, + "pam_limits", PAM_PERM_DENIED); + /* ignore return value as we fail anyway */ + } +#endif + retval |= LOGIN_ERR; + } + } else if (pl->login_limit == 0) { + retval |= LOGIN_ERR; + } + + if (pl->nonewprivs) { +#ifdef __linux__ + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { + pam_syslog(pamh, LOG_ERR, "Could not set prctl(PR_SET_NO_NEW_PRIVS): %m"); + retval |= LIMIT_ERR; + } +#else + pam_syslog(pamh, LOG_INFO, "Setting 'nonewprivs' not supported on this OS"); +#endif + } + + return retval; +} + +/* --- evaluating all files in VENDORDIR/security/limits.d and /etc/security/limits.d --- */ +static const char * +base_name(const char *path) +{ + const char *base = strrchr(path, '/'); + return base ? base+1 : path; +} + +static int +compare_filename(const void *a, const void *b) +{ + return strcmp(base_name(* (const char * const *) a), + base_name(* (const char * const *) b)); +} + +/* Evaluating a list of files which have to be parsed in the right order: + * + * - If etc/security/limits.d/@filename@.conf exists, then + * %vendordir%/security/limits.d/@filename@.conf should not be used. + * - All files in both limits.d directories are sorted by their @filename@.conf in + * lexicographic order regardless of which of the directories they reside in. */ +static char ** +read_limits_dir(pam_handle_t *pamh) +{ + glob_t globbuf; + size_t i=0; + int glob_rv = glob(LIMITS_CONF_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); + char **file_list; + size_t file_list_size = glob_rv == 0 ? globbuf.gl_pathc : 0; + +#ifdef VENDOR_LIMITS_CONF_GLOB + glob_t globbuf_vendor; + int glob_rv_vendor = glob(VENDOR_LIMITS_CONF_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf_vendor); + if (glob_rv_vendor == 0) + file_list_size += globbuf_vendor.gl_pathc; +#endif + file_list = malloc((file_list_size + 1) * sizeof(char*)); + if (file_list == NULL) { + pam_syslog(pamh, LOG_ERR, "Cannot allocate memory for file list: %m"); +#ifdef VENDOR_ACCESS_CONF_GLOB + if (glob_rv_vendor == 0) + globfree(&globbuf_vendor); +#endif + if (glob_rv == 0) + globfree(&globbuf); + return NULL; + } + + if (glob_rv == 0) { + for (i = 0; i < globbuf.gl_pathc; i++) { + file_list[i] = strdup(globbuf.gl_pathv[i]); + if (file_list[i] == NULL) { + pam_syslog(pamh, LOG_ERR, "strdup failed: %m"); + break; + } + } + } +#ifdef VENDOR_LIMITS_CONF_GLOB + if (glob_rv_vendor == 0) { + for (size_t j = 0; j < globbuf_vendor.gl_pathc; j++) { + if (glob_rv == 0 && globbuf.gl_pathc > 0) { + int double_found = 0; + for (size_t k = 0; k < globbuf.gl_pathc; k++) { + if (strcmp(base_name(globbuf.gl_pathv[k]), + base_name(globbuf_vendor.gl_pathv[j])) == 0) { + double_found = 1; + break; + } + } + if (double_found) + continue; + } + file_list[i] = strdup(globbuf_vendor.gl_pathv[j]); + if (file_list[i] == NULL) { + pam_syslog(pamh, LOG_ERR, "strdup failed: %m"); + break; + } + i++; + } + globfree(&globbuf_vendor); + } +#endif + file_list[i] = NULL; + qsort(file_list, i, sizeof(char *), compare_filename); + if (glob_rv == 0) + globfree(&globbuf); + + return file_list; +} + +/* now the session stuff */ +int +pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int retval, i; + char *user_name; + struct passwd *pwd; + int ctrl; + struct pam_limit_s plstruct; + struct pam_limit_s *pl = &plstruct; + char *free_filename = NULL; + + D(("called.")); + + memset(pl, 0, sizeof(*pl)); + + ctrl = _pam_parse(pamh, argc, argv, pl); + retval = pam_get_item( pamh, PAM_USER, (void*) &user_name ); + if ( user_name == NULL || retval != PAM_SUCCESS ) { + pam_syslog(pamh, LOG_ERR, "open_session - error recovering username"); + return PAM_SESSION_ERR; + } + + int conf_file_set_by_user = (pl->conf_file != NULL); + if (pl->conf_file == NULL) { + pl->conf_file = LIMITS_FILE; +#ifdef VENDOR_LIMITS_FILE + /* + * Check whether LIMITS_FILE file is available. + * If it does not exist, fall back to VENDOR_LIMITS_FILE file. + */ + struct stat buffer; + if (stat(pl->conf_file, &buffer) != 0 && errno == ENOENT) + pl->conf_file = VENDOR_LIMITS_FILE; +#endif + } + + pwd = pam_modutil_getpwnam(pamh, user_name); + if (!pwd) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_WARNING, + "open_session username '%s' does not exist", user_name); + return PAM_USER_UNKNOWN; + } + + retval = init_limits(pamh, pl, ctrl); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "cannot initialize"); + return PAM_ABORT; + } + + retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, + ctrl, pl, conf_file_set_by_user); + if (retval == PAM_IGNORE) { + D(("the configuration file ('%s') has an applicable ' -' entry", pl->conf_file)); + free(pl->login_group); + return PAM_SUCCESS; + } + if (retval != PAM_SUCCESS || conf_file_set_by_user) + /* skip reading limits.d if config file explicitly specified */ + goto out; + + /* Read subsequent *.conf files, if they exist. */ + char **filename_list = read_limits_dir(pamh); + if (filename_list != NULL) { + for (i = 0; filename_list[i] != NULL; i++) { + pl->conf_file = filename_list[i]; + retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl, 0); + if (retval != PAM_SUCCESS) + break; + } + for (i = 0; filename_list[i] != NULL; i++) { + if (filename_list[i] == pl->conf_file) + free_filename = filename_list[i]; + else + free(filename_list[i]); + } + free(filename_list); + } + + if (retval == PAM_IGNORE) { + D(("the configuration file ('%s') has an applicable ' -' entry", pl->conf_file)); + free(free_filename); + free(pl->login_group); + return PAM_SUCCESS; + } + +out: + if (retval != PAM_SUCCESS) + { + pam_syslog(pamh, LOG_ERR, "error parsing the configuration file: '%s' ", pl->conf_file); + free(free_filename); + free(pl->login_group); + return retval; + } + + retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, pl); + free(free_filename); + free(pl->login_group); + if (retval & LOGIN_ERR) + pam_error(pamh, _("There were too many logins for '%s'."), + pwd->pw_name); + if (retval != LIMITED_OK) { + return PAM_PERM_DENIED; + } + + return PAM_SUCCESS; +} + +int +pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + /* nothing to do */ + return PAM_SUCCESS; +} + +/* + * Copyright (c) Cristian Gafton, 1996-1997, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_limits/tst-pam_limits b/modules/pam_limits/tst-pam_limits new file mode 100755 index 0000000..f563beb --- /dev/null +++ b/modules/pam_limits/tst-pam_limits @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_limits.so diff --git a/modules/pam_listfile/README.xml b/modules/pam_listfile/README.xml new file mode 100644 index 0000000..d0b6010 --- /dev/null +++ b/modules/pam_listfile/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_listfile.8.xml" xpointer='xpointer(id("pam_listfile-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_listfile/meson.build b/modules/pam_listfile/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_listfile/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_listfile/pam_listfile.8.xml b/modules/pam_listfile/pam_listfile.8.xml new file mode 100644 index 0000000..40a553d --- /dev/null +++ b/modules/pam_listfile/pam_listfile.8.xml @@ -0,0 +1,294 @@ + + + + pam_listfile + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_listfile + deny or allow services based on an arbitrary file + + + + + pam_listfile.so + + item=[tty|user|rhost|ruser|group|shell] + + + sense=[allow|deny] + + + file=/path/filename + + + onerr=[succeed|fail] + + + apply=[user|@group] + + + quiet + + + + + + + DESCRIPTION + + + pam_listfile is a PAM module which provides a way to deny or + allow services based on an arbitrary file. + + + The module gets the of the type specified -- + user specifies the username, + PAM_USER; tty specifies the + name of the terminal (if any) over which the request has been made, + PAM_TTY; rhost specifies the + name of the remote host (if any) from which the request was made, + PAM_RHOST; and ruser specifies + the name of the remote user (if available) who made the request, + PAM_RUSER -- and looks for an instance of that item + in the . + filename contains one line per item listed. If the + item is found, then if , + PAM_SUCCESS is returned, causing the authorization + request to succeed; else if , + PAM_AUTH_ERR is returned, causing the authorization + request to fail. + + + If an error is encountered (for instance, if + filename does not exist, or a poorly-constructed + argument is encountered), then if , + PAM_SUCCESS is returned, otherwise if + , PAM_AUTH_ERR or + PAM_SERVICE_ERR (as appropriate) will be returned. + + + An additional argument, , can be used + to restrict the application of the above to a specific user + () + or a given group + (). + This added restriction is only meaningful when used with the + tty, rhost and + shell items. + + + Besides this last one, all arguments should be specified; do not + count on any default behavior. + + + No credentials are awarded by this module. + + + + + + OPTIONS + + + + + + item=[tty|user|rhost|ruser|group|shell] + + + + What is listed in the file and should be checked for. + + + + + + + sense=[allow|deny] + + + + Action to take if the item is found in the file. + If the item is NOT found in the file, + then the opposite action is requested. + + + + + + + file=/path/filename + + + + File containing one item per line. The file needs to be a plain + file and not world writable. + + + + + + + onerr=[succeed|fail] + + + + What to do if something weird happens like being unable to open + the file. + + + + + + + apply=[user|@group] + + + + Restrict the user class for which the restriction apply. Note that + with this does not make sense, + but for it has a meaning. + + + + + + + quiet + + + + Do not treat service refusals or missing list files as + errors that need to be logged. + + + + + + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + + + PAM_AUTH_ERR + + Authentication failure. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_IGNORE + + + The rule does not apply to the option. + + + + + + PAM_SERVICE_ERR + + + Error in service module. + + + + + + PAM_SUCCESS + + + Success. + + + + + + + + + + EXAMPLES + + Classic 'ftpusers' authentication can be implemented with this entry + in /etc/pam.d/ftpd: + +# +# deny ftp-access to users listed in the /etc/ftpusers file +# +auth required pam_listfile.so \ + onerr=succeed item=user sense=deny file=/etc/ftpusers + + Note, users listed in /etc/ftpusers file are + (counterintuitively) not allowed access to + the ftp service. + + + To allow login access only for certain users, you can use a + /etc/pam.d/login entry like this: + +# +# permit login to users listed in /etc/loginusers +# +auth required pam_listfile.so \ + onerr=fail item=user sense=allow file=/etc/loginusers + + For this example to work, all users who are allowed to use the + login service should be listed in the file + /etc/loginusers. Unless you are explicitly + trying to lock out root, make sure that when you do this, you leave + a way for root to log in, either by listing root in + /etc/loginusers, or by listing a user who is + able to su to the root account. + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_listfile was written by Michael K. Johnson <johnsonm@redhat.com> + and Elliot Lee <sopwith@cuc.edu>. + + + + diff --git a/modules/pam_listfile/pam_listfile.c b/modules/pam_listfile/pam_listfile.c new file mode 100644 index 0000000..b0bd221 --- /dev/null +++ b/modules/pam_listfile/pam_listfile.c @@ -0,0 +1,394 @@ +/* + * pam_listfile module + * + * by Elliot Lee , Red Hat Software. July 25, 1996. + * log refused access error christopher mccrory 1998/7/11 + * + * This code began life as the pam_rootok module. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PAM_DEBUG +#include +#endif + +#include +#include +#include +#include +#include "pam_inline.h" + +/* --- authentication management functions (only) --- */ + +/* Extended Items that are not directly available via pam_get_item() */ +#define EI_GROUP (1 << 0) +#define EI_SHELL (1 << 1) + +/* Constants for apply= parameter */ +#define APPLY_TYPE_NULL 0 +#define APPLY_TYPE_NONE 1 +#define APPLY_TYPE_USER 2 +#define APPLY_TYPE_GROUP 3 + +#define LESSER(a, b) ((a) < (b) ? (a) : (b)) + +static int +pam_listfile(pam_handle_t *pamh, int argc, const char **argv) +{ + int retval = -1; + int onerr = PAM_SERVICE_ERR; + int citem = 0; + int extitem = 0; + int sense = -1; + int quiet = 0; + int i; + const void *void_citemp; + const char *citemp; + const char *ifname=NULL; + char *aline=NULL; + const char *apply_val; + struct stat fileinfo; + FILE *inf; + int apply_type; + size_t n=0; + + /* Stuff for "extended" items */ + struct passwd *userinfo; + + apply_type=APPLY_TYPE_NULL; + apply_val = ""; + + for(i=0; i < argc; i++) { + const char *str; + + /* option quiet has no value */ + if(!strcmp(argv[i],"quiet")) { + quiet = 1; + continue; + } + + if(strchr(argv[i], '=') == NULL) { + pam_syslog(pamh,LOG_ERR, "Bad option: \"%s\"", argv[i]); + continue; + } + if ((str = pam_str_skip_prefix(argv[i], "onerr=")) != NULL) { + if(!strcmp(str,"succeed")) + onerr = PAM_SUCCESS; + else if(!strcmp(str,"fail")) + onerr = PAM_SERVICE_ERR; + else { + pam_syslog(pamh, LOG_ERR, "Unknown option: %s", argv[i]); + if (retval == -1) + retval = PAM_SERVICE_ERR; + continue; + } + } else if ((str = pam_str_skip_prefix(argv[i], "sense=")) != NULL) { + if(!strcmp(str,"allow")) + sense=0; + else if(!strcmp(str,"deny")) + sense=1; + else { + pam_syslog(pamh, LOG_ERR, "Unknown option: %s", argv[i]); + if (retval == -1) + retval = onerr; + continue; + } + } else if ((str = pam_str_skip_prefix(argv[i], "file=")) != NULL) { + ifname = str; + } else if ((str = pam_str_skip_prefix(argv[i], "item=")) != NULL) { + if(!strcmp(str,"user")) + citem = PAM_USER; + else if(!strcmp(str,"tty")) + citem = PAM_TTY; + else if(!strcmp(str,"rhost")) + citem = PAM_RHOST; + else if(!strcmp(str,"ruser")) + citem = PAM_RUSER; + else { /* These items are related to the user, but are not + directly gettable with pam_get_item */ + citem = PAM_USER; + if(!strcmp(str,"group")) + extitem = EI_GROUP; + else if(!strcmp(str,"shell")) + extitem = EI_SHELL; + else + citem = 0; + } + } else if ((str = pam_str_skip_prefix(argv[i], "apply=")) != NULL) { + apply_type=APPLY_TYPE_NONE; + if (*str=='@') { + apply_type=APPLY_TYPE_GROUP; + apply_val = str+1; + } else { + apply_type=APPLY_TYPE_USER; + apply_val = str; + } + } else { + pam_syslog(pamh,LOG_ERR, "Unknown option: %s",argv[i]); + if (retval == -1) + retval = onerr; + continue; + } + } + + if (!citem) { + pam_syslog(pamh,LOG_ERR, + "Unknown item or item not specified"); + if (retval == -1) + retval = onerr; + } + + if (!ifname) { + pam_syslog(pamh,LOG_ERR, "List filename not specified"); + if (retval == -1) + retval = onerr; + } + + if (sense == -1) { + pam_syslog(pamh,LOG_ERR, + "Unknown sense or sense not specified"); + if (retval == -1) + retval = onerr; + } + + if ((apply_type == APPLY_TYPE_NONE) || + ((apply_type != APPLY_TYPE_NULL) && (*apply_val == '\0'))) { + pam_syslog(pamh,LOG_ERR, + "Invalid usage for apply= parameter"); + if (retval == -1) + retval = onerr; + } + + if (retval != -1) + return retval; + + /* Check if it makes sense to use the apply= parameter */ + if (apply_type != APPLY_TYPE_NULL) { + if((citem==PAM_USER) || (citem==PAM_RUSER)) { + pam_syslog(pamh,LOG_WARNING, + "Non-sense use for apply= parameter"); + apply_type=APPLY_TYPE_NULL; + } + if(extitem && (extitem==EI_GROUP)) { + pam_syslog(pamh,LOG_WARNING, + "Non-sense use for apply= parameter"); + apply_type=APPLY_TYPE_NULL; + } + } + + /* Short-circuit - test if this session applies for this user */ + { + const char *user_name; + int rval; + + rval=pam_get_user(pamh,&user_name,NULL); + if(rval==PAM_SUCCESS && user_name[0]) { + /* Got it ? Valid ? */ + if(apply_type==APPLY_TYPE_USER) { + if(strcmp(user_name, apply_val)) { + /* Does not apply to this user */ +#ifdef PAM_DEBUG + pam_syslog(pamh,LOG_DEBUG, + "don't apply: apply=%s, user=%s", + apply_val,user_name); +#endif /* PAM_DEBUG */ + return PAM_IGNORE; + } + } else if(apply_type==APPLY_TYPE_GROUP) { + if(!pam_modutil_user_in_group_nam_nam(pamh,user_name,apply_val)) { + /* Not a member of apply= group */ +#ifdef PAM_DEBUG + pam_syslog(pamh,LOG_DEBUG, + + "don't apply: %s not a member of group %s", + user_name,apply_val); +#endif /* PAM_DEBUG */ + return PAM_IGNORE; + } + } + } + } + + retval = pam_get_item(pamh,citem,&void_citemp); + citemp = void_citemp; + if(retval != PAM_SUCCESS) { + return onerr; + } + if((citem == PAM_USER) && !citemp) { + retval = pam_get_user(pamh,&citemp,NULL); + if (retval != PAM_SUCCESS) { + return PAM_SERVICE_ERR; + } + } + if((citem == PAM_TTY) && citemp) { + /* Normalize the TTY name. */ + const char *str = pam_str_skip_prefix(citemp, "/dev/"); + if (str != NULL) + citemp = str; + } + + if(!citemp || (strlen(citemp) == 0)) { + /* The item was NULL - we are sure not to match */ + return sense?PAM_SUCCESS:PAM_AUTH_ERR; + } + + if(extitem) { + switch(extitem) { + case EI_GROUP: + /* Just ignore, call pam_modutil_in_group... later */ + break; + case EI_SHELL: + /* Assume that we have already gotten PAM_USER in + pam_get_item() - a valid assumption since citem + gets set to PAM_USER in the extitem switch */ + userinfo = pam_modutil_getpwnam(pamh, citemp); + if (userinfo == NULL) { + pam_syslog(pamh, LOG_NOTICE, "getpwnam(%s) failed", + citemp); + return onerr; + } + citemp = userinfo->pw_shell; + break; + default: + pam_syslog(pamh,LOG_ERR, + + "Internal weirdness, unknown extended item %d", + extitem); + return onerr; + } + } +#ifdef PAM_DEBUG + pam_syslog(pamh,LOG_INFO, + + "Got file = %s, item = %d, value = %s, sense = %d", + ifname, citem, citemp, sense); +#endif + if(lstat(ifname,&fileinfo)) { + if(!quiet) + pam_syslog(pamh,LOG_ERR, "Couldn't open %s",ifname); + return onerr; + } + + if((fileinfo.st_mode & S_IWOTH) + || !S_ISREG(fileinfo.st_mode)) { + /* If the file is world writable or is not a + normal file, return error */ + pam_syslog(pamh,LOG_ERR, + "%s is either world writable or not a normal file", + ifname); + return PAM_AUTH_ERR; + } + + inf = fopen(ifname,"r"); + if(inf == NULL) { /* Check that we opened it successfully */ + if (onerr == PAM_SERVICE_ERR) { + /* Only report if it's an error... */ + pam_syslog(pamh,LOG_ERR, "Error opening %s", ifname); + } + return onerr; + } + /* There should be no more errors from here on */ + retval=PAM_AUTH_ERR; + /* This loop assumes that PAM_SUCCESS == 0 + and PAM_AUTH_ERR != 0 */ +#ifdef PAM_DEBUG + assert(PAM_SUCCESS == 0); + assert(PAM_AUTH_ERR != 0); +#endif + while(retval && getline(&aline,&n,inf) != -1) { + const char *a = aline; + + aline[strcspn(aline, "\r\n")] = '\0'; + if(aline[0] == '\0') + continue; + if(citem == PAM_TTY) { + const char *str = pam_str_skip_prefix(a, "/dev/"); + if (str != NULL) + a = str; + } + if (extitem == EI_GROUP) { + retval = !pam_modutil_user_in_group_nam_nam(pamh, + citemp, aline); + } else { + retval = strcmp(a, citemp); + } + } + + free(aline); + fclose(inf); + if ((sense && retval) || (!sense && !retval)) { +#ifdef PAM_DEBUG + pam_syslog(pamh,LOG_INFO, + "Returning PAM_SUCCESS, retval = %d", retval); +#endif + return PAM_SUCCESS; + } + else { + const void *service; + const char *user_name; +#ifdef PAM_DEBUG + pam_syslog(pamh,LOG_INFO, + "Returning PAM_AUTH_ERR, retval = %d", retval); +#endif + (void) pam_get_item(pamh, PAM_SERVICE, &service); + (void) pam_get_user(pamh, &user_name, NULL); + if (!quiet) + pam_syslog (pamh, LOG_NOTICE, "Refused user %s for service %s", + user_name, (const char *)service); + return PAM_AUTH_ERR; + } +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_listfile(pamh, argc, argv); +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_listfile(pamh, argc, argv); +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_listfile(pamh, argc, argv); +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_listfile(pamh, argc, argv); +} + +int +pam_sm_chauthtok(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_listfile(pamh, argc, argv); +} diff --git a/modules/pam_listfile/tst-pam_listfile b/modules/pam_listfile/tst-pam_listfile new file mode 100755 index 0000000..f555a9f --- /dev/null +++ b/modules/pam_listfile/tst-pam_listfile @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_listfile.so diff --git a/modules/pam_listfile/tst-pam_listfile-retval.c b/modules/pam_listfile/tst-pam_listfile-retval.c new file mode 100644 index 0000000..02ed944 --- /dev/null +++ b/modules/pam_listfile/tst-pam_listfile-retval.c @@ -0,0 +1,620 @@ +/* + * Check pam_listfile return values. + * + * Copyright (c) 2023 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_listfile" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char list_file[] = TEST_NAME ".list"; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + + char cwd[PATH_MAX]; + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + struct passwd *pw; + ASSERT_NE(NULL, (pw = getpwuid(geteuid()))); + + struct group *gr; + ASSERT_NE(NULL, (gr = getgrgid(getegid()))); + + /* invalid onerr= specified */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so onerr\n" + "account required %s/" LTDIR "%s.so onerr=\n" + "password required %s/" LTDIR "%s.so onerr=0\n" + "session required %s/" LTDIR "%s.so onerr=1\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* unknown option, implicit onerr=fail */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so one=1\n" + "account required %s/" LTDIR "%s.so two=2\n" + "password required %s/" LTDIR "%s.so three=3\n" + "session required %s/" LTDIR "%s.so four=4\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* unknown option, onerr=succeed after unknown option */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so one=1 onerr=succeed\n" + "account required %s/" LTDIR "%s.so two=2 onerr=succeed\n" + "password required %s/" LTDIR "%s.so three=3 onerr=succeed\n" + "session required %s/" LTDIR "%s.so four=4 onerr=succeed\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* unknown option, onerr=succeed before unknown option */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so onerr=succeed one=1\n" + "account required %s/" LTDIR "%s.so onerr=succeed two=2\n" + "password required %s/" LTDIR "%s.so onerr=succeed three=3\n" + "session required %s/" LTDIR "%s.so onerr=succeed four=4\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* no item= or invalid item= specified, implicit onerr=fail */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so item\n" + "password required %s/" LTDIR "%s.so item=\n" + "session required %s/" LTDIR "%s.so item=bad\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* no item= or invalid item= specified, explicit onerr=fail */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so onerr=fail\n" + "account required %s/" LTDIR "%s.so onerr=fail item\n" + "password required %s/" LTDIR "%s.so onerr=fail item=\n" + "session required %s/" LTDIR "%s.so onerr=fail item=bad\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* no item= or invalid item= specified, onerr=succeed */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so onerr=succeed\n" + "account required %s/" LTDIR "%s.so onerr=succeed item\n" + "password required %s/" LTDIR "%s.so onerr=succeed item=\n" + "session required %s/" LTDIR "%s.so onerr=succeed item=bad\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* no file= specified, implicit onerr=fail */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=user\n" + "account required %s/" LTDIR "%s.so item=group\n" + "password required %s/" LTDIR "%s.so item=ruser\n" + "session required %s/" LTDIR "%s.so item=rhost\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* no file= specified, onerr=succeed */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so onerr=succeed item=user\n" + "account required %s/" LTDIR "%s.so onerr=succeed item=group\n" + "password required %s/" LTDIR "%s.so onerr=succeed item=ruser\n" + "session required %s/" LTDIR "%s.so onerr=succeed item=rhost\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* no sense= or invalid sense= specified, implicit onerr=fail */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=user file=.\n" + "account required %s/" LTDIR "%s.so item=group file=. sense\n" + "password required %s/" LTDIR "%s.so item=shell file=. sense=\n" + "session required %s/" LTDIR "%s.so item=tty file=. sense=bad\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* no sense= or invalid sense= specified, onerr=succeed */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so onerr=succeed item=user file=.\n" + "account required %s/" LTDIR "%s.so onerr=succeed item=group file=. sense\n" + "password required %s/" LTDIR "%s.so onerr=succeed item=shell file=. sense=\n" + "session required %s/" LTDIR "%s.so onerr=succeed item=tty file=. sense=bad\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* invalid apply= specified, implicit onerr=fail */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=ruser file=. sense=allow apply=\n" + "account required %s/" LTDIR "%s.so item=rhost file=. sense=allow apply=\n" + "password required %s/" LTDIR "%s.so item=tty file=. sense=allow apply=@\n" + "session required %s/" LTDIR "%s.so item=tty file=. sense=allow apply=\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* invalid apply= specified, onerr=succeed */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so onerr=succeed item=ruser file=. sense=allow apply=\n" + "account required %s/" LTDIR "%s.so onerr=succeed item=rhost file=. sense=allow apply=\n" + "password required %s/" LTDIR "%s.so onerr=succeed item=tty file=. sense=allow apply=@\n" + "session required %s/" LTDIR "%s.so onerr=succeed item=tty file=. sense=allow apply=\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_IGNORE -> PAM_PERM_DENIED */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=rhost file=. sense=allow apply=:\n" + "account required %s/" LTDIR "%s.so item=tty file=. sense=allow apply=@:\n" + "password required %s/" LTDIR "%s.so item=rhost file=. sense=allow apply=:\n" + "session required %s/" LTDIR "%s.so item=tty file=. sense=allow apply=@:\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* item not set, sense=allow */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=rhost file=. sense=allow apply=%s\n" + "account required %s/" LTDIR "%s.so item=tty file=. sense=allow apply=%s\n" + "password required %s/" LTDIR "%s.so item=rhost file=. sense=allow apply=@%s\n" + "session required %s/" LTDIR "%s.so item=tty file=. sense=allow apply=@%s\n", + cwd, MODULE_NAME, pw->pw_name, + cwd, MODULE_NAME, pw->pw_name, + cwd, MODULE_NAME, gr->gr_name, + cwd, MODULE_NAME, gr->gr_name)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_AUTH_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* item not set, sense=deny */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=ruser file=. sense=deny\n" + "account required %s/" LTDIR "%s.so item=rhost file=. sense=deny\n" + "password required %s/" LTDIR "%s.so item=tty file=. sense=deny\n" + "session required %s/" LTDIR "%s.so item=ruser file=. sense=deny\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* file does not exist, not a regular file, or world writable */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=user file= sense=allow\n" + "account required %s/" LTDIR "%s.so item=user file=. sense=allow onerr=succeed\n" + "password required %s/" LTDIR "%s.so item=user file=/ sense=allow onerr=succeed\n" + "session required %s/" LTDIR "%s.so item=user file=/dev/null sense=allow onerr=succeed\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* item is listed, sense=allow */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=user file=%s sense=allow\n" + "account required %s/" LTDIR "%s.so item=user file=%s sense=allow\n" + "password required %s/" LTDIR "%s.so item=user file=%s sense=allow\n" + "session required %s/" LTDIR "%s.so item=user file=%s sense=allow\n", + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_NE(NULL, fp = fopen(list_file, "w")); + ASSERT_LT(0, fprintf(fp, "%s\n", pw->pw_name)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* item is listed, sense=deny */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=user file=%s sense=deny\n" + "account required %s/" LTDIR "%s.so item=user file=%s sense=deny\n" + "password required %s/" LTDIR "%s.so item=user file=%s sense=deny\n" + "session required %s/" LTDIR "%s.so item=user file=%s sense=deny\n", + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_NE(NULL, fp = fopen(list_file, "w")); + ASSERT_LT(0, fprintf(fp, "%s\n", pw->pw_name)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_AUTH_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* item is not listed, sense=allow */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=user file=%s sense=allow\n" + "account required %s/" LTDIR "%s.so item=user file=%s sense=allow\n" + "password required %s/" LTDIR "%s.so item=user file=%s sense=allow\n" + "session required %s/" LTDIR "%s.so item=user file=%s sense=allow\n", + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_NE(NULL, fp = fopen(list_file, "w")); + ASSERT_LT(0, fprintf(fp, ":\n")); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_AUTH_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* item is not listed, sense=deny */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so item=user file=%s sense=deny\n" + "account required %s/" LTDIR "%s.so item=user file=%s sense=deny\n" + "password required %s/" LTDIR "%s.so item=user file=%s sense=deny\n" + "session required %s/" LTDIR "%s.so item=user file=%s sense=deny\n", + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_NE(NULL, fp = fopen(list_file, "w")); + ASSERT_LT(0, fprintf(fp, ":\n")); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* Perform a test dedicated to configuration file parsing. */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "# support line continuations, ignore escaped newlines in comments \\\n" + "auth required \\\n" + " %s/" LTDIR "%s.so \\ \n" + " item=user \\\t\t\n" + " file=%s \\ \t \t\n" + " sense=deny\n" + "\t \t # allow unneeded whitespace, ignore escaped newlines in comments \\ \n" + " account\t required %s/" LTDIR "%s.so item=user file=%s sense=deny%c\\\n" + "line after NUL byte continues up to here\n" + "# trim trailing comments, ignore escaped newlines in comments \\\t\n" + "password required %s/" LTDIR "%s.so item=user file=%s sense=deny # foo=bar\n" + "# support %*s long lines\n" + "session required %*s/" LTDIR "%s.so item=user file=%s sense=deny", + cwd, MODULE_NAME, list_file, + cwd, MODULE_NAME, list_file, '\0', + cwd, MODULE_NAME, list_file, + 8192, " ", + 65536, cwd, MODULE_NAME, list_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(list_file)); + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_localuser/README.xml b/modules/pam_localuser/README.xml new file mode 100644 index 0000000..f1b05d1 --- /dev/null +++ b/modules/pam_localuser/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_localuser.8.xml" xpointer='xpointer(id("pam_localuser-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_localuser/meson.build b/modules/pam_localuser/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_localuser/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_localuser/pam_localuser.8.xml b/modules/pam_localuser/pam_localuser.8.xml new file mode 100644 index 0000000..2002d1d --- /dev/null +++ b/modules/pam_localuser/pam_localuser.8.xml @@ -0,0 +1,199 @@ + + + + pam_localuser + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_localuser + require users to be listed in /etc/passwd + + + + + pam_localuser.so + + debug + + + file=/path/passwd + + + + + + + DESCRIPTION + + + pam_localuser is a PAM module to help implementing site-wide login + policies, where they typically include a subset of the network's + users and a few accounts that are local to a particular workstation. + Using pam_localuser and pam_wheel or pam_listfile is an effective + way to restrict access to either local users and/or a subset of the + network's users. + + + This could also be implemented using pam_listfile.so and a very + short awk script invoked by cron, but it's common enough to have + been separated out. + + + + + + + OPTIONS + + + + + + debug + + + + Print debug information. + + + + + + + file=/path/passwd + + + + Use a file other than /etc/passwd. + + + + + + + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + + + PAM_SUCCESS + + + The new localuser was set successfully. + + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_CONV_ERR + + + The conversation method supplied by the application + failed to obtain the username. + + + + + + PAM_INCOMPLETE + + + The conversation method supplied by the application + returned PAM_CONV_AGAIN. + + + + + + PAM_SERVICE_ERR + + + The user name is not valid or the passwd file is unavailable. + + + + + + PAM_PERM_DENIED + + + The user is not listed in the passwd file. + + + + + + + + + + EXAMPLES + + Add the following lines to /etc/pam.d/su to + allow only local users or group wheel to use su. + +account sufficient pam_localuser.so +account required pam_wheel.so + + + + + + FILES + + + /etc/passwd + + Local user account information. + + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_localuser was written by Nalin Dahyabhai <nalin@redhat.com>. + + + + \ No newline at end of file diff --git a/modules/pam_localuser/pam_localuser.c b/modules/pam_localuser/pam_localuser.c new file mode 100644 index 0000000..e47bb00 --- /dev/null +++ b/modules/pam_localuser/pam_localuser.c @@ -0,0 +1,136 @@ +/* + * pam_localuser module + * + * Copyright 2001, 2004 Red Hat, Inc. + * Copyright (c) 2020 Dmitry V. Levin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "pam_inline.h" + +static int +pam_localuser(pam_handle_t *pamh, int argc, const char **argv) +{ + int i; + int rc; + int debug = 0; + const char *file_name = NULL; + const char *user_name = NULL; + + /* Process arguments. */ + for (i = 0; i < argc; ++i) { + if (strcmp("debug", argv[i]) == 0) { + debug = 1; + } + } + for (i = 0; i < argc; ++i) { + const char *str; + + if (strcmp("debug", argv[i]) == 0) { + /* Already processed. */ + continue; + } + if ((str = pam_str_skip_prefix(argv[i], "file=")) != NULL) { + file_name = str; + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "set filename to %s", file_name); + } + } else { + pam_syslog(pamh, LOG_ERR, "unrecognized option: %s", + argv[i]); + } + } + + /* Obtain the user name. */ + if ((rc = pam_get_user(pamh, &user_name, NULL)) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", + pam_strerror(pamh, rc)); + return rc == PAM_CONV_AGAIN ? PAM_INCOMPLETE : rc; + } + + return pam_modutil_check_user_in_passwd(pamh, user_name, file_name); +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_localuser(pamh, argc, argv); +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_localuser(pamh, argc, argv); +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_localuser(pamh, argc, argv); +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_localuser(pamh, argc, argv); +} + +int +pam_sm_chauthtok(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_localuser(pamh, argc, argv); +} diff --git a/modules/pam_localuser/tst-pam_localuser b/modules/pam_localuser/tst-pam_localuser new file mode 100755 index 0000000..2bcdf6b --- /dev/null +++ b/modules/pam_localuser/tst-pam_localuser @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_localuser.so diff --git a/modules/pam_localuser/tst-pam_localuser-retval.c b/modules/pam_localuser/tst-pam_localuser-retval.c new file mode 100644 index 0000000..f6c22f9 --- /dev/null +++ b/modules/pam_localuser/tst-pam_localuser-retval.c @@ -0,0 +1,144 @@ +/* + * Check pam_localuser return values. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_localuser" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char passwd_file[] = TEST_NAME ".passwd"; +static const char missing_file[] = TEST_NAME ".missing"; + +static const char alice_line[] = "alice:x:1001:1001:Alice:/home/alice:"; +static const char bob_line[] = "bob:x:1002:1002:Bob:/home/bob:"; +static const char craig_prefix[] = ":x:1003:1003:"; +static const char craig_suffix[] = "craig:/home/craig:"; + +int +main(void) +{ + static struct pam_conv conv; + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + char name[BUFSIZ]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + /* default passwd */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + memset(name, 'x', sizeof(name) - 1); + name[sizeof(name) - 1] = '\0'; + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "root:x", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* missing passwd file */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so file=%s\n", + cwd, MODULE_NAME, missing_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "root", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* custom passwd file */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so file=%s\n", + cwd, MODULE_NAME, passwd_file)); + ASSERT_EQ(0, fclose(fp)); + + memcpy(name + (sizeof(name) - sizeof(craig_prefix)), + craig_prefix, sizeof(craig_prefix)); + ASSERT_NE(NULL, fp = fopen(passwd_file, "w")); + ASSERT_LT(0, fprintf(fp, "%s\n%s\n%s%s\n", + alice_line, bob_line, name, craig_suffix)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + memset(name, 'x', sizeof(name) - 1); + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "alice", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "bob", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "alice:x", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "craig", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + ASSERT_EQ(0, unlink(passwd_file)); + + return 0; +} diff --git a/modules/pam_loginuid/README.xml b/modules/pam_loginuid/README.xml new file mode 100644 index 0000000..f972105 --- /dev/null +++ b/modules/pam_loginuid/README.xml @@ -0,0 +1,23 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_loginuid.8.xml" xpointer='xpointer(id("pam_loginuid-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_loginuid/meson.build b/modules/pam_loginuid/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_loginuid/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_loginuid/pam_loginuid.8.xml b/modules/pam_loginuid/pam_loginuid.8.xml new file mode 100644 index 0000000..d5285f0 --- /dev/null +++ b/modules/pam_loginuid/pam_loginuid.8.xml @@ -0,0 +1,139 @@ + + + + pam_loginuid + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_loginuid + Record user's login uid to the process attribute + + + + + pam_loginuid.so + + require_auditd + + + + + + + DESCRIPTION + + + The pam_loginuid module sets the loginuid process attribute for the + process that was authenticated. This is necessary for applications + to be correctly audited. This PAM module should only be used for entry + point applications like: login, sshd, gdm, vsftpd, crond and atd. + There are probably other entry point applications besides these. + You should not use it for applications like sudo or su as that + defeats the purpose by changing the loginuid to the account they just + switched to. + + + + + OPTIONS + + + + require_auditd + + + + This option, when given, will cause this module to query + the audit daemon status and deny logins if it is not running. + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + + PAM_SUCCESS + + + The loginuid value is set and auditd is running if check requested. + + + + + PAM_IGNORE + + + The /proc/self/loginuid file is not present on the system or the + login process runs inside uid namespace and kernel does not support + overwriting loginuid. + + + + + PAM_SESSION_ERR + + + Any other error prevented setting loginuid or auditd is not running. + + + + + + + + + EXAMPLES + +#%PAM-1.0 +auth required pam_unix.so +auth required pam_nologin.so +account required pam_unix.so +password required pam_unix.so +session required pam_unix.so +session required pam_loginuid.so + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + , + + auditctl8 + , + + auditd8 + + + + + + AUTHOR + + pam_loginuid was written by Steve Grubb <sgrubb@redhat.com> + + + + \ No newline at end of file diff --git a/modules/pam_loginuid/pam_loginuid.c b/modules/pam_loginuid/pam_loginuid.c new file mode 100644 index 0000000..fc73913 --- /dev/null +++ b/modules/pam_loginuid/pam_loginuid.c @@ -0,0 +1,265 @@ +/* + * pam_loginuid module + * + * Copyright 2005 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: + * Steve Grubb + * + * PAM module that sets the login uid introduced in kernel 2.6.11 + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef HAVE_LIBAUDIT +#include +#include +#endif + +/* + * This function writes the loginuid to the /proc system. It returns + * PAM_SUCCESS on success, + * PAM_IGNORE when /proc/self/loginuid does not exist, + * PAM_SESSION_ERR in case of any other error. + */ +static int set_loginuid(pam_handle_t *pamh, uid_t uid) +{ + int fd, count, rc = PAM_SESSION_ERR; + char loginuid[24], buf[24]; + static const char host_uid_map[] = " 0 0 4294967295\n"; + char uid_map[sizeof(host_uid_map)]; + + /* loginuid in user namespaces currently isn't writable and in some + case, not even readable, so consider any failure as ignorable (but try + anyway, in case we hit a kernel which supports it). */ + fd = open("/proc/self/uid_map", O_RDONLY); + if (fd >= 0) { + count = pam_modutil_read(fd, uid_map, sizeof(uid_map)); + if (count <= 0 || strncmp(uid_map, host_uid_map, count) != 0) + rc = PAM_IGNORE; + close(fd); + } + + fd = open("/proc/self/loginuid", O_NOFOLLOW|O_RDWR); + if (fd < 0) { + if (errno == ENOENT) { + rc = PAM_IGNORE; + } + if (rc != PAM_IGNORE) { + pam_syslog(pamh, LOG_ERR, "Cannot open %s: %m", + "/proc/self/loginuid"); + } + return rc; + } + + count = snprintf(loginuid, sizeof(loginuid), "%lu", (unsigned long)uid); + if (pam_modutil_read(fd, buf, sizeof(buf)) == count && + memcmp(buf, loginuid, count) == 0) { + rc = PAM_SUCCESS; + goto done; /* already correct */ + } + if (lseek(fd, 0, SEEK_SET) == 0 && ftruncate(fd, 0) == 0 && + pam_modutil_write(fd, loginuid, count) == count) { + rc = PAM_SUCCESS; + } else { + if (rc != PAM_IGNORE) { + pam_syslog(pamh, LOG_ERR, "Error writing %s: %m", + "/proc/self/loginuid"); + } + } + done: + close(fd); + return rc; +} + +#ifdef HAVE_LIBAUDIT +/* + * This function is called only if "require_auditd" option is passed. It is + * called after loginuid has been set. The purpose is to disallow logins + * should the audit daemon not be running or crashed. It returns PAM_SUCCESS + * if the audit daemon is running and PAM_SESSION_ERR otherwise. + */ +static int check_auditd(void) +{ + int fd, retval; + + fd = audit_open(); + if (fd < 0) { + /* This is here to let people that build their own kernel + and disable the audit system get in. You get these error + codes only when the kernel doesn't have audit + compiled in. */ + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) + return PAM_SUCCESS; + return PAM_SESSION_ERR; + } + retval = audit_request_status(fd); + if (retval > 0) { + struct audit_reply rep; + int i; + int timeout = 30; /* tenths of seconds */ + fd_set read_mask; + + FD_ZERO(&read_mask); + FD_SET(fd, &read_mask); + + for (i = 0; i < timeout; i++) { + struct timeval t; + int rc; + + t.tv_sec = 0; + t.tv_usec = 100000; + do { + rc = select(fd+1, &read_mask, NULL, NULL, &t); + } while (rc < 0 && errno == EINTR); + + rc = audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING,0); + if (rc > 0) { + /* If we get done or error, break out */ + if (rep.type == NLMSG_DONE || + rep.type == NLMSG_ERROR) + break; + + /* If its not status, keep looping */ + if (rep.type != AUDIT_GET) + continue; + + /* Found it... */ + close(fd); + if (rep.status->pid == 0) + return PAM_SESSION_ERR; + else + return PAM_SUCCESS; + } + } + } + close(fd); + if (retval == -ECONNREFUSED) { + /* This is here to let people that build their own kernel + and disable the audit system get in. ECONNREFUSED is + issued by the kernel when there is "no on listening". */ + return PAM_SUCCESS; + } else if (retval == -EPERM && getuid() != 0) { + /* If we get this, then the kernel supports auditing + * but we don't have enough privilege to write to the + * socket. Therefore, we have already been authenticated + * and we are a common user. Just act as though auditing + * is not enabled. Any other error we take seriously. */ + return PAM_SUCCESS; + } + + return PAM_SESSION_ERR; +} +#endif + +/* + * Initialize audit session for user + */ +static int +_pam_loginuid(pam_handle_t *pamh, int flags UNUSED, +#ifdef HAVE_LIBAUDIT + int argc, const char **argv +#else + int argc UNUSED, const char **argv UNUSED +#endif +) +{ + const char *user = NULL; + struct passwd *pwd; + int ret; +#ifdef HAVE_LIBAUDIT + int require_auditd = 0; +#endif + + /* get user name */ + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name"); + return PAM_SESSION_ERR; + } + + /* get user info */ + if ((pwd = pam_modutil_getpwnam(pamh, user)) == NULL) { + pam_syslog(pamh, LOG_NOTICE, + "error: login user-name '%s' does not exist", user); + return PAM_SESSION_ERR; + } + + ret = set_loginuid(pamh, pwd->pw_uid); + switch (ret) { + case PAM_SUCCESS: + case PAM_IGNORE: + break; + default: + pam_syslog(pamh, LOG_ERR, "set_loginuid failed"); + return ret; + } + +#ifdef HAVE_LIBAUDIT + while (argc-- > 0) { + if (strcmp(*argv, "require_auditd") == 0) + require_auditd = 1; + argv++; + } + + if (require_auditd) { + int rc = check_auditd(); + if (rc != PAM_SUCCESS) + pam_syslog(pamh, LOG_ERR, "required running auditd not detected"); + return rc != PAM_SUCCESS ? rc : ret; + } else +#endif + return ret; +} + +/* + * PAM routines + * + * This is here for vsftpd which doesn't seem to run the session stack + */ +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return _pam_loginuid(pamh, flags, argc, argv); +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + return _pam_loginuid(pamh, flags, argc, argv); +} + +int +pam_sm_close_session(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} diff --git a/modules/pam_loginuid/tst-pam_loginuid b/modules/pam_loginuid/tst-pam_loginuid new file mode 100755 index 0000000..bd1e83b --- /dev/null +++ b/modules/pam_loginuid/tst-pam_loginuid @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_loginuid.so diff --git a/modules/pam_mail/README.xml b/modules/pam_mail/README.xml new file mode 100644 index 0000000..5dc89a8 --- /dev/null +++ b/modules/pam_mail/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_mail.8.xml" xpointer='xpointer(id("pam_mail-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_mail/meson.build b/modules/pam_mail/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_mail/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_mail/pam_mail.8.xml b/modules/pam_mail/pam_mail.8.xml new file mode 100644 index 0000000..2c0c054 --- /dev/null +++ b/modules/pam_mail/pam_mail.8.xml @@ -0,0 +1,277 @@ + + + + pam_mail + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_mail + Inform about available mail + + + + + pam_mail.so + + close + + + debug + + + dir=maildir + + + empty + + + hash=count + + + noenv + + + nopen + + + quiet + + + standard + + + + + + + DESCRIPTION + + + The pam_mail PAM module provides the "you have new mail" + service to the user. It can be plugged into any application + that has credential or session hooks. It gives a single message + indicating the newness of any mail it finds + in the user's mail folder. This module also sets the PAM + environment variable, MAIL, to the + user's mail directory. + + + If the mail spool file (be it /var/mail/$USER + or a pathname given with the parameter) is + a directory then pam_mail assumes it is in the + Maildir format. + + + + + + OPTIONS + + + + + + close + + + + Indicate if the user has any mail also on logout. + + + + + + + debug + + + + Print debug information. + + + + + + + dir=maildir + + + + Look for the user's mail in an alternative location defined by + maildir/<login>. The default + location for mail is /var/mail/<login>. + Note, if the supplied + maildir is prefixed by a '~', the + directory is interpreted as indicating a file in the user's + home directory. + + + + + + + empty + + + + Also print message if user has no mail. + + + + + + + hash=count + + + + Mail directory hash depth. For example, a + hashcount of 2 would + make the mail file be + /var/spool/mail/u/s/user. + + + + + + + noenv + + + + Do not set the MAIL + environment variable. + + + + + + + nopen + + + + Don't print any mail information on login. This flag is + useful to get the MAIL + environment variable set, but to not display any information + about it. + + + + + + + quiet + + + + Only report when there is new mail. + + + + + + + standard + + + + Old style "You have..." format which doesn't show the + mail spool being used. This also implies "empty". + + + + + + + + + + + MODULE TYPES PROVIDED + + The and + (on establishment and + deletion of credentials) module types are provided. + + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_SERVICE_ERR + + + Badly formed arguments. + + + + + + PAM_SUCCESS + + + Success. + + + + + + PAM_USER_UNKNOWN + + + User not known. + + + + + + + + + EXAMPLES + + Add the following line to /etc/pam.d/login to + indicate that the user has new mail when they login to the system. + +session optional pam_mail.so standard + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_mail was written by Andrew G. Morgan <morgan@kernel.org>. + + + + \ No newline at end of file diff --git a/modules/pam_mail/pam_mail.c b/modules/pam_mail/pam_mail.c new file mode 100644 index 0000000..3202ce0 --- /dev/null +++ b/modules/pam_mail/pam_mail.c @@ -0,0 +1,469 @@ +/* + * pam_mail module + * + * Written by Andrew Morgan 1996/3/11 + * $HOME additions by David Kinchlea 1997/1/7 + * mailhash additions by Chris Adams 1998/7/11 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PATHS_H +#include +#endif + +#define DEFAULT_MAIL_DIRECTORY PAM_PATH_MAILDIR +#define MAIL_FILE_FORMAT "%s%s/%s" +#define MAIL_ENV_NAME "MAIL" +#define MAIL_ENV_FORMAT MAIL_ENV_NAME "=%s" + +#include +#include +#include +#include +#include "pam_inline.h" +#include "pam_i18n.h" + +/* argument parsing */ + +#define PAM_DEBUG_ARG 0x0001 +#define PAM_NO_LOGIN 0x0002 +#define PAM_LOGOUT_TOO 0x0004 +#define PAM_NEW_MAIL_DIR 0x0010 +#define PAM_MAIL_SILENT 0x0020 +#define PAM_NO_ENV 0x0040 +#define PAM_HOME_MAIL 0x0100 +#define PAM_EMPTY_TOO 0x0200 +#define PAM_STANDARD_MAIL 0x0400 +#define PAM_QUIET_MAIL 0x1000 + +#define HAVE_NEW_MAIL 0x1 +#define HAVE_OLD_MAIL 0x2 +#define HAVE_NO_MAIL 0x3 +#define HAVE_MAIL 0x4 + +static int +_pam_parse (const pam_handle_t *pamh, int flags, int argc, + const char **argv, const char **maildir, size_t *hashcount) +{ + int ctrl=0; + + if (flags & PAM_SILENT) { + ctrl |= PAM_MAIL_SILENT; + } + + *hashcount = 0; + + /* step through arguments */ + for (; argc-- > 0; ++argv) { + const char *str; + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_DEBUG_ARG; + else if (!strcmp(*argv,"quiet")) + ctrl |= PAM_QUIET_MAIL; + else if (!strcmp(*argv,"standard")) + ctrl |= PAM_STANDARD_MAIL | PAM_EMPTY_TOO; + else if ((str = pam_str_skip_prefix(*argv, "dir=")) != NULL) { + *maildir = str; + if (**maildir != '\0') { + D(("new mail directory: %s", *maildir)); + ctrl |= PAM_NEW_MAIL_DIR; + } else { + pam_syslog(pamh, LOG_ERR, + "dir= specification missing argument - ignored"); + } + } else if ((str = pam_str_skip_prefix(*argv, "hash=")) != NULL) { + char *ep = NULL; + *hashcount = strtoul(str,&ep,10); + if (!ep) { + *hashcount = 0; + } + } else if (!strcmp(*argv,"close")) { + ctrl |= PAM_LOGOUT_TOO; + } else if (!strcmp(*argv,"nopen")) { + ctrl |= PAM_NO_LOGIN; + } else if (!strcmp(*argv,"noenv")) { + ctrl |= PAM_NO_ENV; + } else if (!strcmp(*argv,"empty")) { + ctrl |= PAM_EMPTY_TOO; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + if ((*hashcount != 0) && !(ctrl & PAM_NEW_MAIL_DIR)) { + *maildir = DEFAULT_MAIL_DIRECTORY; + ctrl |= PAM_NEW_MAIL_DIR; + } + + return ctrl; +} + +static int +get_folder(pam_handle_t *pamh, int ctrl, + const char *path_mail, char **folder_p, size_t hashcount, + const struct passwd *pwd) +{ + int retval; + const char *path; + char *folder = NULL; + + if (ctrl & PAM_NEW_MAIL_DIR) { + path = path_mail; + if (*path == '~') { /* support for $HOME delivery */ + /* + * "~/xxx" and "~xxx" are treated as same + */ + if (!*++path || (*path == '/' && !*++path)) { + pam_syslog(pamh, LOG_ERR, + "badly formed mail path [%s]", path_mail); + retval = PAM_SERVICE_ERR; + goto get_folder_cleanup; + } + ctrl |= PAM_HOME_MAIL; + if (hashcount != 0) { + pam_syslog(pamh, LOG_ERR, + "cannot do hash= and home directory mail"); + } + } + } else { + path = DEFAULT_MAIL_DIRECTORY; + } + + /* put folder together */ + + hashcount = hashcount < strlen(pwd->pw_name) ? + hashcount : strlen(pwd->pw_name); + + retval = PAM_BUF_ERR; + if (ctrl & PAM_HOME_MAIL) { + if (asprintf(&folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path) < 0) + goto get_folder_cleanup; + } else { + int rc; + size_t i; + char *hash; + + if ((hash = malloc(2 * hashcount + 1)) == NULL) + goto get_folder_cleanup; + + for (i = 0; i < hashcount; i++) { + hash[2 * i] = '/'; + hash[2 * i + 1] = pwd->pw_name[i]; + } + hash[2 * i] = '\0'; + + rc = asprintf(&folder, MAIL_FILE_FORMAT, path, hash, pwd->pw_name); + pam_overwrite_string(hash); + _pam_drop(hash); + if (rc < 0) + goto get_folder_cleanup; + } + D(("folder=[%s]", folder)); + retval = PAM_SUCCESS; + + /* tidy up */ + + get_folder_cleanup: + path = NULL; + + *folder_p = folder; + folder = NULL; + + if (retval == PAM_BUF_ERR) + pam_syslog(pamh, LOG_CRIT, "out of memory for mail folder"); + + return retval; +} + +static int +get_mail_status(pam_handle_t *pamh, int ctrl, const char *folder) +{ + int type = 0; + struct stat mail_st; + + if (stat(folder, &mail_st) < 0) + return 0; + + if (S_ISDIR(mail_st.st_mode)) { /* Assume Maildir format */ + int i, save_errno; + char *dir; + struct dirent **namelist; + + if (asprintf(&dir, "%s/new", folder) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + goto get_mail_status_cleanup; + } + i = scandir(dir, &namelist, 0, alphasort); + save_errno = errno; + pam_overwrite_string(dir); + _pam_drop(dir); + if (i < 0) { + type = 0; + namelist = NULL; + if (save_errno == ENOMEM) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + goto get_mail_status_cleanup; + } + } + type = (i > 2) ? HAVE_NEW_MAIL : 0; + while (--i >= 0) + _pam_drop(namelist[i]); + _pam_drop(namelist); + if (type == 0) { + if (asprintf(&dir, "%s/cur", folder) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + goto get_mail_status_cleanup; + } + i = scandir(dir, &namelist, 0, alphasort); + save_errno = errno; + pam_overwrite_string(dir); + _pam_drop(dir); + if (i < 0) { + type = 0; + namelist = NULL; + if (save_errno == ENOMEM) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + goto get_mail_status_cleanup; + } + } + if (i > 2) + type = HAVE_OLD_MAIL; + else + type = (ctrl & PAM_EMPTY_TOO) ? HAVE_NO_MAIL : 0; + while (--i >= 0) + _pam_drop(namelist[i]); + _pam_drop(namelist); + } + } else { + if (mail_st.st_size > 0) { + if (mail_st.st_atime < mail_st.st_mtime) /* new */ + type = HAVE_NEW_MAIL; + else /* old */ + type = (ctrl & PAM_STANDARD_MAIL) ? HAVE_MAIL : HAVE_OLD_MAIL; + } else if (ctrl & PAM_EMPTY_TOO) { + type = HAVE_NO_MAIL; + } else { + type = 0; + } + } + + get_mail_status_cleanup: + pam_overwrite_object(&mail_st); + D(("user has %d mail in %s folder", type, folder)); + return type; +} + +static int +report_mail(pam_handle_t *pamh, int ctrl, int type, const char *folder) +{ + int retval; + + if ((ctrl & PAM_MAIL_SILENT) || + ((ctrl & PAM_QUIET_MAIL) && type != HAVE_NEW_MAIL)) + { + D(("keeping quiet")); + retval = PAM_SUCCESS; + } + else + { + if (ctrl & PAM_STANDARD_MAIL) + switch (type) + { + case HAVE_NO_MAIL: + retval = pam_info (pamh, "%s", _("You do not have any new mail.")); + break; + case HAVE_NEW_MAIL: + retval = pam_info (pamh, "%s", _("You have new mail.")); + break; + case HAVE_OLD_MAIL: + retval = pam_info (pamh, "%s", _("You have old mail.")); + break; + case HAVE_MAIL: + default: + retval = pam_info (pamh, "%s", _("You have mail.")); + break; + } + else + switch (type) + { + case HAVE_NO_MAIL: + retval = pam_info (pamh, _("You have no mail in folder %s."), + folder); + break; + case HAVE_NEW_MAIL: + retval = pam_info (pamh, _("You have new mail in folder %s."), + folder); + break; + case HAVE_OLD_MAIL: + retval = pam_info (pamh, _("You have old mail in folder %s."), + folder); + break; + case HAVE_MAIL: + default: + retval = pam_info (pamh, _("You have mail in folder %s."), + folder); + break; + } + } + + D(("returning %s", pam_strerror(pamh, retval))); + return retval; +} + +static int _do_mail(pam_handle_t *, int, int, const char **, int); + +/* --- authentication functions --- */ + +int +pam_sm_authenticate (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +/* Checking mail as part of authentication */ +int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + if (!(flags & (PAM_ESTABLISH_CRED|PAM_DELETE_CRED))) + return PAM_IGNORE; + return _do_mail(pamh,flags,argc,argv,(flags & PAM_ESTABLISH_CRED)); +} + +/* --- session management functions --- */ + +int pam_sm_close_session(pam_handle_t *pamh,int flags,int argc + ,const char **argv) +{ + return _do_mail(pamh,flags,argc,argv,0); +} + +/* Checking mail as part of the session management */ +int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + return _do_mail(pamh,flags,argc,argv,1); +} + + +/* --- The Beaf (Tm) --- */ + +static int _do_mail(pam_handle_t *pamh, int flags, int argc, + const char **argv, int est) +{ + int retval, ctrl, type; + size_t hashcount; + char *folder = NULL; + const char *user; + const char *path_mail = NULL; + const struct passwd *pwd = NULL; + + /* + * this module (un)sets the MAIL environment variable, and checks if + * the user has any new mail. + */ + + ctrl = _pam_parse(pamh, flags, argc, argv, &path_mail, &hashcount); + + retval = pam_get_user(pamh, &user, NULL); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", + pam_strerror(pamh, retval)); + return PAM_USER_UNKNOWN; + } + + pwd = pam_modutil_getpwnam (pamh, user); + if (pwd == NULL) { + pam_syslog(pamh, LOG_NOTICE, "user unknown"); + return PAM_USER_UNKNOWN; + } + + /* which folder? */ + + retval = get_folder(pamh, ctrl, path_mail, &folder, hashcount, pwd); + if (retval != PAM_SUCCESS) { + D(("failed to find folder")); + return retval; + } + + /* set the MAIL variable? */ + + if (!(ctrl & PAM_NO_ENV) && est) { + char *tmp; + + if (asprintf(&tmp, MAIL_ENV_FORMAT, folder) < 0) { + pam_syslog(pamh, LOG_CRIT, + "no memory for " MAIL_ENV_NAME " variable"); + retval = PAM_BUF_ERR; + goto do_mail_cleanup; + } + D(("setting env: %s", tmp)); + retval = pam_putenv(pamh, tmp); + pam_overwrite_string(tmp); + _pam_drop(tmp); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_CRIT, + "unable to set " MAIL_ENV_NAME " variable"); + retval = PAM_BUF_ERR; + goto do_mail_cleanup; + } + } else { + D(("not setting " MAIL_ENV_NAME " variable")); + } + + /* + * OK. we've got the mail folder... what about its status? + */ + + if ((est && !(ctrl & PAM_NO_LOGIN)) + || (!est && (ctrl & PAM_LOGOUT_TOO))) { + PAM_MODUTIL_DEF_PRIVS(privs); + + if (pam_modutil_drop_priv(pamh, &privs, pwd)) { + retval = PAM_SESSION_ERR; + goto do_mail_cleanup; + } else { + type = get_mail_status(pamh, ctrl, folder); + if (pam_modutil_regain_priv(pamh, &privs)) { + retval = PAM_SESSION_ERR; + goto do_mail_cleanup; + } + } + + if (type != 0) { + retval = report_mail(pamh, ctrl, type, folder); + type = 0; + } + } + + /* Delete environment variable? */ + if ( ! est && ! (ctrl & PAM_NO_ENV) ) + (void) pam_putenv(pamh, MAIL_ENV_NAME); + + do_mail_cleanup: + pam_overwrite_string(folder); + _pam_drop(folder); + + /* indicate success or failure */ + + return retval; +} + +/* end of module definition */ diff --git a/modules/pam_mail/tst-pam_mail b/modules/pam_mail/tst-pam_mail new file mode 100755 index 0000000..99fb7ed --- /dev/null +++ b/modules/pam_mail/tst-pam_mail @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_mail.so diff --git a/modules/pam_mkhomedir/README.xml b/modules/pam_mkhomedir/README.xml new file mode 100644 index 0000000..ef99895 --- /dev/null +++ b/modules/pam_mkhomedir/README.xml @@ -0,0 +1,23 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_mkhomedir.8.xml" xpointer='xpointer(id("pam_mkhomedir-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_mkhomedir/meson.build b/modules/pam_mkhomedir/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_mkhomedir/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_mkhomedir/mkhomedir_helper.8.xml b/modules/pam_mkhomedir/mkhomedir_helper.8.xml new file mode 100644 index 0000000..0f4c4b4 --- /dev/null +++ b/modules/pam_mkhomedir/mkhomedir_helper.8.xml @@ -0,0 +1,80 @@ + + + + mkhomedir_helper + 8 + Linux-PAM + Linux-PAM Manual + + + + mkhomedir_helper + Helper binary that creates home directories + + + + + mkhomedir_helper + + user + + + umask + + path-to-skel + + home_mode + + + + + + + + + DESCRIPTION + + + mkhomedir_helper is a helper program for the + pam_mkhomedir module that creates home directories + and populates them with contents of the specified skel directory. + + + + The default value of umask is 0022 and the + default value of path-to-skel is + /etc/skel. The default value of + home_mode is computed from the value of + umask. + + + + The helper is separated from the module to not require direct access from + login SELinux domains to the contents of user home directories. The + SELinux domain transition happens when the module is executing the + mkhomedir_helper. + + + + The helper never touches home directories if they already exist. + + + + + SEE ALSO + + + pam_mkhomedir8 + + + + + + AUTHOR + + Written by Tomas Mraz based on the code originally in + pam_mkhomedir module. + + + + \ No newline at end of file diff --git a/modules/pam_mkhomedir/mkhomedir_helper.c b/modules/pam_mkhomedir/mkhomedir_helper.c new file mode 100644 index 0000000..eefb599 --- /dev/null +++ b/modules/pam_mkhomedir/mkhomedir_helper.c @@ -0,0 +1,452 @@ +/* mkhomedir_helper - helper for pam_mkhomedir module + + Released under the GNU LGPL version 2 or later + + Copyright (c) Red Hat, Inc., 2009 + Originally written by Jason Gunthorpe Feb 1999 + Structure taken from pam_lastlogin by Andrew Morgan + 1996 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct dir_spec { + int fd; + char *path; +}; + +static unsigned long u_mask = 0022; +static const char *skeldir = "/etc/skel"; + +static int create_homedir(struct dir_spec *, const struct passwd *, mode_t, + const char *, const char *); + +static int +dir_spec_open(struct dir_spec *spec, const char *path) +{ + spec->path = strdup(path); + if (spec->path == NULL) + goto fail; + + spec->fd = open(spec->path, O_DIRECTORY); + if (spec->fd == -1) + { + free(spec->path); + goto fail; + } + + return 0; +fail: + spec->path = NULL; + spec->fd = -1; + return -1; +} + +static int +dir_spec_at(struct dir_spec *spec, struct dir_spec *parent, const char *path) +{ + if (asprintf(&spec->path, "%s/%s", parent->path, path) < 0) + goto fail; + spec->fd = openat(parent->fd, path, O_DIRECTORY | O_NOFOLLOW); + if (spec->fd == -1) + { + free(spec->path); + goto fail; + } + + return 0; +fail: + spec->path = NULL; + spec->fd = -1; + return -1; +} + +static void +dir_spec_close(struct dir_spec *spec) +{ + if (spec->fd != -1) + close(spec->fd); + free(spec->path); +} + +static int +copy_entry(struct dir_spec *parent, const struct passwd *pwd, mode_t dir_mode, + const char *source, struct dirent *dent) +{ + char remark[BUFSIZ]; + int srcfd = -1, destfd = -1; + int res; + int retval = PAM_SESSION_ERR; + struct stat st; + char *newsource = NULL; + + /* Determine what kind of file it is. */ + if (asprintf(&newsource, "%s/%s", source, dent->d_name) < 0) + { + pam_syslog(NULL, LOG_CRIT, "asprintf failed for 'newsource'"); + retval = PAM_BUF_ERR; + goto go_out; + } + + if (lstat(newsource, &st) != 0) + { + retval = PAM_SUCCESS; + goto go_out; + } + + /* If it's a directory, recurse. */ + if (S_ISDIR(st.st_mode)) + { + retval = create_homedir(parent, pwd, dir_mode & (~u_mask), newsource, + dent->d_name); + goto go_out; + } + + /* If it's a symlink, create a new link. */ + if (S_ISLNK(st.st_mode)) + { + int pointedlen = 0; +#ifndef PATH_MAX + char *pointed = NULL; + { + int size = 100; + + while (1) + { + pointed = malloc(size); + if (pointed == NULL) + { + retval = PAM_BUF_ERR; + goto go_out; + } + pointedlen = readlink(newsource, pointed, size); + if (pointedlen < 0) break; + if (pointedlen < size) break; + free(pointed); + size *= 2; + } + } + if (pointedlen < 0) + free(pointed); + else + pointed[pointedlen] = 0; +#else + char pointed[PATH_MAX] = {}; + + pointedlen = readlink(newsource, pointed, sizeof(pointed) - 1); +#endif + + if (pointedlen >= 0) + { + if (symlinkat(pointed, parent->fd, dent->d_name) != 0) + { + retval = errno == EEXIST ? PAM_SUCCESS : PAM_PERM_DENIED; + + if (retval != PAM_SUCCESS) + pam_syslog(NULL, LOG_DEBUG, "unable to create link %s/%s: %m", + parent->path, dent->d_name); +#ifndef PATH_MAX + free(pointed); +#endif + goto go_out; + } + + if (fchownat(parent->fd, dent->d_name, pwd->pw_uid, pwd->pw_gid, + AT_SYMLINK_NOFOLLOW) != 0) + { + pam_syslog(NULL, LOG_DEBUG, + "unable to change perms on link %s/%s: %m", + parent->path, dent->d_name); +#ifndef PATH_MAX + free(pointed); +#endif + retval = PAM_PERM_DENIED; + goto go_out; + } +#ifndef PATH_MAX + free(pointed); +#endif + } + retval = PAM_SUCCESS; + goto go_out; + } + + /* If it's not a regular file, it's probably not a good idea to create + * the new device node, FIFO, or whatever it is. */ + if (!S_ISREG(st.st_mode)) + { + retval = PAM_SUCCESS; + goto go_out; + } + + /* Open the source file */ + if ((srcfd = open(newsource, O_RDONLY)) < 0 || fstat(srcfd, &st) != 0) + { + pam_syslog(NULL, LOG_DEBUG, + "unable to open or stat src file %s: %m", newsource); + retval = PAM_PERM_DENIED; + goto go_out; + } + + /* Open the dest file */ + if ((destfd = openat(parent->fd, dent->d_name, + O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600)) < 0) + { + retval = errno == EEXIST ? PAM_SUCCESS : PAM_PERM_DENIED; + if (retval != PAM_SUCCESS) + pam_syslog(NULL, LOG_DEBUG, "unable to open dest file %s/%s: %m", + parent->path, dent->d_name); + goto go_out; + } + + /* Set the proper ownership and permissions for the module. We make + the file a+w and then mask it with the set mask. This preserves + execute bits */ + if (fchmod(destfd, (st.st_mode | 0222) & (~u_mask)) != 0 || + fchown(destfd, pwd->pw_uid, pwd->pw_gid) != 0) + { + pam_syslog(NULL, LOG_DEBUG, "unable to change perms on copy %s/%s: %m", + parent->path, dent->d_name); + retval = PAM_PERM_DENIED; + goto go_out; + } + + /* Copy the file */ + do + { + res = pam_modutil_read(srcfd, remark, sizeof(remark)); + + if (res == 0) + continue; + + if (res > 0) + { + if (pam_modutil_write(destfd, remark, res) == res) + continue; + } + + /* If we get here, pam_modutil_read returned a -1 or + pam_modutil_write returned something unexpected. */ + pam_syslog(NULL, LOG_DEBUG, "unable to perform IO: %m"); + retval = PAM_PERM_DENIED; + goto go_out; + } + while (res != 0); + + retval = PAM_SUCCESS; + + go_out: + if (srcfd >= 0) + close(srcfd); + if (destfd >= 0) + close(destfd); + + free(newsource); + + return retval; +} + +/* Do the actual work of creating a home dir */ +static int +create_homedir(struct dir_spec *parent, const struct passwd *pwd, + mode_t dir_mode, const char *source, const char *dest) +{ + DIR *d = NULL; + struct dirent *dent; + struct dir_spec base; + int retval = PAM_SESSION_ERR; + + /* Create the new directory */ + if (mkdirat(parent->fd, dest, 0700)) + { + if (errno == EEXIST) + return PAM_SUCCESS; + pam_syslog(NULL, LOG_ERR, "unable to create directory %s/%s: %m", + parent->path, dest); + return PAM_PERM_DENIED; + } + + if (dir_spec_at(&base, parent, dest) < 0) + { + retval = PAM_PERM_DENIED; + goto go_out; + } + + /* See if we need to copy the skel dir over. */ + if ((source == NULL) || (strlen(source) == 0)) + { + retval = PAM_SUCCESS; + goto go_out; + } + + /* Scan the directory */ + d = opendir(source); + if (d == NULL) + { + pam_syslog(NULL, LOG_DEBUG, "unable to read directory %s: %m", source); + retval = PAM_PERM_DENIED; + goto go_out; + } + + for (dent = readdir(d); dent != NULL; dent = readdir(d)) + { + /* Skip some files.. */ + if (strcmp(dent->d_name,".") == 0 || + strcmp(dent->d_name,"..") == 0) + continue; + + retval = copy_entry(&base, pwd, dir_mode, source, dent); + if (retval != PAM_SUCCESS) + goto go_out; + } + + retval = PAM_SUCCESS; + + go_out: + if (d != NULL) + closedir(d); + + if (fchmodat(parent->fd, dest, dir_mode, AT_SYMLINK_NOFOLLOW) != 0 || + fchownat(parent->fd, dest, pwd->pw_uid, pwd->pw_gid, + AT_SYMLINK_NOFOLLOW) != 0) + { + pam_syslog(NULL, LOG_DEBUG, + "unable to change perms on directory %s/%s: %m", + parent->path, dest); + retval = PAM_PERM_DENIED; + } + + dir_spec_close(&base); + + return retval; +} + +static int +create_homedir_helper(const struct passwd *_pwd, mode_t home_mode, + const char *_skeldir, const char *_homedir) +{ + int retval = PAM_SESSION_ERR; + struct dir_spec base; + char *cp = strrchr(_homedir, '/'); + + *cp = '\0'; + retval = dir_spec_open(&base, cp == _homedir ? "/" : _homedir); + if (retval < 0) + { + pam_syslog(NULL, LOG_DEBUG, + "unable to open parent of home directory %s: %m", _homedir); + retval = PAM_PERM_DENIED; + goto go_out; + } + *cp = '/'; + + retval = create_homedir(&base, _pwd, home_mode, _skeldir, cp + 1); + + go_out: + dir_spec_close(&base); + return retval; +} + +static int +make_parent_dirs(char *dir, int make) +{ + int rc = PAM_SUCCESS; + char *cp = strrchr(dir, '/'); + struct stat st; + + if (!cp) + return rc; + + if (cp != dir) { + *cp = '\0'; + if (stat(dir, &st) && errno == ENOENT) + rc = make_parent_dirs(dir, 1); + *cp = '/'; + + if (rc != PAM_SUCCESS) + return rc; + } + + if (make && mkdir(dir, 0755) && errno != EEXIST) { + pam_syslog(NULL, LOG_ERR, "unable to create directory %s: %m", dir); + return PAM_PERM_DENIED; + } + + return rc; +} + +int +main(int argc, char *argv[]) +{ + struct passwd *pwd; + struct stat st; + char *eptr; + unsigned long home_mode = 0; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [ [ []]]\n", argv[0]); + return PAM_SESSION_ERR; + } + + pwd = getpwnam(argv[1]); + if (pwd == NULL) { + pam_syslog(NULL, LOG_ERR, "User unknown."); + return PAM_USER_UNKNOWN; + } + + if (argc >= 3) { + errno = 0; + u_mask = strtoul(argv[2], &eptr, 0); + if (errno != 0 || *eptr != '\0') { + pam_syslog(NULL, LOG_ERR, "Bogus umask value %s", argv[2]); + return PAM_SESSION_ERR; + } + } + + if (argc >= 4) { + skeldir = argv[3]; + } + + if (argc >= 5) { + errno = 0; + home_mode = strtoul(argv[4], &eptr, 0); + if (errno != 0 || *eptr != '\0') { + pam_syslog(NULL, LOG_ERR, "Bogus home_mode value %s", argv[4]); + return PAM_SESSION_ERR; + } + } + + if (home_mode == 0) + home_mode = 0777 & ~u_mask; + + if (pwd->pw_dir[0] != '/') { + pam_syslog(NULL, LOG_ERR, "Relative user home directory %s", pwd->pw_dir); + return PAM_SESSION_ERR; + } + + /* Stat the home directory, if something exists then we assume it is + correct and return a success */ + if (stat(pwd->pw_dir, &st) == 0) + return PAM_SUCCESS; + + if (make_parent_dirs(pwd->pw_dir, 0) != PAM_SUCCESS) + return PAM_PERM_DENIED; + + return create_homedir_helper(pwd, home_mode, skeldir, pwd->pw_dir); +} diff --git a/modules/pam_mkhomedir/pam_mkhomedir.8.xml b/modules/pam_mkhomedir/pam_mkhomedir.8.xml new file mode 100644 index 0000000..ad95724 --- /dev/null +++ b/modules/pam_mkhomedir/pam_mkhomedir.8.xml @@ -0,0 +1,216 @@ + + + + pam_mkhomedir + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_mkhomedir + + PAM module to create users home directory + + + + + + + + pam_mkhomedir.so + + silent + + + debug + + + umask=mode + + + skel=skeldir + + + + + + + DESCRIPTION + + The pam_mkhomedir PAM module will create a users home directory + if it does not exist when the session begins. This allows users + to be present in central database (such as NIS, kerberos or LDAP) + without using a distributed file system or pre-creating a large + number of directories. The skeleton directory (usually + /etc/skel/) is used to copy default files + and also sets a umask for the creation. + + + The new users home directory will not be removed after logout + of the user. + + + + + OPTIONS + + + + + silent + + + + Don't print informative messages. + + + + + + + debug + + + + Turns on debugging via + + syslog3 + . + + + + + + + umask=mask + + + + The file mode creation mask is set to + mask. The default value of mask + is 0022. If this option is not specified, then the permissions + of created user home directory is set to the value of + configuration item from + /etc/login.defs. If there is no such + configuration item then the value is computed from the + value of in the same file. If + there is no such configuration option either the default + value of 0755 is used for the mode. + + + + + + + skel=/path/to/skel/directory + + + + Indicate an alternative skel directory + to override the default /etc/skel. + + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_PERM_DENIED + + + Not enough permissions to create the new directory + or read the skel directory. + + + + + PAM_USER_UNKNOWN + + + User not known to the underlying authentication module. + + + + + PAM_SUCCESS + + + Environment variables were set. + + + + + + + + FILES + + + /etc/skel + + Default skel directory + + + + + + + EXAMPLES + + A sample /etc/pam.d/login file: + + auth requisite pam_securetty.so + auth sufficient pam_ldap.so + auth required pam_unix.so + auth required pam_nologin.so + account sufficient pam_ldap.so + account required pam_unix.so + password required pam_unix.so + session required pam_mkhomedir.so skel=/etc/skel/ umask=0022 + session required pam_unix.so + session optional pam_lastlog.so + session optional pam_mail.so standard + + + + + + + SEE ALSO + + + pam.d5 + , + + pam8 + . + + + + + AUTHOR + + pam_mkhomedir was written by Jason Gunthorpe <jgg@debian.org>. + + + \ No newline at end of file diff --git a/modules/pam_mkhomedir/pam_mkhomedir.c b/modules/pam_mkhomedir/pam_mkhomedir.c new file mode 100644 index 0000000..71d02b6 --- /dev/null +++ b/modules/pam_mkhomedir/pam_mkhomedir.c @@ -0,0 +1,268 @@ +/* PAM Make Home Dir module + + This module will create a users home directory if it does not exist + when the session begins. This allows users to be present in central + database (such as nis, kerb or ldap) without using a distributed + file system or pre-creating a large number of directories. + + Here is a sample /etc/pam.d/login file for Debian GNU/Linux + 2.1: + + auth requisite pam_securetty.so + auth sufficient pam_ldap.so + auth required pam_unix.so + auth optional pam_group.so + auth optional pam_mail.so + account requisite pam_time.so + account sufficient pam_ldap.so + account required pam_unix.so + session required pam_mkhomedir.so skel=/etc/skel/ umask=0022 + session required pam_unix.so + session optional pam_lastlog.so + password required pam_unix.so + + Released under the GNU LGPL version 2 or later + Copyright (c) Red Hat, Inc. 2009 + Originally written by Jason Gunthorpe Feb 1999 + Structure taken from pam_lastlogin by Andrew Morgan + 1996 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "pam_cc_compat.h" +#include "pam_inline.h" +#include "pam_i18n.h" + +/* argument parsing */ +#define MKHOMEDIR_DEBUG 020 /* be verbose about things */ +#define MKHOMEDIR_QUIET 040 /* keep quiet about things */ + +#define LOGIN_DEFS "/etc/login.defs" +#define UMASK_DEFAULT "0022" + +struct options_t { + int ctrl; + const char *umask; + const char *skeldir; +}; +typedef struct options_t options_t; + +static void +_pam_parse (const pam_handle_t *pamh, int flags, int argc, const char **argv, + options_t *opt) +{ + opt->ctrl = 0; + opt->umask = NULL; + opt->skeldir = "/etc/skel"; + + /* does the application require quiet? */ + if ((flags & PAM_SILENT) == PAM_SILENT) + opt->ctrl |= MKHOMEDIR_QUIET; + + /* step through arguments */ + for (; argc-- > 0; ++argv) + { + const char *str; + + if (!strcmp(*argv, "silent")) { + opt->ctrl |= MKHOMEDIR_QUIET; + } else if (!strcmp(*argv, "debug")) { + opt->ctrl |= MKHOMEDIR_DEBUG; + } else if ((str = pam_str_skip_prefix(*argv, "umask=")) != NULL) { + opt->umask = str; + } else if ((str = pam_str_skip_prefix(*argv, "skel=")) != NULL) { + opt->skeldir = str; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } +} + +static char* +_pam_conv_str_umask_to_homemode(const char *umask) +{ + unsigned int m = 0; + char tmp[5]; + + m = 0777 & ~strtoul(umask, NULL, 8); + (void) snprintf(tmp, sizeof(tmp), "0%o", m); + return strdup(tmp); +} + +/* Do the actual work of creating a home dir */ +static int +create_homedir (pam_handle_t *pamh, options_t *opt, + const char *user, const char *dir) +{ + int retval, child; + struct sigaction newsa, oldsa; + char *login_umask = NULL; + char *login_homemode = NULL; + + /* Mention what is happening, if the notification fails that is OK */ + if (!(opt->ctrl & MKHOMEDIR_QUIET)) + pam_info(pamh, _("Creating directory '%s'."), dir); + + + D(("called.")); + + if (opt->ctrl & MKHOMEDIR_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "Executing mkhomedir_helper."); + } + + /* fetch UMASK from /etc/login.defs if not in argv */ + if (opt->umask == NULL) { + login_umask = pam_modutil_search_key(pamh, LOGIN_DEFS, "UMASK"); + login_homemode = pam_modutil_search_key(pamh, LOGIN_DEFS, "HOME_MODE"); + if (login_homemode == NULL) { + if (login_umask != NULL) { + login_homemode = _pam_conv_str_umask_to_homemode(login_umask); + } else { + login_homemode = _pam_conv_str_umask_to_homemode(UMASK_DEFAULT); + } + } + } else { + login_homemode = _pam_conv_str_umask_to_homemode(opt->umask); + } + + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + */ + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + + /* fork */ + child = fork(); + if (child == 0) { + static char *envp[] = { NULL }; + const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL }; + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD) < 0) + _exit(PAM_SYSTEM_ERR); + + /* exec the mkhomedir helper */ + args[0] = MKHOMEDIR_HELPER; + args[1] = user; + args[2] = opt->umask ? opt->umask : UMASK_DEFAULT; + args[3] = opt->skeldir; + args[4] = login_homemode; + + DIAG_PUSH_IGNORE_CAST_QUAL; + execve(MKHOMEDIR_HELPER, (char **)args, envp); + DIAG_POP_IGNORE_CAST_QUAL; + + /* should not get here: exit with error */ + D(("helper binary is not available")); + _exit(PAM_SYSTEM_ERR); + } else if (child > 0) { + int rc; + while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR); + if (rc < 0) { + pam_syslog(pamh, LOG_ERR, "waitpid failed: %m"); + retval = PAM_SYSTEM_ERR; + } else if (!WIFEXITED(retval)) { + pam_syslog(pamh, LOG_ERR, "mkhomedir_helper abnormal exit: %d", retval); + retval = PAM_SYSTEM_ERR; + } else { + retval = WEXITSTATUS(retval); + } + } else { + D(("fork failed")); + pam_syslog(pamh, LOG_ERR, "fork failed: %m"); + retval = PAM_SYSTEM_ERR; + } + + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + + if (opt->ctrl & MKHOMEDIR_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "mkhomedir_helper returned %d", retval); + } + + if (retval != PAM_SUCCESS && !(opt->ctrl & MKHOMEDIR_QUIET)) { + pam_error(pamh, _("Unable to create and initialize directory '%s'."), + dir); + } + + free(login_umask); + free(login_homemode); + + D(("returning %d", retval)); + return retval; +} + +/* --- authentication management functions (only) --- */ + +int +pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + int retval; + options_t opt; + const void *user; + const struct passwd *pwd; + struct stat St; + + /* Parse the flag values */ + _pam_parse(pamh, flags, argc, argv, &opt); + + /* Determine the user name so we can get the home directory */ + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') + { + pam_syslog(pamh, LOG_NOTICE, "Cannot obtain the user name."); + return PAM_USER_UNKNOWN; + } + + /* Get the password entry */ + pwd = pam_modutil_getpwnam (pamh, user); + if (pwd == NULL) + { + pam_syslog(pamh, LOG_NOTICE, "User unknown."); + D(("couldn't identify user %s", (const char *) user)); + return PAM_USER_UNKNOWN; + } + + /* Stat the home directory, if something exists then we assume it is + correct and return a success*/ + if (stat(pwd->pw_dir, &St) == 0) { + if (opt.ctrl & MKHOMEDIR_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "Home directory %s already exists.", + pwd->pw_dir); + } + return PAM_SUCCESS; + } + + return create_homedir(pamh, &opt, user, pwd->pw_dir); +} + +/* Ignore */ +int pam_sm_close_session (pam_handle_t * pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} diff --git a/modules/pam_mkhomedir/tst-pam_mkhomedir b/modules/pam_mkhomedir/tst-pam_mkhomedir new file mode 100755 index 0000000..5447883 --- /dev/null +++ b/modules/pam_mkhomedir/tst-pam_mkhomedir @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_mkhomedir.so diff --git a/modules/pam_mkhomedir/tst-pam_mkhomedir-retval.c b/modules/pam_mkhomedir/tst-pam_mkhomedir-retval.c new file mode 100644 index 0000000..282c5cd --- /dev/null +++ b/modules/pam_mkhomedir/tst-pam_mkhomedir-retval.c @@ -0,0 +1,110 @@ +/* + * Check pam_mkhomedir return values. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_mkhomedir" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char user_empty[] = ""; +static const char user_missing[] = ":"; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + struct passwd *pw; + struct stat st; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + /* PAM_USER_UNKNOWN */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_empty, + &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_USER_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_missing, + &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_USER_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_SUCCESS */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so debug\n" + "account required %s/" LTDIR "%s.so debug\n" + "password required %s/" LTDIR "%s.so debug\n" + "session required %s/" LTDIR "%s.so debug\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + if ((pw = getpwuid(geteuid())) != NULL && + pw->pw_dir != NULL && + stat(pw->pw_dir, &st) == 0 && + (st.st_mode & S_IFMT) == S_IFDIR) { + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, + &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + } + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_motd/README.xml b/modules/pam_motd/README.xml new file mode 100644 index 0000000..9e8edad --- /dev/null +++ b/modules/pam_motd/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_motd.8.xml" xpointer='xpointer(id("pam_motd-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_motd/meson.build b/modules/pam_motd/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_motd/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_motd/pam_motd.8.xml b/modules/pam_motd/pam_motd.8.xml new file mode 100644 index 0000000..7442037 --- /dev/null +++ b/modules/pam_motd/pam_motd.8.xml @@ -0,0 +1,212 @@ + + + + pam_motd + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_motd + Display the motd file + + + + + pam_motd.so + + motd=/path/filename + + + motd_dir=/path/dirname.d + + + + + + + DESCRIPTION + + + pam_motd is a PAM module that can be used to display + arbitrary motd (message of the day) files after a successful + login. By default, pam_motd shows files in the + following locations: + + + + /etc/motd + /run/motd + /usr/lib/motd + /etc/motd.d/ + /run/motd.d/ + /usr/lib/motd.d/ + + + + Each message size is limited to 64KB. + + + If /etc/motd does not exist, + then /run/motd is shown. If + /run/motd does not exist, then + /usr/lib/motd is shown. + + + Similar overriding behavior applies to the directories. + Files in /etc/motd.d/ override files + with the same name in /run/motd.d/ and + /usr/lib/motd.d/. Files in /run/motd.d/ + override files with the same name in /usr/lib/motd.d/. + + + Files in the directories listed above are displayed in lexicographic + order by name. Moreover, the files are filtered by reading them with the + credentials of the target user authenticating on the system. + + + To silence a message, + a symbolic link with target /dev/null + may be placed in /etc/motd.d with + the same filename as the message to be silenced. Example: + Creating a symbolic link as follows silences /usr/lib/motd.d/my_motd. + + + ln -s /dev/null /etc/motd.d/my_motd + + + The MOTD_SHOWN=pam environment variable + is set after showing the motd files, even when all of them were silenced + using symbolic links. + + + + + + OPTIONS + + + + motd=/path/filename + + + + The /path/filename file is displayed + as message of the day. Multiple paths to try can be + specified as a colon-separated list. By default this option + is set to /etc/motd:/run/motd:/usr/lib/motd. + + + + + + motd_dir=/path/dirname.d + + + + The /path/dirname.d directory is scanned + and each file contained inside of it is displayed. Multiple + directories to scan can be specified as a colon-separated list. + By default this option is set to /etc/motd.d:/run/motd.d:/usr/lib/motd.d. + + + + + + When no options are given, the default behavior applies for both + options. Specifying either option (or both) will disable the + default behavior for both options. + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_ABORT + + + Not all relevant data or options could be obtained. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_IGNORE + + + This is the default return value of this module. + + + + + + + + EXAMPLES + + The suggested usage for /etc/pam.d/login is: + +session optional pam_motd.so + + + + To use a motd file from a different location: + +session optional pam_motd.so motd=/elsewhere/motd + + + + To use a motd file from elsewhere, along with a + corresponding .d directory: + +session optional pam_motd.so motd=/elsewhere/motd motd_dir=/elsewhere/motd.d + + + + + + SEE ALSO + + + motd5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_motd was written by Ben Collins <bcollins@debian.org>. + + + The option was added by + Allison Karlitskaya <allison.karlitskaya@redhat.com>. + + + + \ No newline at end of file diff --git a/modules/pam_motd/pam_motd.c b/modules/pam_motd/pam_motd.c new file mode 100644 index 0000000..89b2595 --- /dev/null +++ b/modules/pam_motd/pam_motd.c @@ -0,0 +1,480 @@ +/* + * pam_motd module + * + * Modified for pam_motd by Ben Collins + * Written by Michael K. Johnson 1996/10/24 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "pam_inline.h" + +#define DEFAULT_MOTD "/etc/motd:/run/motd:/usr/lib/motd" +#define DEFAULT_MOTD_D "/etc/motd.d:/run/motd.d:/usr/lib/motd.d" + +/* --- session management functions (only) --- */ + +int +pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +static const char default_motd[] = DEFAULT_MOTD; +static const char default_motd_dir[] = DEFAULT_MOTD_D; + +static void try_to_display_fd(pam_handle_t *pamh, int fd) +{ + struct stat st; + char *mtmp = NULL; + + /* fill in message buffer with contents of motd */ + if ((fstat(fd, &st) < 0) || !st.st_size || st.st_size > 0x10000) + return; + + if (!(mtmp = malloc(st.st_size+1))) + return; + + if (pam_modutil_read(fd, mtmp, st.st_size) == st.st_size) { + if (mtmp[st.st_size-1] == '\n') + mtmp[st.st_size-1] = '\0'; + else + mtmp[st.st_size] = '\0'; + + pam_info (pamh, "%s", mtmp); + } + + _pam_drop(mtmp); +} + +/* + * Split a DELIM-separated string ARG into an array. + * Outputs a newly allocated array of strings OUT_ARG_SPLIT + * and the number of strings OUT_NUM_STRS. + * Returns 0 in case of error, 1 in case of success. + */ +static int pam_split_string(const pam_handle_t *pamh, char *arg, char delim, + char ***out_arg_split, size_t *out_num_strs) +{ + char *arg_extracted = NULL; + const char *arg_ptr = arg; + char **arg_split = NULL; + char delim_str[2]; + size_t i = 0; + size_t num_strs = 0; + int retval = 0; + + delim_str[0] = delim; + delim_str[1] = '\0'; + + if (arg == NULL) { + goto out; + } + + while (arg_ptr != NULL) { + num_strs++; + arg_ptr = strchr(arg_ptr + sizeof(const char), delim); + } + + arg_split = calloc(num_strs, sizeof(*arg_split)); + if (arg_split == NULL) { + pam_syslog(pamh, LOG_CRIT, "failed to allocate string array"); + goto out; + } + + arg_extracted = strtok_r(arg, delim_str, &arg); + while (arg_extracted != NULL && i < num_strs) { + arg_split[i++] = arg_extracted; + arg_extracted = strtok_r(NULL, delim_str, &arg); + } + + retval = 1; + + out: + *out_num_strs = num_strs; + *out_arg_split = arg_split; + + return retval; +} + +/* Join A_STR and B_STR, inserting a "/" between them if one is not already trailing + * in A_STR or beginning B_STR. A pointer to a newly allocated string holding the + * joined string is returned in STRP_OUT. + * Returns -1 in case of error, or the number of bytes in the joined string in + * case of success. */ +static int join_dir_strings(char **strp_out, const char *a_str, const char *b_str) +{ + int has_sep = 0; + int retval = -1; + char *join_strp = NULL; + + if (strp_out == NULL || a_str == NULL || b_str == NULL) { + goto out; + } + if (strlen(a_str) == 0) { + goto out; + } + + has_sep = (a_str[strlen(a_str) - 1] == '/') || (b_str[0] == '/'); + + retval = asprintf(&join_strp, "%s%s%s", a_str, + (has_sep == 1) ? "" : "/", b_str); + + if (retval < 0) { + goto out; + } + + *strp_out = join_strp; + + out: + return retval; +} + +static int compare_strings(const void *a, const void *b) +{ + const char *a_str = *(const char * const *)a; + const char *b_str = *(const char * const *)b; + + if (a_str == NULL && b_str == NULL) { + return 0; + } + else if (a_str == NULL) { + return -1; + } + else if (b_str == NULL) { + return 1; + } + else { + return strcmp(a_str, b_str); + } +} + +static void try_to_display_directories_with_overrides(pam_handle_t *pamh, + char **motd_dir_path_split, size_t num_motd_dirs, int report_missing) +{ + struct dirent ***dirscans = NULL; + unsigned int *dirscans_sizes = NULL; + unsigned int dirscans_size_total = 0; + char **dirnames_all = NULL; + size_t i; + unsigned int i_dirnames = 0; + + if (pamh == NULL || motd_dir_path_split == NULL) { + goto out; + } + if (num_motd_dirs < 1) { + goto out; + } + + if ((dirscans = calloc(num_motd_dirs, sizeof(*dirscans))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "failed to allocate dirent arrays"); + goto out; + } + if ((dirscans_sizes = calloc(num_motd_dirs, sizeof(*dirscans_sizes))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "failed to allocate dirent array sizes"); + goto out; + } + + for (i = 0; i < num_motd_dirs; i++) { + int rv; + rv = scandir(motd_dir_path_split[i], &(dirscans[i]), NULL, NULL); + if (rv < 0) { + if (errno != ENOENT || report_missing) { + pam_syslog(pamh, LOG_ERR, "error scanning directory %s: %m", + motd_dir_path_split[i]); + } + } else { + dirscans_sizes[i] = rv; + } + if (dirscans_size_total > UINT_MAX - dirscans_sizes[i]) { + pam_syslog(pamh, LOG_CRIT, "encountered too many motd files"); + goto out; + } + dirscans_size_total += dirscans_sizes[i]; + } + + if (dirscans_size_total == 0) + goto out; + + /* filter out unwanted names, directories, and complement data with lstat() */ + for (i = 0; i < num_motd_dirs; i++) { + struct dirent **d = dirscans[i]; + for (unsigned int j = 0; j < dirscans_sizes[i]; j++) { + int rc; + char *fullpath; + struct stat s; + + switch(d[j]->d_type) { /* the filetype determines how to proceed */ + case DT_REG: /* regular files and */ + case DT_LNK: /* symlinks */ + continue; /* are good. */ + case DT_UNKNOWN: /* for file systems that do not provide */ + /* a filetype, we use lstat() */ + if (join_dir_strings(&fullpath, motd_dir_path_split[i], + d[j]->d_name) <= 0) + break; + rc = lstat(fullpath, &s); + _pam_drop(fullpath); /* free the memory alloc'ed by join_dir_strings */ + if (rc != 0) /* if the lstat() somehow failed */ + break; + + if (S_ISREG(s.st_mode) || /* regular files and */ + S_ISLNK(s.st_mode)) continue; /* symlinks are good */ + break; + case DT_DIR: /* We don't want directories */ + default: /* nor anything else */ + break; + } + _pam_drop(d[j]); /* free memory */ + d[j] = NULL; /* indicate this one was dropped */ + dirscans_size_total--; + } + } + + /* Allocate space for all file names found in the directories, including duplicates. */ + if ((dirnames_all = calloc(dirscans_size_total, sizeof(*dirnames_all))) == NULL) { + pam_syslog(pamh, LOG_CRIT, "failed to allocate dirname array"); + goto out; + } + + for (i = 0; i < num_motd_dirs; i++) { + unsigned int j; + + for (j = 0; j < dirscans_sizes[i]; j++) { + if (NULL != dirscans[i][j]) { + dirnames_all[i_dirnames] = dirscans[i][j]->d_name; + i_dirnames++; + } + } + } + + qsort(dirnames_all, dirscans_size_total, + sizeof(const char *), compare_strings); + + for (i = 0; i < dirscans_size_total; i++) { + unsigned int j; + + if (dirnames_all[i] == NULL) { + continue; + } + + /* Skip duplicate file names. */ + if (i > 0 && strcmp(dirnames_all[i], dirnames_all[i - 1]) == 0) { + continue; + } + + for (j = 0; j < num_motd_dirs; j++) { + char *abs_path = NULL; + int fd; + + if (join_dir_strings(&abs_path, motd_dir_path_split[j], + dirnames_all[i]) < 0 || abs_path == NULL) { + continue; + } + + fd = open(abs_path, O_RDONLY, 0); + _pam_drop(abs_path); + + if (fd >= 0) { + try_to_display_fd(pamh, fd); + close(fd); + + /* We displayed a file, skip to the next file name. */ + break; + } + } + } + + out: + _pam_drop(dirnames_all); + if (dirscans_sizes != NULL) { + for (i = 0; i < num_motd_dirs; i++) { + unsigned int j; + + for (j = 0; j < dirscans_sizes[i]; j++) + _pam_drop(dirscans[i][j]); + _pam_drop(dirscans[i]); + } + _pam_drop(dirscans_sizes); + } + _pam_drop(dirscans); +} + +static int drop_privileges(pam_handle_t *pamh, struct pam_modutil_privs *privs) +{ + struct passwd *pw; + const char *username; + int retval; + + retval = pam_get_user(pamh, &username, NULL); + + if (retval == PAM_SUCCESS) { + pw = pam_modutil_getpwnam (pamh, username); + } else { + return PAM_SESSION_ERR; + } + + if (pw == NULL || pam_modutil_drop_priv(pamh, privs, pw)) { + return PAM_SESSION_ERR; + } + + return PAM_SUCCESS; +} + +static int try_to_display(pam_handle_t *pamh, char **motd_path_split, + size_t num_motd_paths, char **motd_dir_path_split, + size_t num_motd_dir_paths, int report_missing) +{ + PAM_MODUTIL_DEF_PRIVS(privs); + + if (drop_privileges(pamh, &privs) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "Unable to drop privileges"); + return PAM_SESSION_ERR; + } + + if (motd_path_split != NULL) { + size_t i; + + for (i = 0; i < num_motd_paths; i++) { + int fd = open(motd_path_split[i], O_RDONLY, 0); + + if (fd >= 0) { + try_to_display_fd(pamh, fd); + close(fd); + + /* We found and displayed a file, + * move onto next filename. + */ + break; + } + } + } + + if (motd_dir_path_split != NULL) { + try_to_display_directories_with_overrides(pamh, + motd_dir_path_split, + num_motd_dir_paths, + report_missing); + } + + if (pam_modutil_regain_priv(pamh, &privs)) { + pam_syslog(pamh, LOG_ERR, "Unable to regain privileges"); + return PAM_SESSION_ERR; + } + + return PAM_SUCCESS; +} + +int pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int retval = PAM_IGNORE; + const char *motd_path = NULL; + char *motd_path_copy = NULL; + size_t num_motd_paths = 0; + char **motd_path_split = NULL; + const char *motd_dir_path = NULL; + char *motd_dir_path_copy = NULL; + size_t num_motd_dir_paths = 0; + char **motd_dir_path_split = NULL; + int report_missing; + + if (flags & PAM_SILENT) { + return retval; + } + + for (; argc-- > 0; ++argv) { + const char *str; + if ((str = pam_str_skip_prefix(*argv, "motd=")) != NULL) { + + motd_path = str; + if (*motd_path != '\0') { + D(("set motd path: %s", motd_path)); + } else { + motd_path = NULL; + pam_syslog(pamh, LOG_ERR, + "motd= specification missing argument - ignored"); + } + } + else if ((str = pam_str_skip_prefix(*argv, "motd_dir=")) != NULL) { + + motd_dir_path = str; + if (*motd_dir_path != '\0') { + D(("set motd.d path: %s", motd_dir_path)); + } else { + motd_dir_path = NULL; + pam_syslog(pamh, LOG_ERR, + "motd_dir= specification missing argument - ignored"); + } + } + else + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + + if (motd_path == NULL && motd_dir_path == NULL) { + motd_path = default_motd; + motd_dir_path = default_motd_dir; + report_missing = 0; + } else { + report_missing = 1; + } + + if (motd_path != NULL) { + motd_path_copy = strdup(motd_path); + } + + if (motd_path_copy != NULL) { + if (pam_split_string(pamh, motd_path_copy, ':', + &motd_path_split, &num_motd_paths) == 0) { + goto out; + } + } + + if (motd_dir_path != NULL) { + motd_dir_path_copy = strdup(motd_dir_path); + } + + if (motd_dir_path_copy != NULL) { + if (pam_split_string(pamh, motd_dir_path_copy, ':', + &motd_dir_path_split, &num_motd_dir_paths) == 0) { + goto out; + } + } + + retval = try_to_display(pamh, motd_path_split, num_motd_paths, + motd_dir_path_split, num_motd_dir_paths, + report_missing); + + out: + _pam_drop(motd_path_copy); + _pam_drop(motd_path_split); + _pam_drop(motd_dir_path_copy); + _pam_drop(motd_dir_path_split); + + if (retval == PAM_SUCCESS) { + retval = pam_putenv(pamh, "MOTD_SHOWN=pam"); + return retval == PAM_SUCCESS ? PAM_IGNORE : retval; + } else { + return retval; + } +} + +/* end of module definition */ diff --git a/modules/pam_motd/tst-pam_motd b/modules/pam_motd/tst-pam_motd new file mode 100755 index 0000000..155e230 --- /dev/null +++ b/modules/pam_motd/tst-pam_motd @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_motd.so diff --git a/modules/pam_namespace/README.xml b/modules/pam_namespace/README.xml new file mode 100644 index 0000000..f94cb06 --- /dev/null +++ b/modules/pam_namespace/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_namespace.8.xml" xpointer='xpointer(id("pam_namespace-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_namespace/argv_parse.c b/modules/pam_namespace/argv_parse.c new file mode 100644 index 0000000..cbae783 --- /dev/null +++ b/modules/pam_namespace/argv_parse.c @@ -0,0 +1,179 @@ +/* + * argv_parse.c --- utility function for parsing a string into a + * argc, argv array. + * + * This file defines a function argv_parse() which parsing a + * passed-in string, handling double quotes and backslashes, and + * creates an allocated argv vector which can be freed using the + * argv_free() function. + * + * See argv_parse.h for the formal definition of the functions. + * + * Copyright 1999 by Theodore Ts'o. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose with or without fee is hereby granted, provided that + * the above copyright notice and this permission notice appear in all + * copies. THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE + * AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (Isn't + * it sick that the U.S. culture of lawsuit-happy lawyers requires + * this kind of disclaimer?) + * + * Version 1.1, modified 2/27/1999 + */ + +#include "config.h" + +#include +#include +#include +#include +#include "argv_parse.h" + +#define STATE_WHITESPACE 1 +#define STATE_TOKEN 2 +#define STATE_QUOTED 3 + +/* + * Returns 0 on success, -1 on failure. + */ +int argv_parse(const char *in_buf, int *ret_argc, char ***ret_argv) +{ + int argc = 0, max_argc = 0; + char **argv, **new_argv, *buf, ch; + const char *cp = NULL; + char *outcp = NULL; + int state = STATE_WHITESPACE; + + buf = malloc(strlen(in_buf)+1); + if (!buf) + return -1; + + argv = NULL; + outcp = buf; + for (cp = in_buf; (ch = *cp); cp++) { + if (state == STATE_WHITESPACE) { + if (isspace((unsigned char)ch)) + continue; + /* Not whitespace, so start a new token */ + state = STATE_TOKEN; + if (argc >= max_argc) { + if (max_argc >= INT_MAX - 3) { + free(argv); + free(buf); + return -1; + } + max_argc += 3; + new_argv = realloc(argv, + (max_argc+1)*sizeof(char *)); + if (!new_argv) { + free(argv); + free(buf); + return -1; + } + argv = new_argv; + } + argv[argc++] = outcp; + } + if (state == STATE_QUOTED) { + if (ch == '"') + state = STATE_TOKEN; + else + *outcp++ = ch; + continue; + } + /* Must be processing characters in a word */ + if (isspace((unsigned char)ch)) { + /* + * Terminate the current word and start + * looking for the beginning of the next word. + */ + *outcp++ = 0; + state = STATE_WHITESPACE; + continue; + } + if (ch == '"') { + state = STATE_QUOTED; + continue; + } + if (ch == '\\') { + ch = *++cp; + switch (ch) { + case '\0': + ch = '\\'; cp--; break; + case 'n': + ch = '\n'; break; + case 't': + ch = '\t'; break; + case 'b': + ch = '\b'; break; + } + } + *outcp++ = ch; + } + if (state != STATE_WHITESPACE) + *outcp++ = '\0'; + if (ret_argv) { + if (argv == NULL) { + free(buf); + if ((argv=malloc(sizeof(char *))) == NULL) + return -1; + } + argv[argc] = NULL; + *ret_argv = argv; + } else { + free(buf); + free(argv); + } + if (ret_argc) + *ret_argc = argc; + return 0; +} + +void argv_free(char **argv) +{ + if (argv) { + free(*argv); + free(argv); + } +} + +#ifdef DEBUG_ARGV_PARSE +/* + * For debugging + */ + +#include + +int main(int argc, char **argv) +{ + int ac, ret; + char **av, **cpp; + char buf[256]; + + while (!feof(stdin)) { + if (fgets(buf, sizeof(buf), stdin) == NULL) + break; + ret = argv_parse(buf, &ac, &av); + if (ret != 0) { + printf("Argv_parse returned %d!\n", ret); + continue; + } + printf("Argv_parse returned %d arguments...\n", ac); + for (cpp = av; *cpp; cpp++) { + if (cpp != av) + printf(", "); + printf("'%s'", *cpp); + } + printf("\n"); + argv_free(av); + } + exit(0); +} +#endif diff --git a/modules/pam_namespace/argv_parse.h b/modules/pam_namespace/argv_parse.h new file mode 100644 index 0000000..c7878fc --- /dev/null +++ b/modules/pam_namespace/argv_parse.h @@ -0,0 +1,43 @@ +/* + * argv_parse.h --- header file for the argv parser. + * + * This file defines the interface for the functions argv_parse() and + * argv_free(). + * + *********************************************************************** + * int argv_parse(char *in_buf, int *ret_argc, char ***ret_argv) + * + * This function takes as its first argument a string which it will + * parse into an argv argument vector, with each white-space separated + * word placed into its own slot in the argv. This function handles + * double quotes and backslashes so that the parsed words can contain + * special characters. The count of the number words found in the + * parsed string, as well as the argument vector, are returned into + * ret_argc and ret_argv, respectively. + *********************************************************************** + * extern void argv_free(char **argv); + * + * This function frees the argument vector created by argv_parse(). + *********************************************************************** + * + * Copyright 1999 by Theodore Ts'o. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose with or without fee is hereby granted, provided that + * the above copyright notice and this permission notice appear in all + * copies. THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE + * AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (Isn't + * it sick that the U.S. culture of lawsuit-happy lawyers requires + * this kind of disclaimer?) + * + * Version 1.1, modified 2/27/1999 + */ + +extern int argv_parse(const char *in_buf, int *ret_argc, char ***ret_argv); +extern void argv_free(char **argv); diff --git a/modules/pam_namespace/md5.c b/modules/pam_namespace/md5.c new file mode 100644 index 0000000..07ad9a0 --- /dev/null +++ b/modules/pam_namespace/md5.c @@ -0,0 +1,263 @@ +/* + * $Id$ + * + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + */ + +#include "md5.h" +#include + +#include "pam_inline.h" + +#define MD5Name(x) x + +#ifdef WORDS_BIGENDIAN +typedef unsigned char PAM_ATTRIBUTE_ALIGNED(4) uint8_aligned; + +static void byteReverse(uint8_aligned *buf, unsigned longs); + +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(uint8_aligned *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} +#else +#define byteReverse(buf, len) /* Nothing */ +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Name(MD5Init)(struct MD5Context *ctx) +{ + ctx->buf.i[0] = 0x67452301U; + ctx->buf.i[1] = 0xefcdab89U; + ctx->buf.i[2] = 0x98badcfeU; + ctx->buf.i[3] = 0x10325476U; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = ctx->in.c + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in.c, 16); + MD5Name(MD5Transform)(ctx->buf.i, ctx->in.i); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in.c, buf, 64); + byteReverse(ctx->in.c, 16); + MD5Name(MD5Transform)(ctx->buf.i, ctx->in.i); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in.c, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in.c + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in.c, 16); + MD5Name(MD5Transform)(ctx->buf.i, ctx->in.i); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in.c, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in.c, 14); + + /* Append length in bits and transform */ + memcpy(ctx->in.i + 14, ctx->bits, 2*sizeof(uint32)); + + MD5Name(MD5Transform)(ctx->buf.i, ctx->in.i); + byteReverse(ctx->buf.c, 4); + memcpy(digest, ctx->buf.c, 16); + pam_overwrite_object(ctx); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +void MD5Name(MD5)(unsigned const char *buf, unsigned len, unsigned char digest[16]) +{ + struct MD5Context ctx; + MD5Name(MD5Init)(&ctx); + MD5Name(MD5Update)(&ctx, buf, len); + MD5Name(MD5Final)(digest, &ctx); +} diff --git a/modules/pam_namespace/md5.h b/modules/pam_namespace/md5.h new file mode 100644 index 0000000..501aab4 --- /dev/null +++ b/modules/pam_namespace/md5.h @@ -0,0 +1,36 @@ + +#ifndef MD5_H +#define MD5_H + +#include "pam_cc_compat.h" + +typedef unsigned int uint32; + +struct MD5Context { + union { + uint32 i[4]; + unsigned char c[16] PAM_ATTRIBUTE_ALIGNED(4); + } buf; + uint32 bits[2]; + union { + uint32 i[16]; + unsigned char c[64] PAM_ATTRIBUTE_ALIGNED(4); + } in; +}; + +#define MD5_DIGEST_LENGTH 16 + +void MD5Init(struct MD5Context *); +void MD5Update(struct MD5Context *, unsigned const char *, unsigned); +void MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], struct MD5Context *); +void MD5Transform(uint32 buf[4], uint32 const in[MD5_DIGEST_LENGTH]); +void MD5(unsigned const char *, unsigned, unsigned char digest[MD5_DIGEST_LENGTH]); + + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ + +typedef struct MD5Context MD5_CTX; + +#endif /* MD5_H */ diff --git a/modules/pam_namespace/meson.build b/modules/pam_namespace/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_namespace/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_namespace/namespace.conf b/modules/pam_namespace/namespace.conf new file mode 100644 index 0000000..75ec619 --- /dev/null +++ b/modules/pam_namespace/namespace.conf @@ -0,0 +1,31 @@ +# /etc/security/namespace.conf +# +# See /usr/share/doc/pam-*/txts/README.pam_namespace for more information. +# +# Uncommenting the following three lines will polyinstantiate +# /tmp, /var/tmp and user's home directories. /tmp and /var/tmp will +# be polyinstantiated based on the MLS level part of the security context as well as user +# name, Polyinstantion will not be performed for user root and adm for directories +# /tmp and /var/tmp, whereas home directories will be polyinstantiated for all users. +# The user name and context is appended to the instance prefix. +# +# Note that instance directories do not have to reside inside the +# polyinstantiated directory. In the examples below, instances of /tmp +# will be created in /tmp-inst directory, where as instances of /var/tmp +# and users home directories will reside within the directories that +# are being polyinstantiated. +# +# Instance parent directories must exist for the polyinstantiation +# mechanism to work. By default, they should be created with the mode +# of 000. pam_namespace module will enforce this mode unless it +# is explicitly called with an argument to ignore the mode of the +# instance parent. System administrators should use this argument with +# caution, as it will reduce security and isolation achieved by +# polyinstantiation. The parent directories (except $HOME) are created +# at boot by pam_namespace_helper, but in a live system, system +# administrators should create the parent directories before enabling +# them here. +# +#/tmp /tmp-inst/ level root,adm +#/var/tmp /var/tmp/tmp-inst/ level root,adm +#$HOME $HOME/$USER.inst/ level diff --git a/modules/pam_namespace/namespace.conf.5.xml b/modules/pam_namespace/namespace.conf.5.xml new file mode 100644 index 0000000..54f9431 --- /dev/null +++ b/modules/pam_namespace/namespace.conf.5.xml @@ -0,0 +1,240 @@ + + + + namespace.conf + 5 + Linux-PAM + Linux-PAM Manual + + + + namespace.conf + the namespace configuration file + + + + + DESCRIPTION + + + The pam_namespace.so module allows setup of + private namespaces with polyinstantiated directories. + Directories can be polyinstantiated based on user name + or, in the case of SELinux, user name, sensitivity level or complete security context. If an + executable script /etc/security/namespace.init + exists, it is used to initialize the namespace every time an instance + directory is set up and mounted. The script receives the polyinstantiated + directory path and the instance directory path as its arguments. The + script is invoked with full root privileges and accessing the instance directory + in this context needs to be done with caution, as it is controlled by the unprivileged + user for which it has been created. + + + + The /etc/security/namespace.conf file specifies + which directories are polyinstantiated, how they are polyinstantiated, + how instance directories would be named, and any users for whom + polyinstantiation would not be performed. + + + + The /etc/security/namespace.conf file + ( or %vendordir%/security/namespace.conf if it does + not exist) specifies which directories are polyinstantiated, how they are + polyinstantiated, how instance directories would be named, and any users + for whom polyinstantiation would not be performed. + Then individual *.conf files from the + /etc/security/namespace.d/ and + %vendordir%/security/namespace.d directories are taken too. + If /etc/security/namespace.d/@filename@.conf exists, then + %vendordir%/security/namespace.d/@filename@.conf will not be used. + All namespace.d/*.conf files are sorted by their + @filename@.conf in lexicographic order regardless of which + of the directories they reside in. + + + + When someone logs in, the file namespace.conf is + scanned. Comments are marked by # characters. + Each non comment line represents one polyinstantiated + directory. The fields are separated by spaces but can be quoted by + " characters also escape + sequences \b, \n, and + \t are recognized. The fields are as follows: + + + polydir instance_prefix method list_of_uids + + + + The first field, polydir, is the absolute + pathname of the directory to polyinstantiate. The special string + $HOME is replaced with the user's home directory, + and $USER with the username. This field cannot + be blank. + + + + The second field, instance_prefix is + the string prefix used to build the pathname for the instantiation + of <polydir>. The path must end in a trailing slash, or + in a directory prefix used to build the full per-instance path. + Depending on the polyinstantiation method it + is then appended with "instance differentiation string" to generate the + final instance directory path. This directory is created if it did not + exist already, and is then bind mounted on the <polydir> to provide + an instance of <polydir> based on the <method> column. + The special string $HOME is replaced with the + user's home directory, and $USER with the username. + This field cannot be blank. + + + + The third field, method, is the method + used for polyinstantiation. It can take these values; "user" + for polyinstantiation based on user name, "level" for + polyinstantiation based on process MLS level and user name, "context" for + polyinstantiation based on process security context and user name, + "tmpfs" for mounting tmpfs filesystem as an instance dir, and + "tmpdir" for creating temporary directory as an instance dir which is + removed when the user's session is closed. + Methods "context" and "level" are only available with SELinux. This + field cannot be blank. + + + + The fourth field, list_of_uids, is + a comma separated list of user names for whom the polyinstantiation + is not performed. If left blank, polyinstantiation will be performed + for all users. If the list is preceded with a single "~" character, + polyinstantiation is performed only for users in the list. + + + + The method field can contain also following + optional flags separated by : characters. + + + create=mode,owner,group + - create the polyinstantiated directory. The mode, owner and group parameters + are optional. The default for mode is determined by umask, the default + owner is the user whose session is opened, the default group is the + primary group of the user. + + + iscript=path + - path to the instance directory init script. The base directory for relative + paths is /etc/security/namespace.d. + + + noinit + - instance directory init script will not be executed. + + + shared + - the instance directories for "context" and "level" methods will not + contain the user name and will be shared among all users. + + + mntopts=value + - value of this flag is passed to the mount call when the tmpfs mount is + done. It allows for example the specification of the maximum size of the + tmpfs instance that is created by the mount call. In addition to + options specified in the + tmpfs5 + manual the nosuid, + noexec, and nodev flags + can be used to respectively disable setuid bit effect, disable running + executables, and disable devices to be interpreted on the mounted + tmpfs filesystem. + + + + The directory where polyinstantiated instances are to be + created, must exist and must have, by default, the mode of 0000. The + requirement that the instance parent be of mode 0000 can be overridden + with the command line option ignore_instance_parent_mode + + + + In case of context or level polyinstantiation the SELinux context + which is used for polyinstantiation is the context used for executing + a new process as obtained by getexeccon. This context must be set + by the calling application or pam_selinux.so + module. If this context is not set the polyinstantiation will be + based just on user name. + + + + The "instance differentiation string" is <user name> for "user" + method and <user name>_<raw directory context> for "context" + and "level" methods. If the whole string is too long the end of it is + replaced with md5sum of itself. Also when command line option + gen_hash is used the whole string is replaced + with md5sum of itself. + + + + + + EXAMPLES + + These are some example lines which might be specified in + /etc/security/namespace.conf. + + + + # The following three lines will polyinstantiate /tmp, + # /var/tmp and user's home directories. /tmp and /var/tmp + # will be polyinstantiated based on the security level + # as well as user name, whereas home directory will be + # polyinstantiated based on the full security context and user name. + # Polyinstantiation will not be performed for user root + # and adm for directories /tmp and /var/tmp, whereas home + # directories will be polyinstantiated for all users. + # + # Note that instance directories do not have to reside inside + # the polyinstantiated directory. In the examples below, + # instances of /tmp will be created in /tmp-inst directory, + # where as instances of /var/tmp and users home directories + # will reside within the directories that are being + # polyinstantiated. + # + /tmp /tmp-inst/ level root,adm + /var/tmp /var/tmp/tmp-inst/ level root,adm + $HOME $HOME/$USER.inst/inst- context + + + + For the <service>s you need polyinstantiation (login for example) + put the following line in /etc/pam.d/<service> as the last line for + session group: + + + + session required pam_namespace.so [arguments] + + + + This module also depends on pam_selinux.so setting the context. + + + + + + SEE ALSO + + pam_namespace8, + pam.d5, + pam8 + + + + + AUTHORS + + The namespace.conf manual page was written by Janak Desai <janak@us.ibm.com>. + More features added by Tomas Mraz <tmraz@redhat.com>. + + + \ No newline at end of file diff --git a/modules/pam_namespace/namespace.init b/modules/pam_namespace/namespace.init new file mode 100755 index 0000000..1a6b624 --- /dev/null +++ b/modules/pam_namespace/namespace.init @@ -0,0 +1,25 @@ +#!/bin/sh +# It receives polydir path as $1, the instance path as $2, +# a flag whether the instance dir was newly created (0 - no, 1 - yes) in $3, +# and user name in $4. +# +# The following section will copy the contents of /etc/skel if this is a +# newly created home directory. +if [ "$3" = 1 ]; then + # This line will fix the labeling on all newly created directories + [ -x /sbin/restorecon ] && /sbin/restorecon "$1" + user="$4" + passwd=$(getent passwd "$user") + homedir=$(echo "$passwd" | cut -f6 -d":") + if [ "$1" = "$homedir" ]; then + gid=$(echo "$passwd" | cut -f4 -d":") + cp -rT /etc/skel "$homedir" + chown -R "$user":"$gid" "$homedir" + mask=$(sed -E -n 's/^UMASK[[:space:]]+([^#[:space:]]+).*/\1/p' /etc/login.defs) + mode=$(printf "%o" $((0777 & ~mask))) + chmod ${mode:-700} "$homedir" + [ -x /sbin/restorecon ] && /sbin/restorecon -R "$homedir" + fi +fi + +exit 0 diff --git a/modules/pam_namespace/pam_namespace.8.xml b/modules/pam_namespace/pam_namespace.8.xml new file mode 100644 index 0000000..a866d2e --- /dev/null +++ b/modules/pam_namespace/pam_namespace.8.xml @@ -0,0 +1,411 @@ + + + + pam_namespace + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_namespace + + PAM module for configuring namespace for a session + + + + + + + + pam_namespace.so + + debug + + + unmnt_remnt + + + unmnt_only + + + require_selinux + + + gen_hash + + + ignore_config_error + + + ignore_instance_parent_mode + + + unmount_on_close + + + use_current_context + + + use_default_context + + + mount_private + + + + + + + DESCRIPTION + + The pam_namespace PAM module sets up a private namespace for a session + with polyinstantiated directories. A polyinstantiated directory + provides a different instance of itself based on user name, or when + using SELinux, user name, security context or both. If an executable + script /etc/security/namespace.init exists, it + is used to initialize the instance directory after it is set up + and mounted on the polyinstantiated directory. The script receives the + polyinstantiated directory path, the instance directory path, flag + whether the instance directory was newly created (0 for no, 1 for yes), + and the user name as its arguments. The script is invoked with full root + privileges and accessing the instance directory in this context needs to + be done with caution, as it is controlled by the unprivileged user for + which it has been created. + + + + If /etc/security/namespace.init does not exist, + %vendordir%/security/namespace.init is the + alternative to be used for it. + + + + The pam_namespace module disassociates the session namespace from + the parent namespace. Any mounts/unmounts performed in the parent + namespace, such as mounting of devices, are not reflected in the + session namespace. To propagate selected mount/unmount events from + the parent namespace into the disassociated session namespace, an + administrator may use the special shared-subtree feature. For + additional information on shared-subtree feature, please refer to + the mount(8) man page and the shared-subtree description at + http://lwn.net/Articles/159077 and http://lwn.net/Articles/159092. + + + + + + OPTIONS + + + + + debug + + + + A lot of debug information is logged using syslog + + + + + + + unmnt_remnt + + + + For programs such as su and newrole, the login + session has already setup a polyinstantiated + namespace. For these programs, polyinstantiation + is performed based on new user id or security + context, however the command first needs to + undo the polyinstantiation performed by login. + This argument instructs the command to + first undo previous polyinstantiation before + proceeding with new polyinstantiation based on + new id/context + + + + + + + unmnt_only + + + + For trusted programs that want to undo any + existing bind mounts and process instance + directories on their own, this argument allows + them to unmount currently mounted instance + directories + + + + + + + require_selinux + + + + If selinux is not enabled, return failure + + + + + + + gen_hash + + + + Instead of using the security context string + for the instance name, generate and use its + md5 hash. + + + + + + + ignore_config_error + + + + If a line in the configuration file corresponding + to a polyinstantiated directory contains format + error, skip that line process the next line. + Without this option, pam will return an error + to the calling program resulting in termination + of the session. + + + + + + + ignore_instance_parent_mode + + + + Instance parent directories by default are expected to have + the restrictive mode of 000. Using this option, an administrator + can choose to ignore the mode of the instance parent. This option + should be used with caution as it will reduce security and + isolation goals of the polyinstantiation mechanism. + + + + + + + unmount_on_close + + + + Explicitly unmount the polyinstantiated directories instead + of relying on automatic namespace destruction after the last + process in a namespace exits. This option should be used + only in case it is ensured by other means that there cannot be + any processes running in the private namespace left after the + session close. It is also useful only in case there are + multiple pam session calls in sequence from the same process. + + + + + + + use_current_context + + + + Useful for services which do not change the SELinux context + with setexeccon call. The module will use the current SELinux + context of the calling process for the level and context + polyinstantiation. + + + + + + + use_default_context + + + + Useful for services which do not use pam_selinux for changing + the SELinux context with setexeccon call. The module will use + the default SELinux context of the user for the level and context + polyinstantiation. + + + + + + + mount_private + + + + This option can be used on systems where the / mount point or + its submounts are made shared (for example with a + mount --make-rshared / command). + The module will mark the whole directory tree so any mount and + unmount operations in the polyinstantiation namespace are private. + Normally the pam_namespace will try to detect the + shared / mount point and make the polyinstantiated directories + private automatically. This option has to be used just when + only a subtree is shared and / is not. + + + Note that mounts and unmounts done in the private namespace will not + affect the parent namespace if this option is used or when the + shared / mount point is autodetected. + + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + The module must not be called from multithreaded processes. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + Namespace setup was successful. + + + + + PAM_SERVICE_ERR + + + Unexpected system error occurred while setting up namespace. + + + + + PAM_SESSION_ERR + + + Unexpected namespace configuration error occurred. + + + + + + + + FILES + + + /etc/security/namespace.conf + + Main configuration file + + + + + %vendordir%/security/namespace.conf + + Default configuration file if + /etc/security/namespace.conf does not exist. + + + + + /etc/security/namespace.d + + Directory for additional configuration files + + + + + %vendordir%/security/namespace.d + + Directory for additional vendor specific configuration files. + + + + + /etc/security/namespace.init + + Init script for instance directories + + + + + %vendordir%/security/namespace.init + + Vendor init script for instance directories if + /etc/security/namespace.init does not exist. + + + + + + + + EXAMPLES + + + For the <service>s you need polyinstantiation (login for example) + put the following line in /etc/pam.d/<service> as the last line for + session group: + + + + session required pam_namespace.so [arguments] + + + + To use polyinstantiation with graphical display manager gdm, please refer + to gdm's documentation. + + + + + + SEE ALSO + + + namespace.conf5 + , + + pam.d5 + , + + mount8 + , + + pam8 + . + + + + + AUTHORS + + The namespace setup scheme was designed by Stephen Smalley, Janak Desai + and Chad Sellers. + The pam_namespace PAM module was developed by Janak Desai <janak@us.ibm.com>, + Chad Sellers <csellers@tresys.com> and Steve Grubb <sgrubb@redhat.com>. + Additional improvements by Xavier Toth <txtoth@gmail.com> and Tomas Mraz + <tmraz@redhat.com>. + + + \ No newline at end of file diff --git a/modules/pam_namespace/pam_namespace.c b/modules/pam_namespace/pam_namespace.c new file mode 100644 index 0000000..ba7910f --- /dev/null +++ b/modules/pam_namespace/pam_namespace.c @@ -0,0 +1,2373 @@ +/****************************************************************************** + * A module for Linux-PAM that will set the default namespace after + * establishing a session via PAM. + * + * (C) Copyright IBM Corporation 2005 + * (C) Copyright Red Hat, Inc. 2006, 2008 + * All Rights Reserved. + * + * Written by: Janak Desai + * With Revisions by: Steve Grubb + * Contributions by: Xavier Toth , + * Tomas Mraz + * Derived from a namespace setup patch by Chad Sellers + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#define _ATFILE_SOURCE + +#include "config.h" +#include +#include "pam_cc_compat.h" +#include "pam_inline.h" +#include "pam_namespace.h" +#include "argv_parse.h" + +/* --- evaluating all files in VENDORDIR/security/namespace.d and /etc/security/namespace.d --- */ +static const char *base_name(const char *path) +{ + const char *base = strrchr(path, '/'); + return base ? base+1 : path; +} + +static int +compare_filename(const void *a, const void *b) +{ + return strcmp(base_name(* (char * const *) a), + base_name(* (char * const *) b)); +} + +static void close_fds_pre_exec(struct instance_data *idata) +{ + if (pam_modutil_sanitize_helper_fds(idata->pamh, PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_IGNORE_FD, PAM_MODUTIL_IGNORE_FD) < 0) { + _exit(1); + } +} + +/* Evaluating a list of files which have to be parsed in the right order: + * + * - If etc/security/namespace.d/@filename@.conf exists, then + * %vendordir%/security/namespace.d/@filename@.conf should not be used. + * - All files in both namespace.d directories are sorted by their @filename@.conf in + * lexicographic order regardless of which of the directories they reside in. */ +static char **read_namespace_dir(struct instance_data *idata) +{ + glob_t globbuf; + size_t i=0; + int glob_rv = glob(NAMESPACE_D_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf); + char **file_list; + size_t file_list_size = glob_rv == 0 ? globbuf.gl_pathc : 0; + +#ifdef VENDOR_NAMESPACE_D_GLOB + glob_t globbuf_vendor; + int glob_rv_vendor = glob(VENDOR_NAMESPACE_D_GLOB, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf_vendor); + if (glob_rv_vendor == 0) + file_list_size += globbuf_vendor.gl_pathc; +#endif + file_list = malloc((file_list_size + 1) * sizeof(char*)); + if (file_list == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "Cannot allocate memory for file list: %m"); +#ifdef VENDOR_NAMESPACE_D_GLOB + if (glob_rv_vendor == 0) + globfree(&globbuf_vendor); +#endif + if (glob_rv == 0) + globfree(&globbuf); + return NULL; + } + + if (glob_rv == 0) { + for (i = 0; i < globbuf.gl_pathc; i++) { + file_list[i] = strdup(globbuf.gl_pathv[i]); + if (file_list[i] == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "strdup failed: %m"); + break; + } + } + } +#ifdef VENDOR_NAMESPACE_D_GLOB + if (glob_rv_vendor == 0) { + for (size_t j = 0; j < globbuf_vendor.gl_pathc; j++) { + if (glob_rv == 0 && globbuf.gl_pathc > 0) { + int double_found = 0; + for (size_t k = 0; k < globbuf.gl_pathc; k++) { + if (strcmp(base_name(globbuf.gl_pathv[k]), + base_name(globbuf_vendor.gl_pathv[j])) == 0) { + double_found = 1; + break; + } + } + if (double_found) + continue; + } + file_list[i] = strdup(globbuf_vendor.gl_pathv[j]); + if (file_list[i] == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "strdup failed: %m"); + break; + } + i++; + } + globfree(&globbuf_vendor); + } +#endif + file_list[i] = NULL; + qsort(file_list, i, sizeof(char *), compare_filename); + if (glob_rv == 0) + globfree(&globbuf); + + return file_list; +} + +/* + * Adds an entry for a polyinstantiated directory to the linked list of + * polyinstantiated directories. It is called from process_line() while + * parsing the namespace configuration file. + */ +static void add_polydir_entry(struct instance_data *idata, + struct polydir_s *ent) +{ + /* Now attach to linked list */ + ent->next = NULL; + if (idata->polydirs_ptr == NULL) + idata->polydirs_ptr = ent; + else { + struct polydir_s *tail; + + tail = idata->polydirs_ptr; + while (tail->next) + tail = tail->next; + tail->next = ent; + } +} + +static void del_polydir(struct polydir_s *poly) +{ + if (poly) { + free(poly->uid); + free(poly->init_script); + free(poly->mount_opts); + free(poly); + } +} + +/* + * Deletes all the entries in the linked list. + */ +static void del_polydir_list(struct polydir_s *polydirs_ptr) +{ + struct polydir_s *dptr = polydirs_ptr; + + while (dptr) { + struct polydir_s *tptr = dptr; + dptr = dptr->next; + del_polydir(tptr); + } +} + +static void unprotect_dirs(struct protect_dir_s *dir) +{ + struct protect_dir_s *next; + + while (dir != NULL) { + umount(dir->dir); + free(dir->dir); + next = dir->next; + free(dir); + dir = next; + } +} + +static void cleanup_polydir_data(pam_handle_t *pamh UNUSED , void *data, int err UNUSED) +{ + del_polydir_list(data); +} + +static void cleanup_protect_data(pam_handle_t *pamh UNUSED , void *data, int err UNUSED) +{ + unprotect_dirs(data); +} + +static char *expand_variables(const char *orig, const char *const var_names[], const char *var_values[]) +{ + const char *src = orig; + char *dst; + char *expanded; + char c; + size_t dstlen = 0; + while (*src) { + if (*src == '$') { + int i; + for (i = 0; var_names[i]; i++) { + size_t namelen = strlen(var_names[i]); + if (strncmp(var_names[i], src+1, namelen) == 0) { + dstlen += strlen(var_values[i]) - 1; /* $ */ + src += namelen; + break; + } + } + } + ++dstlen; + ++src; + } + if ((dst=expanded=malloc(dstlen + 1)) == NULL) + return NULL; + src = orig; + while ((c=*src) != '\0') { + if (c == '$') { + int i; + for (i = 0; var_names[i]; i++) { + size_t namelen = strlen(var_names[i]); + if (strncmp(var_names[i], src+1, namelen) == 0) { + dst = stpcpy(dst, var_values[i]); + --dst; + c = *dst; /* replace $ */ + src += namelen; + break; + } + } + } + *dst = c; + ++dst; + ++src; + } + *dst = '\0'; + return expanded; +} + +static int parse_create_params(char *params, struct polydir_s *poly) +{ + char *next; + struct passwd *pwd = NULL; + struct group *grp; + + poly->mode = (mode_t)ULONG_MAX; + poly->owner = (uid_t)ULONG_MAX; + poly->group = (gid_t)ULONG_MAX; + + if (*params != '=') + return 0; + params++; + + next = strchr(params, ','); + if (next != NULL) { + *next = '\0'; + next++; + } + + if (*params != '\0') { + errno = 0; + poly->mode = (mode_t)strtoul(params, NULL, 0); + if (errno != 0) { + poly->mode = (mode_t)ULONG_MAX; + } + } + + params = next; + if (params == NULL) + return 0; + next = strchr(params, ','); + if (next != NULL) { + *next = '\0'; + next++; + } + + if (*params != '\0') { + pwd = getpwnam(params); /* session modules are not reentrant */ + if (pwd == NULL) + return -1; + poly->owner = pwd->pw_uid; + } + + params = next; + if (params == NULL || *params == '\0') { + if (pwd != NULL) + poly->group = pwd->pw_gid; + return 0; + } + grp = getgrnam(params); + if (grp == NULL) + return -1; + poly->group = grp->gr_gid; + + return 0; +} + +static int parse_iscript_params(char *params, struct polydir_s *poly) +{ + if (*params != '=') + return 0; + params++; + + if (*params != '\0') { + if (*params != '/') { /* path is relative to NAMESPACE_D_DIR */ + if (asprintf(&poly->init_script, "%s%s", NAMESPACE_D_DIR, params) == -1) + return -1; + } else { + poly->init_script = strdup(params); + } + if (poly->init_script == NULL) + return -1; + } + return 0; +} + +struct mntflag { + const char *name; + size_t len; + unsigned long flag; +}; + +#define LITERAL_AND_LEN(x) x, sizeof(x) - 1 + +static const struct mntflag mntflags[] = { + { LITERAL_AND_LEN("noexec"), MS_NOEXEC }, + { LITERAL_AND_LEN("nosuid"), MS_NOSUID }, + { LITERAL_AND_LEN("nodev"), MS_NODEV } + }; + +static int filter_mntopts(const char *opts, char **filtered, + unsigned long *mountflags) +{ + size_t origlen = strlen(opts); + const char *end; + char *dest; + + dest = *filtered = NULL; + *mountflags = 0; + + if (origlen == 0) + return 0; + + do { + size_t len; + unsigned int i; + + end = strchr(opts, ','); + if (end == NULL) { + len = strlen(opts); + } else { + len = end - opts; + } + + for (i = 0; i < PAM_ARRAY_SIZE(mntflags); i++) { + if (mntflags[i].len != len) + continue; + if (memcmp(mntflags[i].name, opts, len) == 0) { + *mountflags |= mntflags[i].flag; + opts = end; + break; + } + } + + if (opts != end) { + if (dest != NULL) { + *dest = ','; + ++dest; + } else { + dest = *filtered = calloc(1, origlen + 1); + if (dest == NULL) + return -1; + } + memcpy(dest, opts, len); + dest += len; + } + + opts = end + 1; + } while (end != NULL); + + return 0; +} + +static int parse_method(char *method, struct polydir_s *poly, + struct instance_data *idata) +{ + enum polymethod pm; + char *sptr = NULL; + static const char *const method_names[] = { "user", "context", "level", "tmpdir", + "tmpfs", NULL }; + static const char *const flag_names[] = { "create", "noinit", "iscript", + "shared", "mntopts", NULL }; + static const unsigned int flag_values[] = { POLYDIR_CREATE, POLYDIR_NOINIT, + POLYDIR_ISCRIPT, POLYDIR_SHARED, POLYDIR_MNTOPTS }; + int i; + char *flag; + + method = strtok_r(method, ":", &sptr); + pm = NONE; + + for (i = 0; method_names[i]; i++) { + if (strcmp(method, method_names[i]) == 0) { + pm = i + 1; /* 0 = NONE */ + } + } + + if (pm == NONE) { + pam_syslog(idata->pamh, LOG_NOTICE, "Unknown method"); + return -1; + } + + poly->method = pm; + + while ((flag=strtok_r(NULL, ":", &sptr)) != NULL) { + for (i = 0; flag_names[i]; i++) { + size_t namelen = strlen(flag_names[i]); + + if (strncmp(flag, flag_names[i], namelen) == 0) { + poly->flags |= flag_values[i]; + switch (flag_values[i]) { + case POLYDIR_CREATE: + if (parse_create_params(flag+namelen, poly) != 0) { + pam_syslog(idata->pamh, LOG_CRIT, "Invalid create parameters"); + return -1; + } + break; + + case POLYDIR_ISCRIPT: + if (parse_iscript_params(flag+namelen, poly) != 0) { + pam_syslog(idata->pamh, LOG_CRIT, "Memory allocation error"); + return -1; + }; + break; + + case POLYDIR_MNTOPTS: + if (flag[namelen] != '=') + break; + if (poly->method != TMPFS) { + pam_syslog(idata->pamh, LOG_WARNING, "Mount options applicable only to tmpfs method"); + break; + } + free(poly->mount_opts); /* if duplicate mntopts specified */ + poly->mount_opts = NULL; + if (filter_mntopts(flag+namelen+1, &poly->mount_opts, &poly->mount_flags) != 0) { + pam_syslog(idata->pamh, LOG_CRIT, "Memory allocation error"); + return -1; + } + break; + } + } + } + } + + return 0; +} + +/* + * Called from parse_config_file, this function processes a single line + * of the namespace configuration file. It skips over comments and incomplete + * or malformed lines. It processes a valid line with information on + * polyinstantiating a directory by populating appropriate fields of a + * polyinstantiated directory structure and then calling add_polydir_entry to + * add that entry to the linked list of polyinstantiated directories. + */ +static int process_line(char *line, const char *home, const char *rhome, + struct instance_data *idata) +{ + char *dir = NULL, *instance_prefix = NULL, *rdir = NULL; + char *method, *uids; + char *tptr; + struct polydir_s *poly; + int retval = 0; + char **config_options = NULL; + static const char *const var_names[] = {"HOME", "USER", NULL}; + const char *var_values[] = {home, idata->user}; + const char *rvar_values[] = {rhome, idata->ruser}; + size_t len; + + /* + * skip the leading white space + */ + while (*line && isspace((unsigned char)*line)) + line++; + + /* + * Rip off the comments + */ + tptr = strchr(line,'#'); + if (tptr) + *tptr = '\0'; + + /* + * Rip off the newline char + */ + tptr = strchr(line,'\n'); + if (tptr) + *tptr = '\0'; + + /* + * Anything left ? + */ + if (line[0] == 0) + return 0; + + poly = calloc(1, sizeof(*poly)); + if (poly == NULL) + goto erralloc; + + /* + * Initialize and scan the five strings from the line from the + * namespace configuration file. + */ + retval = argv_parse(line, NULL, &config_options); + if (retval != 0) { + goto erralloc; + } + + dir = config_options[0]; + if (dir == NULL) { + pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing polydir"); + goto skipping; + } + instance_prefix = config_options[1]; + if (instance_prefix == NULL) { + pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing instance_prefix"); + dir = NULL; + goto skipping; + } + method = config_options[2]; + if (method == NULL) { + pam_syslog(idata->pamh, LOG_NOTICE, "Invalid line missing method"); + instance_prefix = NULL; + dir = NULL; + goto skipping; + } + + /* + * Only the uids field is allowed to be blank, to indicate no + * override users for polyinstantiation of that directory. If + * any of the other fields are blank, the line is incomplete so + * skip it. + */ + uids = config_options[3]; + + /* + * Expand $HOME and $USER in poly dir and instance dir prefix + */ + if ((rdir=expand_variables(dir, var_names, rvar_values)) == NULL) { + instance_prefix = NULL; + dir = NULL; + goto erralloc; + } + + if ((dir=expand_variables(dir, var_names, var_values)) == NULL) { + instance_prefix = NULL; + goto erralloc; + } + + if ((instance_prefix=expand_variables(instance_prefix, var_names, var_values)) + == NULL) { + goto erralloc; + } + + if (idata->flags & PAMNS_DEBUG) { + pam_syslog(idata->pamh, LOG_DEBUG, "Expanded polydir: '%s'", dir); + pam_syslog(idata->pamh, LOG_DEBUG, "Expanded ruser polydir: '%s'", rdir); + pam_syslog(idata->pamh, LOG_DEBUG, "Expanded instance prefix: '%s'", instance_prefix); + } + + len = strlen(dir); + if (len > 0 && dir[len-1] == '/') { + dir[len-1] = '\0'; + } + + len = strlen(rdir); + if (len > 0 && rdir[len-1] == '/') { + rdir[len-1] = '\0'; + } + + if (dir[0] == '\0' || rdir[0] == '\0') { + pam_syslog(idata->pamh, LOG_NOTICE, "Invalid polydir"); + goto skipping; + } + + /* + * Populate polyinstantiated directory structure with appropriate + * pathnames and the method with which to polyinstantiate. + */ + if (parse_method(method, poly, idata) != 0) { + goto skipping; + } + +#define COPY_STR(dst, src, apd) \ + (snprintf((dst), sizeof(dst), "%s%s", (src), (apd)) != \ + (ssize_t) (strlen(src) + strlen(apd))) + + if (COPY_STR(poly->dir, dir, "") + || COPY_STR(poly->rdir, rdir, "") + || COPY_STR(poly->instance_prefix, instance_prefix, + poly->method == TMPDIR ? "XXXXXX" : "")) { + pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames too long"); + goto skipping; + } + + /* + * Ensure that all pathnames are absolute path names. + */ + if ((poly->dir[0] != '/') || (poly->method != TMPFS && poly->instance_prefix[0] != '/')) { + pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames must start with '/'"); + goto skipping; + } + if (strstr(dir, "..") || strstr(poly->instance_prefix, "..")) { + pam_syslog(idata->pamh, LOG_NOTICE, "Pathnames must not contain '..'"); + goto skipping; + } + + /* + * If the line in namespace.conf for a directory to polyinstantiate + * contains a list of override users (users for whom polyinstantiation + * is not performed), read the user ids, convert names into uids, and + * add to polyinstantiated directory structure. + */ + if (uids) { + uid_t *uidptr; + const char *ustr, *sstr; + size_t count, i; + + if (*uids == '~') { + poly->flags |= POLYDIR_EXCLUSIVE; + uids++; + } + for (count = 0, ustr = sstr = uids; sstr; ustr = sstr + 1, count++) + sstr = strchr(ustr, ','); + + if (count > UINT_MAX || count > SIZE_MAX / sizeof(uid_t)) { + pam_syslog(idata->pamh, LOG_ERR, "Too many uids encountered in configuration"); + goto skipping; + } + + poly->num_uids = count; + poly->uid = malloc(count * sizeof (uid_t)); + uidptr = poly->uid; + if (uidptr == NULL) { + goto erralloc; + } + + ustr = uids; + for (i = 0; i < count; i++) { + struct passwd *pwd; + + tptr = strchr(ustr, ','); + if (tptr) + *tptr = '\0'; + + pwd = pam_modutil_getpwnam(idata->pamh, ustr); + if (pwd == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "Unknown user %s in configuration", ustr); + poly->num_uids--; + } else { + *uidptr = pwd->pw_uid; + uidptr++; + } + ustr = tptr + 1; + } + } + + /* + * Add polyinstantiated directory structure to the linked list + * of all polyinstantiated directory structures. + */ + add_polydir_entry(idata, poly); + + goto out; + +erralloc: + pam_syslog(idata->pamh, LOG_CRIT, "Memory allocation error"); + +skipping: + if (idata->flags & PAMNS_IGN_CONFIG_ERR) + retval = 0; + else + retval = PAM_SERVICE_ERR; + del_polydir(poly); +out: + free(rdir); + free(dir); + free(instance_prefix); + argv_free(config_options); + return retval; +} + + +/* + * Parses /etc/security/namespace.conf file to build a linked list of + * polyinstantiated directory structures of type polydir_s. Each entry + * in the linked list contains information needed to polyinstantiate + * one directory. + */ +static int parse_config_file(struct instance_data *idata) +{ + FILE *fil; + char *home, *rhome; + const char *confname; + struct passwd *cpwd; + char *line; + int retval; + size_t len = 0; + size_t n; + + /* + * Extract the user's home directory to resolve $HOME entries + * in the namespace configuration file. + */ + cpwd = pam_modutil_getpwnam(idata->pamh, idata->user); + if (!cpwd) { + pam_syslog(idata->pamh, LOG_ERR, + "Error getting home dir for '%s'", idata->user); + return PAM_SESSION_ERR; + } + if ((home=strdup(cpwd->pw_dir)) == NULL) { + pam_syslog(idata->pamh, LOG_CRIT, + "Memory allocation error"); + return PAM_SESSION_ERR; + } + + cpwd = pam_modutil_getpwnam(idata->pamh, idata->ruser); + if (!cpwd) { + pam_syslog(idata->pamh, LOG_ERR, + "Error getting home dir for '%s'", idata->ruser); + free(home); + return PAM_SESSION_ERR; + } + + if ((rhome=strdup(cpwd->pw_dir)) == NULL) { + pam_syslog(idata->pamh, LOG_CRIT, + "Memory allocation error"); + free(home); + return PAM_SESSION_ERR; + } + + /* + * Open configuration file, read one line at a time and call + * process_line to process each line. + */ + + confname = PAM_NAMESPACE_CONFIG; +#ifdef VENDOR_PAM_NAMESPACE_CONFIG + /* Check whether PAM_NAMESPACE_CONFIG file is available. + * If it does not exist, fall back to VENDOR_PAM_NAMESPACE_CONFIG file. */ + struct stat buffer; + if (stat(confname, &buffer) != 0 && errno == ENOENT) { + confname = VENDOR_PAM_NAMESPACE_CONFIG; + } +#endif + char **filename_list = read_namespace_dir(idata); + n = 0; + for (;;) { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "Parsing config file %s", + confname); + fil = fopen(confname, "r"); + if (fil == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "Error opening config file %s", + confname); + free(rhome); + free(home); + return PAM_SERVICE_ERR; + } + + /* Use unlocked IO */ + __fsetlocking(fil, FSETLOCKING_BYCALLER); + + line = NULL; + /* loop reading the file */ + while (getline(&line, &len, fil) > 0) { + retval = process_line(line, home, rhome, idata); + if (retval) { + pam_syslog(idata->pamh, LOG_ERR, + "Error processing conf file %s line %s", confname, line); + fclose(fil); + free(line); + free(rhome); + free(home); + return PAM_SERVICE_ERR; + } + } + fclose(fil); + free(line); + + if (filename_list == NULL || filename_list[n] == NULL) + break; + + confname = filename_list[n++]; + } + + if (filename_list != NULL) { + for (size_t i = 0; filename_list[i] != NULL; i++) + free(filename_list[i]); + free(filename_list); + } + + free(rhome); + free(home); + + /* All done...just some debug stuff */ + if (idata->flags & PAMNS_DEBUG) { + struct polydir_s *dptr = idata->polydirs_ptr; + uid_t *iptr; + uid_t i; + + pam_syslog(idata->pamh, LOG_DEBUG, + dptr?"Configured poly dirs:":"No configured poly dirs"); + while (dptr) { + pam_syslog(idata->pamh, LOG_DEBUG, "dir='%s' iprefix='%s' meth=%d", + dptr->dir, dptr->instance_prefix, dptr->method); + for (i = 0, iptr = dptr->uid; i < dptr->num_uids; i++, iptr++) + pam_syslog(idata->pamh, LOG_DEBUG, "override user %d ", *iptr); + dptr = dptr->next; + } + } + + return PAM_SUCCESS; +} + + +/* + * This function returns true if a given uid is present in the polyinstantiated + * directory's list of override uids. If the uid is one of the override + * uids for the polyinstantiated directory, polyinstantiation is not + * performed for that user for that directory. + * If exclusive is set the returned values are opposite. + */ +static int ns_override(struct polydir_s *polyptr, struct instance_data *idata, + uid_t uid) +{ + unsigned int i; + + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Checking for ns override in dir %s for uid %d", + polyptr->dir, uid); + + for (i = 0; i < polyptr->num_uids; i++) { + if (uid == polyptr->uid[i]) { + return !(polyptr->flags & POLYDIR_EXCLUSIVE); + } + } + + return !!(polyptr->flags & POLYDIR_EXCLUSIVE); +} + +/* + * md5hash generates a hash of the passed in instance directory name. + */ +static char *md5hash(const char *instname, struct instance_data *idata) +{ + int i; + char *md5inst = NULL; + char *to; + unsigned char inst_digest[MD5_DIGEST_LENGTH]; + + /* + * Create MD5 hashes for instance pathname. + */ + + MD5((const unsigned char *)instname, strlen(instname), inst_digest); + + if ((md5inst = malloc(MD5_DIGEST_LENGTH * 2 + 1)) == NULL) { + pam_syslog(idata->pamh, LOG_CRIT, "Unable to allocate buffer"); + return NULL; + } + + to = md5inst; + for (i = 0; i < MD5_DIGEST_LENGTH; i++) { + snprintf(to, 3, "%02x", (unsigned int)inst_digest[i]); + to += 2; + } + + return md5inst; +} + +#ifdef WITH_SELINUX +static int form_context(const struct polydir_s *polyptr, + char **i_context, char **origcon, + struct instance_data *idata) +{ + int rc = PAM_SUCCESS; + char *scon = NULL; + security_class_t tclass; + + /* + * Get the security context of the directory to polyinstantiate. + */ + rc = getfilecon(polyptr->dir, origcon); + if (rc < 0 || *origcon == NULL) { + pam_syslog(idata->pamh, LOG_ERR, + "Error getting poly dir context, %m"); + return PAM_SESSION_ERR; + } + + if (polyptr->method == USER) return PAM_SUCCESS; + + if (idata->flags & PAMNS_USE_CURRENT_CONTEXT) { + rc = getcon(&scon); + } else if (idata->flags & PAMNS_USE_DEFAULT_CONTEXT) { + char *seuser = NULL, *level = NULL; + + if ((rc=getseuserbyname(idata->user, &seuser, &level)) == 0) { + rc = get_default_context_with_level(seuser, level, NULL, &scon); + free(seuser); + free(level); + } + } else { + rc = getexeccon(&scon); + } + if (rc < 0 || scon == NULL) { + pam_syslog(idata->pamh, LOG_ERR, + "Error getting exec context, %m"); + return PAM_SESSION_ERR; + } + + /* + * If polyinstantiating based on security context, get current + * process security context, get security class for directories, + * and ask the policy to provide security context of the + * polyinstantiated instance directory. + */ + + if (polyptr->method == CONTEXT) { + tclass = string_to_security_class("dir"); + if (tclass == 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error getting dir security class"); + freecon(scon); + return PAM_SESSION_ERR; + } + + if (security_compute_member(scon, *origcon, tclass, + i_context) < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error computing poly dir member context"); + freecon(scon); + return PAM_SESSION_ERR; + } else if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "member context returned by policy %s", *i_context); + freecon(scon); + return PAM_SUCCESS; + } + + /* + * If polyinstantiating based on security level, get current + * process security context, get security class for directories, + * and change the directories MLS Level to match process. + */ + + if (polyptr->method == LEVEL) { + context_t scontext = NULL; + context_t fcontext = NULL; + rc = PAM_SESSION_ERR; + + scontext = context_new(scon); + if (! scontext) { + pam_syslog(idata->pamh, LOG_CRIT, "out of memory"); + goto fail; + } + fcontext = context_new(*origcon); + if (! fcontext) { + pam_syslog(idata->pamh, LOG_CRIT, "out of memory"); + goto fail; + } + if (context_range_set(fcontext, context_range_get(scontext)) != 0) { + pam_syslog(idata->pamh, LOG_ERR, "Unable to set MLS Component of context"); + goto fail; + } + *i_context=strdup(context_str(fcontext)); + if (! *i_context) { + pam_syslog(idata->pamh, LOG_CRIT, "out of memory"); + goto fail; + } + + rc = PAM_SUCCESS; + fail: + context_free(scontext); + context_free(fcontext); + freecon(scon); + return rc; + } + /* Should never get here */ + freecon(scon); + return PAM_SUCCESS; +} +#endif + +/* + * poly_name returns the name of the polyinstantiated instance directory + * based on the method used for polyinstantiation (user, context or level) + * In addition, the function also returns the security contexts of the + * original directory to polyinstantiate and the polyinstantiated instance + * directory. + */ +#ifdef WITH_SELINUX +static int poly_name(const struct polydir_s *polyptr, char **i_name, + char **i_context, char **origcon, + struct instance_data *idata) +#else +static int poly_name(const struct polydir_s *polyptr, char **i_name, + struct instance_data *idata) +#endif +{ + int rc; + char *hash = NULL; + enum polymethod pm; +#ifdef WITH_SELINUX + char *rawcon = NULL; +#endif + + *i_name = NULL; +#ifdef WITH_SELINUX + *i_context = NULL; + *origcon = NULL; + if ((idata->flags & PAMNS_SELINUX_ENABLED) && + (rc=form_context(polyptr, i_context, origcon, idata)) != PAM_SUCCESS) { + return rc; + } +#endif + + rc = PAM_SESSION_ERR; + /* + * Set the name of the polyinstantiated instance dir based on the + * polyinstantiation method. + */ + + pm = polyptr->method; + if (pm == LEVEL || pm == CONTEXT) +#ifdef WITH_SELINUX + if (!(idata->flags & PAMNS_CTXT_BASED_INST)) { +#else + { + pam_syslog(idata->pamh, LOG_NOTICE, + "Context and level methods not available, using user method"); +#endif + if (polyptr->flags & POLYDIR_SHARED) { + rc = PAM_IGNORE; + goto fail; + } + pm = USER; + } + + switch (pm) { + case USER: + if (asprintf(i_name, "%s", idata->user) < 0) { + *i_name = NULL; + goto fail; + } + break; + +#ifdef WITH_SELINUX + case LEVEL: + case CONTEXT: + if (selinux_trans_to_raw_context(*i_context, &rawcon) < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Error translating directory context"); + goto fail; + } + if (polyptr->flags & POLYDIR_SHARED) { + if (asprintf(i_name, "%s", rawcon) < 0) { + *i_name = NULL; + goto fail; + } + } else { + if (asprintf(i_name, "%s_%s", rawcon, idata->user) < 0) { + *i_name = NULL; + goto fail; + } + } + break; + +#endif /* WITH_SELINUX */ + + case TMPDIR: + case TMPFS: + if ((*i_name=strdup("")) == NULL) + goto fail; + return PAM_SUCCESS; + + default: + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_ERR, "Unknown method"); + goto fail; + } + + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "poly_name %s", *i_name); + + if ((idata->flags & PAMNS_GEN_HASH) || strlen(*i_name) > NAMESPACE_MAX_DIR_LEN) { + hash = md5hash(*i_name, idata); + if (hash == NULL) { + goto fail; + } + if (idata->flags & PAMNS_GEN_HASH) { + free(*i_name); + *i_name = hash; + hash = NULL; + } else { + char *newname; + if (asprintf(&newname, "%.*s_%s", NAMESPACE_MAX_DIR_LEN-1-(int)strlen(hash), + *i_name, hash) < 0) { + goto fail; + } + free(*i_name); + *i_name = newname; + } + } + rc = PAM_SUCCESS; + +fail: + free(hash); +#ifdef WITH_SELINUX + freecon(rawcon); +#endif + if (rc != PAM_SUCCESS) { +#ifdef WITH_SELINUX + freecon(*i_context); + *i_context = NULL; + freecon(*origcon); + *origcon = NULL; +#endif + free(*i_name); + *i_name = NULL; + } + return rc; +} + +static int protect_mount(int dfd, const char *path, struct instance_data *idata) +{ + struct protect_dir_s *dir = idata->protect_dirs; + char tmpbuf[64]; + + while (dir != NULL) { + if (strcmp(path, dir->dir) == 0) { + return 0; + } + dir = dir->next; + } + + dir = calloc(1, sizeof(*dir)); + + if (dir == NULL) { + return -1; + } + + dir->dir = strdup(path); + + if (dir->dir == NULL) { + free(dir); + return -1; + } + + snprintf(tmpbuf, sizeof(tmpbuf), "/proc/self/fd/%d", dfd); + + if (idata->flags & PAMNS_DEBUG) { + pam_syslog(idata->pamh, LOG_INFO, + "Protect mount of %s over itself", path); + } + + if (mount(tmpbuf, tmpbuf, NULL, MS_BIND, NULL) != 0) { + int save_errno = errno; + pam_syslog(idata->pamh, LOG_ERR, + "Protect mount of %s failed: %m", tmpbuf); + free(dir->dir); + free(dir); + errno = save_errno; + return -1; + } + + dir->next = idata->protect_dirs; + idata->protect_dirs = dir; + + return 0; +} + +static int protect_dir(const char *path, mode_t mode, int do_mkdir, + struct instance_data *idata) +{ + char *p = strdup(path); + char *d; + char *dir = p; + int dfd = AT_FDCWD; + int dfd_next; + int save_errno; + int flags = O_RDONLY | O_DIRECTORY; + int rv = -1; + struct stat st; + + if (p == NULL) { + goto error; + } + + if (*dir == '/') { + dfd = open("/", flags); + if (dfd == -1) { + goto error; + } + dir++; /* assume / is safe */ + } + + while ((d=strchr(dir, '/')) != NULL) { + *d = '\0'; + dfd_next = openat(dfd, dir, flags); + if (dfd_next == -1) { + goto error; + } + + if (dfd != AT_FDCWD) + close(dfd); + dfd = dfd_next; + + if (fstat(dfd, &st) != 0) { + goto error; + } + + if (flags & O_NOFOLLOW) { + /* we are inside user-owned dir - protect */ + if (protect_mount(dfd, p, idata) == -1) + goto error; + } else if (st.st_uid != 0 || st.st_gid != 0 || + (st.st_mode & S_IWOTH)) { + /* do not follow symlinks on subdirectories */ + flags |= O_NOFOLLOW; + } + + *d = '/'; + dir = d + 1; + } + + rv = openat(dfd, dir, flags); + + if (rv == -1) { + if (!do_mkdir || mkdirat(dfd, dir, mode) != 0) { + goto error; + } + rv = openat(dfd, dir, flags); + } + + if (flags & O_NOFOLLOW) { + /* we are inside user-owned dir - protect */ + if (protect_mount(rv, p, idata) == -1) { + save_errno = errno; + close(rv); + rv = -1; + errno = save_errno; + } + } + +error: + save_errno = errno; + free(p); + if (dfd != AT_FDCWD && dfd >= 0) + close(dfd); + errno = save_errno; + + return rv; +} + +static int check_inst_parent(char *ipath, struct instance_data *idata) +{ + struct stat instpbuf; + char *inst_parent, *trailing_slash; + int dfd; + /* + * stat the instance parent path to make sure it exists + * and is a directory. Check that its mode is 000 (unless the + * admin explicitly instructs to ignore the instance parent + * mode by the "ignore_instance_parent_mode" argument). + */ + inst_parent = strdup(ipath); + if (!inst_parent) { + pam_syslog(idata->pamh, LOG_CRIT, "Error allocating pathname string"); + return PAM_SESSION_ERR; + } + + trailing_slash = strrchr(inst_parent, '/'); + if (trailing_slash) + *trailing_slash = '\0'; + + dfd = protect_dir(inst_parent, 0, 1, idata); + + if (dfd == -1 || fstat(dfd, &instpbuf) < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error creating or accessing instance parent %s, %m", inst_parent); + if (dfd != -1) + close(dfd); + free(inst_parent); + return PAM_SESSION_ERR; + } + + if ((idata->flags & PAMNS_IGN_INST_PARENT_MODE) == 0) { + if ((instpbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) || instpbuf.st_uid != 0) { + pam_syslog(idata->pamh, LOG_ERR, "Mode of inst parent %s not 000 or owner not root", + inst_parent); + close(dfd); + free(inst_parent); + return PAM_SESSION_ERR; + } + } + close(dfd); + free(inst_parent); + return PAM_SUCCESS; +} + +/* +* Check to see if there is a namespace initialization script in +* the /etc/security directory. If such a script exists +* execute it and pass directory to polyinstantiate and instance +* directory as arguments. +*/ +static int inst_init(const struct polydir_s *polyptr, const char *ipath, + struct instance_data *idata, int newdir) +{ + pid_t rc, pid; + int status; + const char *init_script = NAMESPACE_INIT_SCRIPT; + +#ifdef VENDOR_NAMESPACE_INIT_SCRIPT + /* Check whether NAMESPACE_INIT_SCRIPT file is available. + * If it does not exist, fall back to VENDOR_NAMESPACE_INIT_SCRIPT file. */ + struct stat buffer; + if (stat(init_script, &buffer) != 0 && errno == ENOENT) { + init_script = VENDOR_NAMESPACE_INIT_SCRIPT; + } +#endif + + if ((polyptr->flags & POLYDIR_ISCRIPT) && polyptr->init_script) + init_script = polyptr->init_script; + + if (access(init_script, F_OK) == 0) { + if (access(init_script, X_OK) < 0) { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_ERR, + "Namespace init script not executable"); + return PAM_SESSION_ERR; + } else { + struct sigaction newsa, oldsa; + + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) { + pam_syslog(idata->pamh, LOG_ERR, "failed to reset SIGCHLD handler"); + return PAM_SESSION_ERR; + } + + pid = fork(); + if (pid == 0) { + static char *envp[] = { NULL }; +#ifdef WITH_SELINUX + if (idata->flags & PAMNS_SELINUX_ENABLED) { + if (setexeccon(NULL) < 0) + _exit(1); + } +#endif + /* Pass maximum privs when we exec() */ + if (setuid(geteuid()) < 0) { + /* ignore failures, they don't matter */ + } + + close_fds_pre_exec(idata); + + if (execle(init_script, init_script, + polyptr->dir, ipath, newdir?"1":"0", idata->user, NULL, envp) < 0) + _exit(1); + } else if (pid > 0) { + while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) && + (errno == EINTR)); + if (rc == (pid_t)-1) { + pam_syslog(idata->pamh, LOG_ERR, "waitpid failed- %m"); + rc = PAM_SESSION_ERR; + goto out; + } + if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error initializing instance"); + rc = PAM_SESSION_ERR; + goto out; + } + } else if (pid < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Cannot fork to run namespace init script, %m"); + rc = PAM_SESSION_ERR; + goto out; + } + rc = PAM_SUCCESS; +out: + (void) sigaction(SIGCHLD, &oldsa, NULL); + return rc; + } + } + return PAM_SUCCESS; +} + +static int create_polydir(struct polydir_s *polyptr, + struct instance_data *idata) +{ + mode_t mode; + int rc; +#ifdef WITH_SELINUX + char *dircon_raw, *oldcon_raw = NULL; + struct selabel_handle *label_handle; +#endif + const char *dir = polyptr->dir; + uid_t uid; + gid_t gid; + + if (polyptr->mode != (mode_t)ULONG_MAX) + mode = polyptr->mode; + else + mode = 0777; + +#ifdef WITH_SELINUX + if (idata->flags & PAMNS_SELINUX_ENABLED) { + if (getfscreatecon_raw(&oldcon_raw) != 0) + pam_syslog(idata->pamh, LOG_NOTICE, + "Error retrieving fs create context: %m"); + + label_handle = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (!label_handle) { + pam_syslog(idata->pamh, LOG_NOTICE, + "Unable to initialize SELinux labeling handle: %m"); + } else { + rc = selabel_lookup_raw(label_handle, &dircon_raw, dir, S_IFDIR); + if (rc) { + pam_syslog(idata->pamh, LOG_NOTICE, + "Unable to get default context for directory %s, check your policy: %m", dir); + } else { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Polydir %s context: %s", dir, dircon_raw); + if (setfscreatecon_raw(dircon_raw) != 0) + pam_syslog(idata->pamh, LOG_NOTICE, + "Error setting context for directory %s: %m", dir); + freecon(dircon_raw); + } + selabel_close(label_handle); + } + } +#endif + + rc = protect_dir(dir, mode, 1, idata); + if (rc == -1) { + pam_syslog(idata->pamh, LOG_ERR, + "Error creating directory %s: %m", dir); +#ifdef WITH_SELINUX + freecon(oldcon_raw); +#endif + return PAM_SESSION_ERR; + } + +#ifdef WITH_SELINUX + if (idata->flags & PAMNS_SELINUX_ENABLED) { + if (setfscreatecon_raw(oldcon_raw) != 0) + pam_syslog(idata->pamh, LOG_NOTICE, + "Error resetting fs create context: %m"); + freecon(oldcon_raw); + } +#endif + + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "Created polydir %s", dir); + + if (polyptr->mode != (mode_t)ULONG_MAX) { + /* explicit mode requested */ + if (fchmod(rc, mode) != 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error changing mode of directory %s: %m", dir); + close(rc); + umount(dir); /* undo the eventual protection bind mount */ + rmdir(dir); + return PAM_SESSION_ERR; + } + } + + if (polyptr->owner != (uid_t)ULONG_MAX) + uid = polyptr->owner; + else + uid = idata->uid; + + if (polyptr->group != (gid_t)ULONG_MAX) + gid = polyptr->group; + else + gid = idata->gid; + + if (fchown(rc, uid, gid) != 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Unable to change owner on directory %s: %m", dir); + close(rc); + umount(dir); /* undo the eventual protection bind mount */ + rmdir(dir); + return PAM_SESSION_ERR; + } + + close(rc); + + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Polydir owner %u group %u", uid, gid); + + return PAM_SUCCESS; +} + +/* + * Create polyinstantiated instance directory (ipath). + */ +#ifdef WITH_SELINUX +static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *statbuf, + const char *icontext, const char *ocontext, + struct instance_data *idata) +#else +static int create_instance(struct polydir_s *polyptr, char *ipath, struct stat *statbuf, + struct instance_data *idata) +#endif +{ + struct stat newstatbuf; + int fd; + + /* + * Check to make sure instance parent is valid. + */ + if (check_inst_parent(ipath, idata)) + return PAM_SESSION_ERR; + + /* + * Create instance directory and set its security context to the context + * returned by the security policy. Set its mode and ownership + * attributes to match that of the original directory that is being + * polyinstantiated. + */ + + if (polyptr->method == TMPDIR) { + if (mkdtemp(polyptr->instance_prefix) == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "Error creating temporary instance %s, %m", + polyptr->instance_prefix); + polyptr->method = NONE; /* do not clean up! */ + return PAM_SESSION_ERR; + } + /* copy the actual directory name to ipath */ + strcpy(ipath, polyptr->instance_prefix); + } else if (mkdir(ipath, S_IRUSR) < 0) { + if (errno == EEXIST) + return PAM_IGNORE; + else { + pam_syslog(idata->pamh, LOG_ERR, "Error creating %s, %m", + ipath); + return PAM_SESSION_ERR; + } + } + + /* Open a descriptor to it to prevent races */ + fd = open(ipath, O_DIRECTORY | O_RDONLY); + if (fd < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Error opening %s, %m", ipath); + rmdir(ipath); + return PAM_SESSION_ERR; + } +#ifdef WITH_SELINUX + /* If SE Linux is disabled, no need to label it */ + if (idata->flags & PAMNS_SELINUX_ENABLED) { + /* If method is USER, icontext is NULL */ + if (icontext) { + if (fsetfilecon(fd, icontext) < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error setting context of %s to %s", ipath, icontext); + close(fd); + rmdir(ipath); + return PAM_SESSION_ERR; + } + } else { + if (fsetfilecon(fd, ocontext) < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error setting context of %s to %s", ipath, ocontext); + close(fd); + rmdir(ipath); + return PAM_SESSION_ERR; + } + } + } +#endif + if (fstat(fd, &newstatbuf) < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Error stating %s, %m", + ipath); + close(fd); + rmdir(ipath); + return PAM_SESSION_ERR; + } + if (newstatbuf.st_uid != statbuf->st_uid || + newstatbuf.st_gid != statbuf->st_gid) { + if (fchown(fd, statbuf->st_uid, statbuf->st_gid) < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error changing owner for %s, %m", + ipath); + close(fd); + rmdir(ipath); + return PAM_SESSION_ERR; + } + } + if (fchmod(fd, statbuf->st_mode & 07777) < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Error changing mode for %s, %m", + ipath); + close(fd); + rmdir(ipath); + return PAM_SESSION_ERR; + } + close(fd); + return PAM_SUCCESS; +} + + +/* + * This function performs the namespace setup for a particular directory + * that is being polyinstantiated. It calls poly_name to create name of instance + * directory, calls create_instance to mkdir it with appropriate + * security attributes, and performs bind mount to setup the process + * namespace. + */ +static int ns_setup(struct polydir_s *polyptr, + struct instance_data *idata) +{ + int retval; + int newdir = 1; + char *inst_dir = NULL; + char *instname = NULL; + struct stat statbuf; +#ifdef WITH_SELINUX + char *instcontext = NULL, *origcontext = NULL; +#endif + + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Set namespace for directory %s", polyptr->dir); + + retval = protect_dir(polyptr->dir, 0, 0, idata); + + if (retval < 0 && errno != ENOENT) { + pam_syslog(idata->pamh, LOG_ERR, "Polydir %s access error: %m", + polyptr->dir); + return PAM_SESSION_ERR; + } + + if (retval < 0) { + if ((polyptr->flags & POLYDIR_CREATE) && + create_polydir(polyptr, idata) != PAM_SUCCESS) + return PAM_SESSION_ERR; + } else { + close(retval); + } + + if (polyptr->method == TMPFS) { + if (mount("tmpfs", polyptr->dir, "tmpfs", polyptr->mount_flags, polyptr->mount_opts) < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Error mounting tmpfs on %s, %m", + polyptr->dir); + return PAM_SESSION_ERR; + } + + if (polyptr->flags & POLYDIR_NOINIT) + return PAM_SUCCESS; + + return inst_init(polyptr, "tmpfs", idata, 1); + } + + if (stat(polyptr->dir, &statbuf) < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Error stating %s: %m", + polyptr->dir); + return PAM_SESSION_ERR; + } + + /* + * Obtain the name of instance pathname based on the + * polyinstantiation method and instance context returned by + * security policy. + */ +#ifdef WITH_SELINUX + retval = poly_name(polyptr, &instname, &instcontext, + &origcontext, idata); +#else + retval = poly_name(polyptr, &instname, idata); +#endif + + if (retval != PAM_SUCCESS) { + if (retval != PAM_IGNORE) + pam_syslog(idata->pamh, LOG_ERR, "Error getting instance name"); + goto cleanup; + } else { +#ifdef WITH_SELINUX + if ((idata->flags & PAMNS_DEBUG) && + (idata->flags & PAMNS_SELINUX_ENABLED)) + pam_syslog(idata->pamh, LOG_DEBUG, "Inst ctxt %s Orig ctxt %s", + instcontext, origcontext); +#endif + } + + if (asprintf(&inst_dir, "%s%s", polyptr->instance_prefix, instname) < 0) + goto error_out; + + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "instance_dir %s", + inst_dir); + + /* + * Create instance directory with appropriate security + * contexts, owner, group and mode bits. + */ +#ifdef WITH_SELINUX + retval = create_instance(polyptr, inst_dir, &statbuf, instcontext, + origcontext, idata); +#else + retval = create_instance(polyptr, inst_dir, &statbuf, idata); +#endif + + if (retval == PAM_IGNORE) { + newdir = 0; + retval = PAM_SUCCESS; + } + + if (retval != PAM_SUCCESS) { + goto error_out; + } + + /* + * Bind mount instance directory on top of the polyinstantiated + * directory to provide an instance of polyinstantiated directory + * based on polyinstantiated method. + */ + if (mount(inst_dir, polyptr->dir, NULL, MS_BIND, NULL) < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Error mounting %s on %s, %m", + inst_dir, polyptr->dir); + goto error_out; + } + + if (!(polyptr->flags & POLYDIR_NOINIT)) + retval = inst_init(polyptr, inst_dir, idata, newdir); + + goto cleanup; + + /* + * various error exit points. Free allocated memory and set return + * value to indicate a pam session error. + */ +error_out: + retval = PAM_SESSION_ERR; + +cleanup: + free(inst_dir); + free(instname); +#ifdef WITH_SELINUX + freecon(instcontext); + freecon(origcontext); +#endif + return retval; +} + + +/* + * This function checks to see if the current working directory is + * inside the directory passed in as the first argument. + */ +static int cwd_in(char *dir, struct instance_data *idata) +{ + int retval = 0; + char cwd[PATH_MAX]; + + if (getcwd(cwd, PATH_MAX) == NULL) { + pam_syslog(idata->pamh, LOG_ERR, "Can't get current dir, %m"); + return -1; + } + + if (strncmp(cwd, dir, strlen(dir)) == 0) { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "cwd is inside %s", dir); + retval = 1; + } else { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "cwd is outside %s", dir); + } + + return retval; +} + +static int cleanup_tmpdirs(struct instance_data *idata) +{ + struct polydir_s *pptr; + pid_t rc, pid; + struct sigaction newsa, oldsa; + int status; + + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) { + pam_syslog(idata->pamh, LOG_ERR, "Cannot set signal value"); + return PAM_SESSION_ERR; + } + + for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) { + if (pptr->method == TMPDIR && access(pptr->instance_prefix, F_OK) == 0) { + pid = fork(); + if (pid == 0) { + static char *envp[] = { NULL }; +#ifdef WITH_SELINUX + if (idata->flags & PAMNS_SELINUX_ENABLED) { + if (setexeccon(NULL) < 0) + _exit(1); + } +#endif + close_fds_pre_exec(idata); + if (execle("/bin/rm", "/bin/rm", "-rf", pptr->instance_prefix, NULL, envp) < 0) + _exit(1); + } else if (pid > 0) { + while (((rc = waitpid(pid, &status, 0)) == (pid_t)-1) && + (errno == EINTR)); + if (rc == (pid_t)-1) { + pam_syslog(idata->pamh, LOG_ERR, "waitpid failed: %m"); + rc = PAM_SESSION_ERR; + goto out; + } + if (!WIFEXITED(status) || WIFSIGNALED(status) > 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Error removing %s", pptr->instance_prefix); + } + } else if (pid < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Cannot fork to cleanup temporary directory, %m"); + rc = PAM_SESSION_ERR; + goto out; + } + } + } + + rc = PAM_SUCCESS; +out: + sigaction(SIGCHLD, &oldsa, NULL); + return rc; +} + +/* + * This function checks to see if polyinstantiation is needed for any + * of the directories listed in the configuration file. If needed, + * cycles through all polyinstantiated directory entries and calls + * ns_setup to setup polyinstantiation for each one of them. + */ +static int setup_namespace(struct instance_data *idata, enum unmnt_op unmnt) +{ + int retval = 0, need_poly = 0, changing_dir = 0; + char *cptr, *fptr, poly_parent[PATH_MAX]; + struct polydir_s *pptr; + + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "Set up namespace for pid %d", + getpid()); + + /* + * Cycle through all polyinstantiated directory entries to see if + * polyinstantiation is needed at all. + */ + for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) { + if (ns_override(pptr, idata, idata->uid)) { + if (unmnt == NO_UNMNT || ns_override(pptr, idata, idata->ruid)) { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Overriding poly for user %d for dir %s", + idata->uid, pptr->dir); + } else { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Need unmount ns for user %d for dir %s", + idata->ruid, pptr->dir); + need_poly = 1; + break; + } + continue; + } else { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Need poly ns for user %d for dir %s", + idata->uid, pptr->dir); + need_poly = 1; + break; + } + } + + /* + * If polyinstantiation is needed, call the unshare system call to + * disassociate from the parent namespace. + */ + if (need_poly) { + if (unshare(CLONE_NEWNS) < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Unable to unshare from parent namespace, %m"); + return PAM_SESSION_ERR; + } + if (idata->flags & PAMNS_MOUNT_PRIVATE) { + /* + * Remount / as SLAVE so that nothing mounted in the namespace + * shows up in the parent + */ + if (mount("/", "/", NULL, MS_SLAVE | MS_REC , NULL) < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Failed to mark / as a slave mount point, %m"); + return PAM_SESSION_ERR; + } + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "The / mount point was marked as slave"); + } + } else { + del_polydir_list(idata->polydirs_ptr); + return PAM_SUCCESS; + } + + /* + * Again cycle through all polyinstantiated directories, this time, + * call ns_setup to setup polyinstantiation for a particular entry. + */ + for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) { + enum unmnt_op dir_unmnt = unmnt; + + if (ns_override(pptr, idata, idata->ruid)) { + dir_unmnt = NO_UNMNT; + } + if (ns_override(pptr, idata, idata->uid)) { + if (dir_unmnt == NO_UNMNT) { + continue; + } else { + dir_unmnt = UNMNT_ONLY; + } + } + + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Setting poly ns for user %d for dir %s", + idata->uid, pptr->dir); + + if ((dir_unmnt == UNMNT_REMNT) || (dir_unmnt == UNMNT_ONLY)) { + /* + * Check to see if process current directory is in the + * bind mounted instance_parent directory that we are trying to + * umount + */ + if ((changing_dir = cwd_in(pptr->rdir, idata)) < 0) { + retval = PAM_SESSION_ERR; + goto out; + } else if (changing_dir) { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "changing cwd"); + + /* + * Change current working directory to the parent of + * the mount point, that is parent of the orig + * directory where original contents of the polydir + * are available from + */ + strcpy(poly_parent, pptr->rdir); + fptr = strchr(poly_parent, '/'); + cptr = strrchr(poly_parent, '/'); + if (fptr && cptr && (fptr == cptr)) + strcpy(poly_parent, "/"); + else if (cptr) + *cptr = '\0'; + if (chdir(poly_parent) < 0) { + pam_syslog(idata->pamh, LOG_ERR, + "Can't chdir to %s, %m", poly_parent); + } + } + + if (umount(pptr->rdir) < 0) { + int saved_errno = errno; + pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m", + pptr->rdir); + if (saved_errno != EINVAL) { + retval = PAM_SESSION_ERR; + goto out; + } + } else if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "Umount succeeded %s", + pptr->rdir); + } + + if (dir_unmnt != UNMNT_ONLY) { + retval = ns_setup(pptr, idata); + if (retval == PAM_IGNORE) + retval = PAM_SUCCESS; + if (retval != PAM_SUCCESS) + break; + } + } +out: + if (retval != PAM_SUCCESS) { + cleanup_tmpdirs(idata); + unprotect_dirs(idata->protect_dirs); + } else if (pam_set_data(idata->pamh, NAMESPACE_PROTECT_DATA, idata->protect_dirs, + cleanup_protect_data) != PAM_SUCCESS) { + pam_syslog(idata->pamh, LOG_ERR, "Unable to set namespace protect data"); + cleanup_tmpdirs(idata); + unprotect_dirs(idata->protect_dirs); + return PAM_SYSTEM_ERR; + } else if (pam_set_data(idata->pamh, NAMESPACE_POLYDIR_DATA, idata->polydirs_ptr, + cleanup_polydir_data) != PAM_SUCCESS) { + pam_syslog(idata->pamh, LOG_ERR, "Unable to set namespace polydir data"); + cleanup_tmpdirs(idata); + pam_set_data(idata->pamh, NAMESPACE_PROTECT_DATA, NULL, NULL); + idata->protect_dirs = NULL; + return PAM_SYSTEM_ERR; + } + return retval; +} + + +/* + * Orig namespace. This function is called from when closing a pam + * session. If authorized, it unmounts instance directory. + */ +static int orig_namespace(struct instance_data *idata) +{ + struct polydir_s *pptr; + + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "orig namespace for pid %d", + getpid()); + + /* + * Cycle through all polyinstantiated directories from the namespace + * configuration file to see if polyinstantiation was performed for + * this user for each of the entry. If it was, try and unmount + * appropriate polyinstantiated instance directories. + */ + for (pptr = idata->polydirs_ptr; pptr; pptr = pptr->next) { + if (ns_override(pptr, idata, idata->uid)) + continue; + else { + if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, + "Unmounting instance dir for user %d & dir %s", + idata->uid, pptr->dir); + + if (umount(pptr->dir) < 0) { + pam_syslog(idata->pamh, LOG_ERR, "Unmount of %s failed, %m", + pptr->dir); + return PAM_SESSION_ERR; + } else if (idata->flags & PAMNS_DEBUG) + pam_syslog(idata->pamh, LOG_DEBUG, "Unmount of %s succeeded", + pptr->dir); + } + } + + cleanup_tmpdirs(idata); + return 0; +} + + +#ifdef WITH_SELINUX +/* + * This function checks if the calling program has requested context + * change by calling setexeccon(). If context change is not requested + * then it does not make sense to polyinstantiate based on context. + * The return value from this function is used when selecting the + * polyinstantiation method. If context change is not requested then + * the polyinstantiation method is set to USER, even if the configuration + * file lists the method as "context" or "level". + */ +static int ctxt_based_inst_needed(void) +{ + char *scon = NULL; + int rc = 0; + + rc = getexeccon(&scon); + if (rc < 0 || scon == NULL) + return 0; + else { + freecon(scon); + return 1; + } +} +#endif + +static int root_shared(void) +{ + FILE *f; + char *line = NULL; + size_t n = 0; + int rv = 0; + + f = fopen("/proc/self/mountinfo", "r"); + + if (f == NULL) + return 0; + + while(getline(&line, &n, f) != -1) { + char *l; + char *sptr; + int i; + + l = line; + sptr = NULL; + for (i = 0; i < 7; i++) { + char *tok; + + tok = strtok_r(l, " ", &sptr); + l = NULL; + if (tok == NULL) + /* next mountinfo line */ + break; + + if (i == 4 && strcmp(tok, "/") != 0) + /* next mountinfo line */ + break; + + if (i == 6) { + if (pam_str_skip_prefix(tok, "shared:") != NULL) + /* there might be more / mounts, the last one counts */ + rv = 1; + else + rv = 0; + } + } + } + + free(line); + fclose(f); + + return rv; +} + +static int get_user_data(struct instance_data *idata) +{ + int retval; + char *user_name; + struct passwd *pwd; + /* + * Lookup user and fill struct items + */ + retval = pam_get_item(idata->pamh, PAM_USER, (void*) &user_name ); + if ( user_name == NULL || retval != PAM_SUCCESS ) { + pam_syslog(idata->pamh, LOG_ERR, "Error recovering pam user name"); + return PAM_SESSION_ERR; + } + + pwd = pam_modutil_getpwnam(idata->pamh, user_name); + if (!pwd) { + pam_syslog(idata->pamh, LOG_ERR, "user unknown '%s'", user_name); + return PAM_USER_UNKNOWN; + } + + /* + * Add the user info to the instance data so we can refer to them later. + */ + idata->user[0] = 0; + strncat(idata->user, user_name, sizeof(idata->user) - 1); + idata->uid = pwd->pw_uid; + idata->gid = pwd->pw_gid; + + /* Fill in RUSER too */ + retval = pam_get_item(idata->pamh, PAM_RUSER, (void*) &user_name ); + if ( user_name != NULL && retval == PAM_SUCCESS && user_name[0] != '\0' ) { + strncat(idata->ruser, user_name, sizeof(idata->ruser) - 1); + pwd = pam_modutil_getpwnam(idata->pamh, user_name); + } else { + pwd = pam_modutil_getpwuid(idata->pamh, getuid()); + } + if (!pwd) { + pam_syslog(idata->pamh, LOG_ERR, "user unknown '%s'", user_name); + return PAM_USER_UNKNOWN; + } + user_name = pwd->pw_name; + + idata->ruser[0] = 0; + strncat(idata->ruser, user_name, sizeof(idata->ruser) - 1); + idata->ruid = pwd->pw_uid; + + return PAM_SUCCESS; +} + +/* + * Entry point from pam_open_session call. + */ +int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int i, retval; + struct instance_data idata; + enum unmnt_op unmnt = NO_UNMNT; + + /* init instance data */ + idata.flags = 0; + idata.polydirs_ptr = NULL; + idata.protect_dirs = NULL; + idata.pamh = pamh; +#ifdef WITH_SELINUX + if (is_selinux_enabled()) + idata.flags |= PAMNS_SELINUX_ENABLED; + if (ctxt_based_inst_needed()) + idata.flags |= PAMNS_CTXT_BASED_INST; +#endif + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) + idata.flags |= PAMNS_DEBUG; + if (strcmp(argv[i], "gen_hash") == 0) + idata.flags |= PAMNS_GEN_HASH; + if (strcmp(argv[i], "ignore_config_error") == 0) + idata.flags |= PAMNS_IGN_CONFIG_ERR; + if (strcmp(argv[i], "ignore_instance_parent_mode") == 0) + idata.flags |= PAMNS_IGN_INST_PARENT_MODE; + if (strcmp(argv[i], "use_current_context") == 0) { + idata.flags |= PAMNS_USE_CURRENT_CONTEXT; + idata.flags |= PAMNS_CTXT_BASED_INST; + } + if (strcmp(argv[i], "use_default_context") == 0) { + idata.flags |= PAMNS_USE_DEFAULT_CONTEXT; + idata.flags |= PAMNS_CTXT_BASED_INST; + } + if (strcmp(argv[i], "mount_private") == 0) { + idata.flags |= PAMNS_MOUNT_PRIVATE; + } + if (strcmp(argv[i], "unmnt_remnt") == 0) + unmnt = UNMNT_REMNT; + if (strcmp(argv[i], "unmnt_only") == 0) + unmnt = UNMNT_ONLY; + if (strcmp(argv[i], "require_selinux") == 0) { + if (!(idata.flags & PAMNS_SELINUX_ENABLED)) { + pam_syslog(idata.pamh, LOG_ERR, + "selinux_required option given and selinux is disabled"); + return PAM_SESSION_ERR; + } + } + } + if (idata.flags & PAMNS_DEBUG) + pam_syslog(idata.pamh, LOG_DEBUG, "open_session - start"); + + retval = get_user_data(&idata); + if (retval != PAM_SUCCESS) + return retval; + + if (root_shared()) { + idata.flags |= PAMNS_MOUNT_PRIVATE; + } + + /* + * Parse namespace configuration file which lists directories to + * polyinstantiate, directory where instance directories are to + * be created and the method used for polyinstantiation. + */ + retval = parse_config_file(&idata); + if (retval != PAM_SUCCESS) { + del_polydir_list(idata.polydirs_ptr); + return PAM_SESSION_ERR; + } + + if (idata.polydirs_ptr) { + retval = setup_namespace(&idata, unmnt); + if (idata.flags & PAMNS_DEBUG) { + if (retval) + pam_syslog(idata.pamh, LOG_DEBUG, + "namespace setup failed for pid %d", getpid()); + else + pam_syslog(idata.pamh, LOG_DEBUG, + "namespace setup ok for pid %d", getpid()); + } + } else if (idata.flags & PAMNS_DEBUG) + pam_syslog(idata.pamh, LOG_DEBUG, "Nothing to polyinstantiate"); + + if (retval != PAM_SUCCESS) + del_polydir_list(idata.polydirs_ptr); + return retval; +} + + +/* + * Entry point from pam_close_session call. + */ +int pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int i, retval; + struct instance_data idata; + const void *polyptr; + + /* init instance data */ + idata.flags = 0; + idata.polydirs_ptr = NULL; + idata.pamh = pamh; +#ifdef WITH_SELINUX + if (is_selinux_enabled()) + idata.flags |= PAMNS_SELINUX_ENABLED; + if (ctxt_based_inst_needed()) + idata.flags |= PAMNS_CTXT_BASED_INST; +#endif + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) + idata.flags |= PAMNS_DEBUG; + if (strcmp(argv[i], "ignore_config_error") == 0) + idata.flags |= PAMNS_IGN_CONFIG_ERR; + if (strcmp(argv[i], "unmount_on_close") == 0) + idata.flags |= PAMNS_UNMOUNT_ON_CLOSE; + } + + if (idata.flags & PAMNS_DEBUG) + pam_syslog(idata.pamh, LOG_DEBUG, "close_session - start"); + + /* + * Normally the unmount is implicitly done when the last + * process in the private namespace exits. + * If it is ensured that there are no child processes left in + * the private namespace by other means and if there are + * multiple sessions opened and closed sequentially by the + * same process, the "unmount_on_close" option might be + * used to unmount the polydirs explicitly. + */ + if (!(idata.flags & PAMNS_UNMOUNT_ON_CLOSE)) { + pam_set_data(idata.pamh, NAMESPACE_POLYDIR_DATA, NULL, NULL); + pam_set_data(idata.pamh, NAMESPACE_PROTECT_DATA, NULL, NULL); + + if (idata.flags & PAMNS_DEBUG) + pam_syslog(idata.pamh, LOG_DEBUG, "close_session - successful"); + return PAM_SUCCESS; + } + + retval = get_user_data(&idata); + if (retval != PAM_SUCCESS) + return retval; + + retval = pam_get_data(idata.pamh, NAMESPACE_POLYDIR_DATA, &polyptr); + if (retval != PAM_SUCCESS || polyptr == NULL) + /* nothing to reset */ + return PAM_SUCCESS; + + DIAG_PUSH_IGNORE_CAST_QUAL; + idata.polydirs_ptr = (void *)polyptr; + DIAG_POP_IGNORE_CAST_QUAL; + + if (idata.flags & PAMNS_DEBUG) + pam_syslog(idata.pamh, LOG_DEBUG, "Resetting namespace for pid %d", + getpid()); + + retval = orig_namespace(&idata); + if (idata.flags & PAMNS_DEBUG) { + if (retval) + pam_syslog(idata.pamh, LOG_DEBUG, + "resetting namespace failed for pid %d", getpid()); + else + pam_syslog(idata.pamh, LOG_DEBUG, + "resetting namespace ok for pid %d", getpid()); + } + + pam_set_data(idata.pamh, NAMESPACE_POLYDIR_DATA, NULL, NULL); + pam_set_data(idata.pamh, NAMESPACE_PROTECT_DATA, NULL, NULL); + + return PAM_SUCCESS; +} diff --git a/modules/pam_namespace/pam_namespace.h b/modules/pam_namespace/pam_namespace.h new file mode 100644 index 0000000..1799938 --- /dev/null +++ b/modules/pam_namespace/pam_namespace.h @@ -0,0 +1,194 @@ +/****************************************************************************** + * A module for Linux-PAM that will set the default namespace after + * establishing a session via PAM. + * + * (C) Copyright IBM Corporation 2005 + * (C) Copyright Red Hat 2006 + * All Rights Reserved. + * + * Written by: Janak Desai + * With Revisions by: Steve Grubb + * Derived from a namespace setup patch by Chad Sellers + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __linux__ +#error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!! +#endif + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "security/pam_modules.h" +#include "security/pam_modutil.h" +#include "security/pam_ext.h" +#include "md5.h" + +#ifdef WITH_SELINUX +#include +#include +#include +#include +#endif + +#ifndef CLONE_NEWNS +#define CLONE_NEWNS 0x00020000 /* Flag to create new namespace */ +#endif + +/* mount flags for mount_private */ +#ifndef MS_REC +#define MS_REC (1<<14) +#endif +#ifndef MS_PRIVATE +#define MS_PRIVATE (1<<18) +#endif +#ifndef MS_SLAVE +#define MS_SLAVE (1<<19) +#endif + + +/* + * Module defines + */ +#define PAM_NAMESPACE_CONFIG (SCONFIG_DIR "/namespace.conf") +#define NAMESPACE_INIT_SCRIPT (SCONFIG_DIR "/namespace.init") +#define NAMESPACE_D_DIR (SCONFIG_DIR "/namespace.d/") +#define NAMESPACE_D_GLOB (SCONFIG_DIR "/namespace.d/*.conf") +#ifdef VENDOR_SCONFIG_DIR +#define VENDOR_NAMESPACE_INIT_SCRIPT (VENDOR_SCONFIG_DIR "/namespace.init") +#define VENDOR_PAM_NAMESPACE_CONFIG (VENDOR_SCONFIG_DIR "/namespace.conf") +#define VENDOR_NAMESPACE_D_DIR (VENDOR_SCONFIG_DIR "/namespace.d/") +#define VENDOR_NAMESPACE_D_GLOB (VENDOR_SCONFIG_DIR "/namespace.d/*.conf") +#endif + +/* module flags */ +#define PAMNS_DEBUG 0x00000100 /* Running in debug mode */ +#define PAMNS_SELINUX_ENABLED 0x00000400 /* SELinux is enabled */ +#define PAMNS_CTXT_BASED_INST 0x00000800 /* Context based instance needed */ +#define PAMNS_GEN_HASH 0x00002000 /* Generate md5 hash for inst names */ +#define PAMNS_IGN_CONFIG_ERR 0x00004000 /* Ignore format error in conf file */ +#define PAMNS_IGN_INST_PARENT_MODE 0x00008000 /* Ignore instance parent mode */ +#define PAMNS_UNMOUNT_ON_CLOSE 0x00010000 /* Unmount at session close */ +#define PAMNS_USE_CURRENT_CONTEXT 0x00020000 /* use getcon instead of getexeccon */ +#define PAMNS_USE_DEFAULT_CONTEXT 0x00040000 /* use get_default_context instead of getexeccon */ +#define PAMNS_MOUNT_PRIVATE 0x00080000 /* Make the polydir mounts private */ + +/* polydir flags */ +#define POLYDIR_EXCLUSIVE 0x00000001 /* polyinstantiate exclusively for override uids */ +#define POLYDIR_CREATE 0x00000002 /* create the polydir */ +#define POLYDIR_NOINIT 0x00000004 /* no init script */ +#define POLYDIR_SHARED 0x00000008 /* share context/level instances among users */ +#define POLYDIR_ISCRIPT 0x00000010 /* non default init script */ +#define POLYDIR_MNTOPTS 0x00000020 /* mount options for tmpfs mount */ + + +#define NAMESPACE_MAX_DIR_LEN 80 +#define NAMESPACE_POLYDIR_DATA "pam_namespace:polydir_data" +#define NAMESPACE_PROTECT_DATA "pam_namespace:protect_data" + +/* + * Polyinstantiation method options, based on user, security context + * or both + */ +enum polymethod { + NONE, + USER, + CONTEXT, + LEVEL, + TMPDIR, + TMPFS +}; + +/* + * Depending on the application using this namespace module, we + * may need to unmount previously bind mounted instance directory. + * Applications such as login and sshd, that establish a new + * session unmount of instance directory is not needed. For applications + * such as su and newrole, that switch the identity, this module + * has to unmount previous instance directory first and re-mount + * based on the new identity. For other trusted applications that + * just want to undo polyinstantiation, only unmount of previous + * instance directory is needed. + */ +enum unmnt_op { + NO_UNMNT, + UNMNT_REMNT, + UNMNT_ONLY, +}; + +/* + * Structure that holds information about a directory to polyinstantiate + */ +struct polydir_s { + char dir[PATH_MAX]; /* directory to polyinstantiate */ + char rdir[PATH_MAX]; /* directory to unmount (based on RUSER) */ + char instance_prefix[PATH_MAX]; /* prefix for instance dir path name */ + enum polymethod method; /* method used to polyinstantiate */ + unsigned int num_uids; /* number of override uids */ + uid_t *uid; /* list of override uids */ + unsigned int flags; /* polydir flags */ + char *init_script; /* path to init script */ + char *mount_opts; /* mount options for tmpfs mount */ + unsigned long mount_flags; /* mount flags for tmpfs mount */ + uid_t owner; /* user which should own the polydir */ + gid_t group; /* group which should own the polydir */ + mode_t mode; /* mode of the polydir */ + struct polydir_s *next; /* pointer to the next polydir entry */ +}; + +struct protect_dir_s { + char *dir; /* protected directory */ + struct protect_dir_s *next; /* next entry */ +}; + +struct instance_data { + pam_handle_t *pamh; /* The pam handle for this instance */ + struct polydir_s *polydirs_ptr; /* The linked list pointer */ + struct protect_dir_s *protect_dirs; /* The pointer to stack of mount-protected dirs */ + char user[LOGIN_NAME_MAX]; /* User name */ + char ruser[LOGIN_NAME_MAX]; /* Requesting user name */ + uid_t uid; /* The uid of the user */ + gid_t gid; /* The gid of the user's primary group */ + uid_t ruid; /* The uid of the requesting user */ + unsigned long flags; /* Flags for debug, selinux etc */ +}; diff --git a/modules/pam_namespace/pam_namespace.service.in b/modules/pam_namespace/pam_namespace.service.in new file mode 100644 index 0000000..e231191 --- /dev/null +++ b/modules/pam_namespace/pam_namespace.service.in @@ -0,0 +1,11 @@ +[Unit] +After=local-fs.target +Before=multi-user.target shutdown.target +Conflicts=shutdown.target +DefaultDependencies=no +Description=Make sure parent directories configured in @SCONFIGDIR@/namespace.conf for polyinstantiation exist +Documentation=man:pam_namespace(8) + +[Service] +ExecStart=@sbindir@/pam_namespace_helper +Type=oneshot diff --git a/modules/pam_namespace/pam_namespace_helper.8.xml b/modules/pam_namespace/pam_namespace_helper.8.xml new file mode 100644 index 0000000..002c254 --- /dev/null +++ b/modules/pam_namespace/pam_namespace_helper.8.xml @@ -0,0 +1,59 @@ + + + + pam_namespace_helper + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_namespace_helper + Helper binary that creates home directories + + + + + pam_namespace_helper + + + + + + DESCRIPTION + + + pam_namespace_helper is a helper program + for the pam_namespace module that sets up a + private namespace for a session with polyinstantiated + directories. The helper ensures that the namespace mount points + exist before they are started to be used for the + polyinstantiated directories. Mount points for home directories + (lines with $HOME) are not created. + + + + pam_namespace_helper should be run by + systemd at system startup. It should also be run by the + administrator after defining the polyinstantiated directories + but before enabling them. + + + + + SEE ALSO + + + pam_namespace8 + + + + + + AUTHOR + + Written by Topi Miettinen. + + + + \ No newline at end of file diff --git a/modules/pam_namespace/pam_namespace_helper.in b/modules/pam_namespace/pam_namespace_helper.in new file mode 100644 index 0000000..b9c361f --- /dev/null +++ b/modules/pam_namespace/pam_namespace_helper.in @@ -0,0 +1,15 @@ +#!/bin/sh + +CONF=@SCONFIGDIR@/namespace.conf + +# Match logic of process_line(), except lines with $HOME are ignored +# skip the leading white space, rip off the comments, ignore empty lines +sed -e 's/^[ ]*//g' -e 's/#.*//g' -e '/.*\$HOME.*/d' -e '/^$/d' < $CONF | \ + while read polydir instance_prefix method uids; do + if [ ! -e "$instance_prefix" ]; then + echo "mkdir $instance_prefix" + mkdir --parents --mode=0 -Z "$instance_prefix" + fi + done + +exit 0 diff --git a/modules/pam_namespace/tst-pam_namespace b/modules/pam_namespace/tst-pam_namespace new file mode 100755 index 0000000..c929dfc --- /dev/null +++ b/modules/pam_namespace/tst-pam_namespace @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_namespace.so diff --git a/modules/pam_nologin/README.xml b/modules/pam_nologin/README.xml new file mode 100644 index 0000000..5a99332 --- /dev/null +++ b/modules/pam_nologin/README.xml @@ -0,0 +1,31 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_nologin.8.xml" xpointer='xpointer(id("pam_nologin-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_nologin/meson.build b/modules/pam_nologin/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_nologin/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_nologin/pam_nologin.8.xml b/modules/pam_nologin/pam_nologin.8.xml new file mode 100644 index 0000000..1ea725c --- /dev/null +++ b/modules/pam_nologin/pam_nologin.8.xml @@ -0,0 +1,172 @@ + + + + pam_nologin + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_nologin + Prevent non-root users from login + + + + + pam_nologin.so + + file=/path/nologin + + + successok + + + + + + + DESCRIPTION + + + pam_nologin is a PAM module that prevents users from logging into + the system when /var/run/nologin or + /etc/nologin exists. The contents + of the file are displayed to the user. The pam_nologin module + has no effect on the root user's ability to log in. + + + + + + OPTIONS + + + + file=/path/nologin + + + + Use this file instead the default + /var/run/nologin or + /etc/nologin. + + + + + + successok + + + + Return PAM_SUCCESS if no file exists, the default is PAM_IGNORE. + + + + + + + + MODULE TYPES PROVIDED + + The and module + types are provided. + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + The user is not root and /etc/nologin + exists, so the user is not permitted to log in. + + + + + PAM_BUF_ERR + + Memory buffer error. + + + + PAM_IGNORE + + + This is the default return value. + + + + + PAM_SUCCESS + + + Success: either the user is root or the + nologin file does not exist. + + + + + PAM_USER_UNKNOWN + + + User not known to the underlying authentication module. + + + + + + + + EXAMPLES + + The suggested usage for /etc/pam.d/login is: + +auth required pam_nologin.so + + + + + NOTES + + In order to make this module effective, all login methods should be + secured by it. It should be used as a required + method listed before any sufficient methods in + order to get standard Unix nologin semantics. Note, the use of + module argument causes the module to + return PAM_SUCCESS and as such would break + such a configuration - failing sufficient modules + would lead to a successful login because the nologin module + succeeded. + + + + + SEE ALSO + + + nologin5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_nologin was written by Michael K. Johnson <johnsonm@redhat.com>. + + + + \ No newline at end of file diff --git a/modules/pam_nologin/pam_nologin.c b/modules/pam_nologin/pam_nologin.c new file mode 100644 index 0000000..50575e1 --- /dev/null +++ b/modules/pam_nologin/pam_nologin.c @@ -0,0 +1,184 @@ +/* + * pam_nologin module + * + * Written by Michael K. Johnson 1996/10/24 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "pam_inline.h" + +#define DEFAULT_NOLOGIN_PATH "/var/run/nologin" +#define COMPAT_NOLOGIN_PATH "/etc/nologin" + +/* + * parse some command line options + */ +struct opt_s { + int retval_when_nofile; + const char *nologin_file; +}; + +static void +parse_args(pam_handle_t *pamh, int argc, const char **argv, struct opt_s *opts) +{ + int i; + + memset(opts, 0, sizeof(*opts)); + + opts->retval_when_nofile = PAM_IGNORE; + + for (i=0; iretval_when_nofile = PAM_SUCCESS; + } else if ((str = pam_str_skip_prefix(argv[i], "file=")) != NULL) { + opts->nologin_file = str; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", argv[i]); + } + } +} + +/* + * do the meat of the work for this module + */ + +static int perform_check(pam_handle_t *pamh, struct opt_s *opts) +{ + const char *username; + int retval = opts->retval_when_nofile; + int fd = -1; + + if ((pam_get_user(pamh, &username, NULL) != PAM_SUCCESS)) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name"); + return PAM_USER_UNKNOWN; + } + + if (opts->nologin_file == NULL) { + if ((fd = open(DEFAULT_NOLOGIN_PATH, O_RDONLY, 0)) < 0) { + fd = open(COMPAT_NOLOGIN_PATH, O_RDONLY, 0); + } + } else { + fd = open(opts->nologin_file, O_RDONLY, 0); + } + + if (fd >= 0) { + + int msg_style = PAM_TEXT_INFO; + struct passwd *user_pwd; + struct stat st; + + user_pwd = pam_modutil_getpwnam(pamh, username); + if (user_pwd == NULL) { + retval = PAM_USER_UNKNOWN; + msg_style = PAM_ERROR_MSG; + } else if (user_pwd->pw_uid) { + retval = PAM_AUTH_ERR; + msg_style = PAM_ERROR_MSG; + } + + /* fill in message buffer with contents of /etc/nologin */ + if (fstat(fd, &st) < 0) { + /* give up trying to display message */ + goto clean_up_fd; + } + + /* + * on some OSes (e.g. Hurd) reading a directory succeeds, + * instead of failing with EISDIR; hence, work as if + * pam_modutil_read later on would fail + */ + if (S_ISDIR(st.st_mode)) { + retval = PAM_SYSTEM_ERR; + goto clean_up_fd; + } + + /* Don't print anything if the message is empty, will only + disturb the output with empty lines */ + if (st.st_size > 0) { + char *mtmp; + if ((uintmax_t)st.st_size > (uintmax_t)INT_MAX) { + pam_syslog(pamh, LOG_CRIT, "file too large"); + retval = PAM_SYSTEM_ERR; + goto clean_up_fd; + } + mtmp = malloc(st.st_size+1); + if (!mtmp) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + retval = PAM_BUF_ERR; + goto clean_up_fd; + } + + if (pam_modutil_read(fd, mtmp, st.st_size) == st.st_size) { + mtmp[st.st_size] = '\0'; + (void) pam_prompt (pamh, msg_style, NULL, "%s", mtmp); + } + else + retval = PAM_SYSTEM_ERR; + + free(mtmp); + } + + clean_up_fd: + + close(fd); + } + + return retval; +} + +/* --- authentication management functions --- */ + +int +pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + struct opt_s opts; + + parse_args(pamh, argc, argv, &opts); + + return perform_check(pamh, &opts); +} + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc, const char **argv) +{ + struct opt_s opts; + + parse_args(pamh, argc, argv, &opts); + + return opts.retval_when_nofile; +} + +/* --- account management function --- */ + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + struct opt_s opts; + + parse_args(pamh, argc, argv, &opts); + + return perform_check(pamh, &opts); +} + +/* end of module definition */ diff --git a/modules/pam_nologin/tst-pam_nologin b/modules/pam_nologin/tst-pam_nologin new file mode 100755 index 0000000..caa91b6 --- /dev/null +++ b/modules/pam_nologin/tst-pam_nologin @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_nologin.so diff --git a/modules/pam_nologin/tst-pam_nologin-retval.c b/modules/pam_nologin/tst-pam_nologin-retval.c new file mode 100644 index 0000000..4d44a38 --- /dev/null +++ b/modules/pam_nologin/tst-pam_nologin-retval.c @@ -0,0 +1,226 @@ +/* + * Check pam_nologin return values. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_nologin" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char missing_file[] = TEST_NAME ".missing"; +static const char empty_file[] = "/dev/null"; +static const char user_name[] = ""; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + struct passwd *pw; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + /* PAM_IGNORE -> PAM_PERM_DENIED */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so file=%s\n" + "account required %s/" LTDIR "%s.so file=%s\n" + "password required %s/" LTDIR "%s.so file=%s\n" + "session required %s/" LTDIR "%s.so file=%s\n", + cwd, MODULE_NAME, missing_file, + cwd, MODULE_NAME, missing_file, + cwd, MODULE_NAME, missing_file, + cwd, MODULE_NAME, missing_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_IGNORE -> PAM_SUCCESS */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so file=%s\n" + "auth required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "account required %s/" LTDIR "%s.so file=%s\n" + "account required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "password required %s/" LTDIR "%s.so file=%s\n" + "password required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "session required %s/" LTDIR "%s.so file=%s\n" + "session required %s/../pam_permit/" LTDIR "pam_permit.so\n", + cwd, MODULE_NAME, missing_file, cwd, + cwd, MODULE_NAME, missing_file, cwd, + cwd, MODULE_NAME, missing_file, cwd, + cwd, MODULE_NAME, missing_file, cwd)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* successok -> PAM_SUCCESS */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so successok file=%s\n" + "account required %s/" LTDIR "%s.so successok file=%s\n" + "password required %s/" LTDIR "%s.so successok file=%s\n" + "session required %s/" LTDIR "%s.so successok file=%s\n", + cwd, MODULE_NAME, missing_file, + cwd, MODULE_NAME, missing_file, + cwd, MODULE_NAME, missing_file, + cwd, MODULE_NAME, missing_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_USER_UNKNOWN */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so file=%s\n" + "account required %s/" LTDIR "%s.so file=%s\n" + "password required %s/" LTDIR "%s.so file=%s\n" + "session required %s/" LTDIR "%s.so file=%s\n", + cwd, MODULE_NAME, empty_file, + cwd, MODULE_NAME, empty_file, + cwd, MODULE_NAME, empty_file, + cwd, MODULE_NAME, empty_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_USER_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_USER_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* uid == 0 */ + if ((pw = getpwuid(0)) != NULL) { + /* successok -> PAM_SUCCESS */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so successok file=%s\n" + "account required %s/" LTDIR "%s.so successok file=%s\n" + "password required %s/" LTDIR "%s.so successok file=%s\n" + "session required %s/" LTDIR "%s.so successok file=%s\n", + cwd, MODULE_NAME, empty_file, + cwd, MODULE_NAME, empty_file, + cwd, MODULE_NAME, empty_file, + cwd, MODULE_NAME, empty_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, + &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_SYSTEM_ERR */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so file=%s\n" + "account required %s/" LTDIR "%s.so file=%s\n" + "password required %s/" LTDIR "%s.so file=%s\n" + "session required %s/" LTDIR "%s.so file=%s\n", + cwd, MODULE_NAME, ".", + cwd, MODULE_NAME, ".", + cwd, MODULE_NAME, ".", + cwd, MODULE_NAME, ".")); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, + &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SYSTEM_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SYSTEM_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + } + + /* uid != 0 */ + if (geteuid() != 0 && (pw = getpwuid(geteuid())) != NULL) { + /* PAM_AUTH_ERR */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so file=%s\n" + "account required %s/" LTDIR "%s.so file=%s\n" + "password required %s/" LTDIR "%s.so file=%s\n" + "session required %s/" LTDIR "%s.so file=%s\n", + cwd, MODULE_NAME, empty_file, + cwd, MODULE_NAME, empty_file, + cwd, MODULE_NAME, empty_file, + cwd, MODULE_NAME, empty_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, pw->pw_name, + &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_AUTH_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + } + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_permit/README.xml b/modules/pam_permit/README.xml new file mode 100644 index 0000000..c08425f --- /dev/null +++ b/modules/pam_permit/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_permit.8.xml" xpointer='xpointer(id("pam_permit-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_permit/meson.build b/modules/pam_permit/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_permit/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_permit/pam_permit.8.xml b/modules/pam_permit/pam_permit.8.xml new file mode 100644 index 0000000..0634e5e --- /dev/null +++ b/modules/pam_permit/pam_permit.8.xml @@ -0,0 +1,103 @@ + + + + pam_permit + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_permit + The promiscuous module + + + + + pam_permit.so + + + + + + DESCRIPTION + + + pam_permit is a PAM module that always permit access. It does + nothing else. + + + In the case of authentication, the user's name will be set to + nobody if the application didn't set one. + Many applications and PAM modules become confused if this name + is unknown. + + + This module is very dangerous. It should be used with extreme + caution. + + + + + + OPTIONS + This module does not recognise any options. + + + + MODULE TYPES PROVIDED + + The , , + and + module types are provided. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + This module always returns this value. + + + + + + + + EXAMPLES + + Add this line to your other login entries to disable account + management, but continue to permit users to log in. + +account required pam_permit.so + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_permit was written by Andrew G. Morgan, <morgan@kernel.org>. + + + + \ No newline at end of file diff --git a/modules/pam_permit/pam_permit.c b/modules/pam_permit/pam_permit.c new file mode 100644 index 0000000..4f97368 --- /dev/null +++ b/modules/pam_permit/pam_permit.c @@ -0,0 +1,84 @@ +/* + * pam_permit module + * + * Written by Andrew Morgan 1996/3/11 + */ + +#include "config.h" +#include + +#include +#include + +#define DEFAULT_USER "nobody" + +/* --- authentication management functions --- */ + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + int retval; + const char *user=NULL; + + /* + * authentication requires we know who the user wants to be + */ + retval = pam_get_user(pamh, &user, NULL); + if (retval != PAM_SUCCESS) { + D(("get user returned error: %s", pam_strerror(pamh,retval))); + return retval; + } + if (*user == '\0') { + D(("username not known")); + retval = pam_set_item(pamh, PAM_USER, (const void *) DEFAULT_USER); + if (retval != PAM_SUCCESS) + return PAM_USER_UNKNOWN; + } + user = NULL; /* clean up */ + + return PAM_SUCCESS; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +/* --- account management functions --- */ + +int +pam_sm_acct_mgmt(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +/* --- password management --- */ + +int +pam_sm_chauthtok(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +/* --- session management --- */ + +int +pam_sm_open_session(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_close_session(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +/* end of module definition */ diff --git a/modules/pam_permit/tst-pam_permit b/modules/pam_permit/tst-pam_permit new file mode 100755 index 0000000..8adb427 --- /dev/null +++ b/modules/pam_permit/tst-pam_permit @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_permit.so diff --git a/modules/pam_permit/tst-pam_permit-retval.c b/modules/pam_permit/tst-pam_permit-retval.c new file mode 100644 index 0000000..aacdedb --- /dev/null +++ b/modules/pam_permit/tst-pam_permit-retval.c @@ -0,0 +1,87 @@ +/* + * Check pam_permit return values. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_permit" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char user_name[] = ""; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* Perform a test dedicated to configuration file parsing. */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "# ignore escaped newlines in comments \\\n" + "auth required \\\n" + " %s/" LTDIR "%s.so\n" + "# allow unneeded whitespaces\n" + " account required %s/" LTDIR "%s.so%c\\\n" + "line after NUL byte continues up to here\n" + "password required %s/" LTDIR "%s.so # eol comment\n" + "session required %s/" LTDIR "%s.so", + cwd, MODULE_NAME, + cwd, MODULE_NAME, '\0', + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_pwhistory/README.xml b/modules/pam_pwhistory/README.xml new file mode 100644 index 0000000..194edbc --- /dev/null +++ b/modules/pam_pwhistory/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_pwhistory.8.xml" xpointer='xpointer(id("pam_pwhistory-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_pwhistory/meson.build b/modules/pam_pwhistory/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_pwhistory/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_pwhistory/opasswd.c b/modules/pam_pwhistory/opasswd.c new file mode 100644 index 0000000..512688d --- /dev/null +++ b/modules/pam_pwhistory/opasswd.c @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2008 Thorsten Kukuk + * Copyright (c) 2013 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HELPER_COMPILE +#include +#endif +#include + +#ifdef HAVE_CRYPT_H +#include +#endif + +#ifdef HELPER_COMPILE +#define pam_modutil_getpwnam(h,n) getpwnam(n) +#define pam_modutil_getspnam(h,n) getspnam(n) +#define pam_syslog(h,a,...) helper_log_err(a,__VA_ARGS__) +#else +#include +#include +#endif +#include +#include "pam_inline.h" + +#include "opasswd.h" + +#ifndef RANDOM_DEVICE +#define RANDOM_DEVICE "/dev/urandom" +#endif + +#define DEFAULT_OLD_PASSWORDS_FILE SCONFIG_DIR "/opasswd" + +typedef struct { + char *user; + char *uid; + int count; + char *old_passwords; +} opwd; + +#ifdef HELPER_COMPILE +void +helper_log_err(int err, const char *format, ...) +{ + va_list args; + + va_start(args, format); + openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV); + vsyslog(err, format, args); + va_end(args); + closelog(); +} +#endif + +static int +parse_entry (char *line, opwd *data) +{ + const char delimiters[] = ":"; + char *endptr; + char *count; + + data->user = strsep (&line, delimiters); + data->uid = strsep (&line, delimiters); + count = strsep (&line, delimiters); + if (count == NULL) + return 1; + + data->count = strtol (count, &endptr, 10); + if (endptr != NULL && *endptr != '\0') + return 1; + + data->old_passwords = strsep (&line, delimiters); + + return 0; +} + +/* Return 1 if the passwords are equal, 0 if they are not, and -1 on error. */ +static int +compare_password(const char *newpass, const char *oldpass) +{ + char *outval; + int retval; +#ifdef HAVE_CRYPT_R + struct crypt_data *cdata; + + cdata = calloc(1, sizeof(*cdata)); + if (!cdata) + return -1; + + outval = crypt_r (newpass, oldpass, cdata); +#else + outval = crypt (newpass, oldpass); +#endif + + retval = outval != NULL && strcmp(outval, oldpass) == 0; + +#ifdef HAVE_CRYPT_R + pam_overwrite_object(cdata); + free(cdata); +#else + pam_overwrite_string(outval); +#endif + return retval; +} + +/* Check, if the new password is already in the opasswd file. */ +PAMH_ARG_DECL(int +check_old_pass, const char *user, const char *newpass, const char *filename, int debug) +{ + int retval = PAM_SUCCESS; + FILE *oldpf; + char *buf = NULL; + size_t buflen = 0; + opwd entry; + int found = 0; + +#ifndef HELPER_COMPILE + if (SELINUX_ENABLED) + return PAM_PWHISTORY_RUN_HELPER; +#endif + + const char *opasswd_file = + (filename != NULL ? filename : DEFAULT_OLD_PASSWORDS_FILE); + + if ((oldpf = fopen (opasswd_file, "r")) == NULL) + { + if (errno != ENOENT) + pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", opasswd_file); + return PAM_SUCCESS; + } + + while (!feof (oldpf)) + { + ssize_t n = getline (&buf, &buflen, oldpf); + + if (n < 1) + break; + + buf[strcspn(buf, "\n")] = '\0'; + if (buf[0] == '\0') /* ignore empty lines */ + continue; + + if (strncmp (buf, user, strlen (user)) == 0 && + buf[strlen (user)] == ':') + { + /* We found the line we needed */ + if (parse_entry (buf, &entry) == 0) + { + found = 1; + break; + } + } + } + + fclose (oldpf); + + if (found && entry.old_passwords) + { + const char delimiters[] = ","; + char *running; + char *oldpass; + + running = entry.old_passwords; + + do { + oldpass = strsep (&running, delimiters); + if (oldpass && strlen (oldpass) > 0) { + int rc; + + rc = compare_password(newpass, oldpass); + if (rc) { + if (rc < 0) + pam_syslog (pamh, LOG_ERR, "Cannot allocate crypt data"); + else if (debug) + pam_syslog (pamh, LOG_DEBUG, "New password already used"); + + retval = PAM_AUTHTOK_ERR; + break; + } + } + } while (oldpass != NULL); + } + + pam_overwrite_n(buf, buflen); + free (buf); + + return retval; +} + +PAMH_ARG_DECL(int +save_old_pass, const char *user, int howmany, const char *filename, int debug UNUSED) +{ + struct stat opasswd_stat; + FILE *oldpf, *newpf; + int newpf_fd; + int do_create = 0; + int retval = PAM_SUCCESS; + char *buf = NULL; + size_t buflen = 0; + int found = 0; + struct passwd *pwd; + const char *oldpass; + + /* Define opasswd file and temp file for opasswd */ + const char *opasswd_file = + (filename != NULL ? filename : DEFAULT_OLD_PASSWORDS_FILE); + char *opasswd_tmp; + + if (asprintf (&opasswd_tmp, "%s.tmpXXXXXX", opasswd_file) < 0) + return PAM_BUF_ERR; + + pwd = pam_modutil_getpwnam (pamh, user); + if (pwd == NULL) + { + free (opasswd_tmp); + return PAM_USER_UNKNOWN; + } + + if (howmany <= 0) + { + free (opasswd_tmp); + return PAM_SUCCESS; + } + +#ifndef HELPER_COMPILE + if (SELINUX_ENABLED) + { + free (opasswd_tmp); + return PAM_PWHISTORY_RUN_HELPER; + } +#endif + + if ((strcmp(pwd->pw_passwd, "x") == 0) || + ((pwd->pw_passwd[0] == '#') && + (pwd->pw_passwd[1] == '#') && + (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0))) + { + struct spwd *spw = pam_modutil_getspnam (pamh, user); + + if (spw == NULL) + { + free (opasswd_tmp); + return PAM_USER_UNKNOWN; + } + oldpass = spw->sp_pwdp; + } + else + oldpass = pwd->pw_passwd; + + if (oldpass == NULL || *oldpass == '\0') + { + free (opasswd_tmp); + return PAM_SUCCESS; + } + + if ((oldpf = fopen (opasswd_file, "r")) == NULL) + { + if (errno == ENOENT) + { + pam_syslog (pamh, LOG_NOTICE, "Creating %s", opasswd_file); + do_create = 1; + } + else + { + pam_syslog (pamh, LOG_ERR, "Cannot open %s: %m", opasswd_file); + free (opasswd_tmp); + return PAM_AUTHTOK_ERR; + } + } + else if (fstat (fileno (oldpf), &opasswd_stat) < 0) + { + pam_syslog (pamh, LOG_ERR, "Cannot stat %s: %m", opasswd_file); + fclose (oldpf); + free (opasswd_tmp); + return PAM_AUTHTOK_ERR; + } + + /* Open a temp passwd file */ + newpf_fd = mkstemp (opasswd_tmp); + if (newpf_fd == -1) + { + pam_syslog (pamh, LOG_ERR, "Cannot create %s temp file: %m", + opasswd_file); + if (oldpf) + fclose (oldpf); + free (opasswd_tmp); + return PAM_AUTHTOK_ERR; + } + if (do_create) + { + if (fchmod (newpf_fd, S_IRUSR|S_IWUSR) != 0) + pam_syslog (pamh, LOG_ERR, + "Cannot set permissions of %s temp file: %m", opasswd_file); + if (fchown (newpf_fd, 0, 0) != 0) + pam_syslog (pamh, LOG_ERR, + "Cannot set owner/group of %s temp file: %m", opasswd_file); + } + else + { + if (fchmod (newpf_fd, opasswd_stat.st_mode) != 0) + pam_syslog (pamh, LOG_ERR, + "Cannot set permissions of %s temp file: %m", opasswd_file); + if (fchown (newpf_fd, opasswd_stat.st_uid, opasswd_stat.st_gid) != 0) + pam_syslog (pamh, LOG_ERR, + "Cannot set owner/group of %s temp file: %m", opasswd_file); + } + newpf = fdopen (newpf_fd, "w+"); + if (newpf == NULL) + { + pam_syslog (pamh, LOG_ERR, "Cannot fdopen %s: %m", opasswd_tmp); + if (oldpf) + fclose (oldpf); + close (newpf_fd); + retval = PAM_AUTHTOK_ERR; + goto error_opasswd; + } + + if (!do_create) + while (!feof (oldpf)) + { + char *save; + ssize_t n = getline (&buf, &buflen, oldpf); + + if (n < 1) + break; + + save = strdup (buf); /* Copy to write the original data back. */ + if (save == NULL) + { + fclose (oldpf); + fclose (newpf); + retval = PAM_BUF_ERR; + goto error_opasswd; + } + + buf[strcspn(buf, "\n")] = '\0'; + if (buf[0] == '\0') /* ignore empty lines */ + goto write_old_data; + + if (strncmp (buf, user, strlen (user)) == 0 && + buf[strlen (user)] == ':') + { + /* We found the line we needed */ + opwd entry; + + if (parse_entry (buf, &entry) == 0) + { + char *out = NULL; + + found = 1; + + /* Don't save the current password twice */ + if (entry.old_passwords && entry.old_passwords[0] != '\0') + { + char *cp = entry.old_passwords; + char *last = cp; + + entry.count = 1; /* Don't believe the count */ + while ((cp = strchr (cp, ',')) != NULL) + { + entry.count++; + last = ++cp; + } + + /* compare the last password */ + if (strcmp (last, oldpass) == 0) + goto write_old_data; + } + else + entry.count = 0; + + /* increase count. */ + entry.count++; + + /* check that we don't remember too many passwords. */ + while (entry.count > howmany && entry.count > 1) + { + char *p = strpbrk (entry.old_passwords, ","); + if (p != NULL) + entry.old_passwords = ++p; + entry.count--; + } + + if (entry.count == 1) + { + if (asprintf (&out, "%s:%s:%d:%s\n", + entry.user, entry.uid, entry.count, + oldpass) < 0) + { + free (save); + retval = PAM_AUTHTOK_ERR; + fclose (oldpf); + fclose (newpf); + goto error_opasswd; + } + } + else + { + if (asprintf (&out, "%s:%s:%d:%s,%s\n", + entry.user, entry.uid, entry.count, + entry.old_passwords, oldpass) < 0) + { + free (save); + retval = PAM_AUTHTOK_ERR; + fclose (oldpf); + fclose (newpf); + goto error_opasswd; + } + } + + if (fputs (out, newpf) < 0) + { + free (out); + free (save); + retval = PAM_AUTHTOK_ERR; + fclose (oldpf); + fclose (newpf); + goto error_opasswd; + } + free (out); + } + } + else + { + write_old_data: + if (fputs (save, newpf) < 0) + { + free (save); + retval = PAM_AUTHTOK_ERR; + fclose (oldpf); + fclose (newpf); + goto error_opasswd; + } + } + free (save); + } + + if (!found) + { + char *out; + + if (asprintf (&out, "%s:%d:1:%s\n", user, pwd->pw_uid, oldpass) < 0) + { + retval = PAM_AUTHTOK_ERR; + if (oldpf) + fclose (oldpf); + fclose (newpf); + goto error_opasswd; + } + if (fputs (out, newpf) < 0) + { + pam_overwrite_string(out); + free (out); + retval = PAM_AUTHTOK_ERR; + if (oldpf) + fclose (oldpf); + fclose (newpf); + goto error_opasswd; + } + pam_overwrite_string(out); + free (out); + } + + if (oldpf) + if (fclose (oldpf) != 0) + { + pam_syslog (pamh, LOG_ERR, "Error while closing old opasswd file: %m"); + retval = PAM_AUTHTOK_ERR; + fclose (newpf); + goto error_opasswd; + } + + if (fflush (newpf) != 0 || fsync (fileno (newpf)) != 0) + { + pam_syslog (pamh, LOG_ERR, + "Error while syncing temporary opasswd file: %m"); + retval = PAM_AUTHTOK_ERR; + fclose (newpf); + goto error_opasswd; + } + + if (fclose (newpf) != 0) + { + pam_syslog (pamh, LOG_ERR, + "Error while closing temporary opasswd file: %m"); + retval = PAM_AUTHTOK_ERR; + goto error_opasswd; + } + + char *opasswd_backup; + if (asprintf (&opasswd_backup, "%s.old", opasswd_file) < 0) + { + retval = PAM_BUF_ERR; + goto error_opasswd; + } + + unlink (opasswd_backup); + if (link (opasswd_file, opasswd_backup) != 0 && + errno != ENOENT) + pam_syslog (pamh, LOG_ERR, "Cannot create backup file of %s: %m", + opasswd_file); + rename (opasswd_tmp, opasswd_file); + free (opasswd_backup); + error_opasswd: + unlink (opasswd_tmp); + free (opasswd_tmp); + pam_overwrite_n(buf, buflen); + free (buf); + + return retval; +} diff --git a/modules/pam_pwhistory/opasswd.h b/modules/pam_pwhistory/opasswd.h new file mode 100644 index 0000000..4b981d8 --- /dev/null +++ b/modules/pam_pwhistory/opasswd.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2008 Thorsten Kukuk + * Copyright (c) 2013 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __OPASSWD_H__ +#define __OPASSWD_H__ + +#define PAM_PWHISTORY_RUN_HELPER PAM_CRED_INSUFFICIENT + +#ifdef WITH_SELINUX +#include +#define SELINUX_ENABLED (is_selinux_enabled()>0) +#else +#define SELINUX_ENABLED 0 +#endif + +#ifdef HELPER_COMPILE +#define PAMH_ARG_DECL(fname, ...) fname(__VA_ARGS__) +#else +#define PAMH_ARG_DECL(fname, ...) fname(pam_handle_t *pamh, __VA_ARGS__) +#endif + +#ifdef HELPER_COMPILE +void +PAM_FORMAT((printf, 2, 3)) +helper_log_err(int err, const char *format, ...); +#endif + +PAMH_ARG_DECL(int check_old_pass, const char *user, const char *newpass, + const char *filename, int debug); + +PAMH_ARG_DECL(int save_old_pass, const char *user, int howmany, + const char *filename, int debug); + +#endif /* __OPASSWD_H__ */ diff --git a/modules/pam_pwhistory/pam_pwhistory.8.xml b/modules/pam_pwhistory/pam_pwhistory.8.xml new file mode 100644 index 0000000..d83d8d9 --- /dev/null +++ b/modules/pam_pwhistory/pam_pwhistory.8.xml @@ -0,0 +1,300 @@ + + + + pam_pwhistory + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_pwhistory + PAM module to remember last passwords + + + + + pam_pwhistory.so + + debug + + + use_authtok + + + enforce_for_root + + + remember=N + + + retry=N + + + authtok_type=STRING + + + file=/path/filename + + + conf=/path/to/config-file + + + + + + + + DESCRIPTION + + + This module saves the last passwords for each user in order + to force password change history and keep the user from + alternating between the same password too frequently. + + + This module does not work together with kerberos. In general, + it does not make much sense to use this module in conjunction + with NIS or LDAP, since the old passwords are stored on the + local machine and are not available on another machine for + password history checking. + + + + + OPTIONS + + + + debug + + + + Turns on debugging via + + syslog3 + . + + + + + + use_authtok + + + + When password changing enforce the module to use the new password + provided by a previously stacked + module (this is used in the example of the stacking of the + pam_passwdqc module documented below). + + + + + + enforce_for_root + + + + If this option is set, the check is enforced for root, too. + + + + + + remember=N + + + + The last N passwords for each + user are saved. + The default is 10. Value of + 0 makes the module to keep the existing + contents of the opasswd file unchanged. + + + + + + retry=N + + + + Prompt user at most N times + before returning with error. The default is + 1. + + + + + + + authtok_type=STRING + + + + See + pam_get_authtok3 + for more details. + + + + + + + file=/path/filename + + + + Store password history in file /path/filename + rather than the default location. The default location is + /etc/security/opasswd. + + + + + + + conf=/path/to/config-file + + + + Use another configuration file instead of the default + /etc/security/pwhistory.conf. + + + + + + + The options for configuring the module behavior are described in the + pwhistory.conf + 5 manual page. The options + specified on the module command line override the values from the + configuration file. + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_AUTHTOK_ERR + + + No new password was entered, the user aborted password + change or new password couldn't be set. + + + + + PAM_IGNORE + + + Password history was disabled. + + + + + PAM_MAXTRIES + + + Password was rejected too often. + + + + + PAM_USER_UNKNOWN + + + User is not known to system. + + + + + + + + EXAMPLES + + An example password section would be: + +#%PAM-1.0 +password required pam_pwhistory.so +password required pam_unix.so use_authtok + + + + In combination with pam_passwdqc: + +#%PAM-1.0 +password required pam_passwdqc.so config=/etc/passwdqc.conf +password required pam_pwhistory.so use_authtok +password required pam_unix.so use_authtok + + + + + + FILES + + + /etc/security/opasswd + + Default file with password history + + + + /etc/security/pwhistory.conf + + Config file for pam_pwhistory options + + + + %vendordir%/security/pwhistory.conf + + + Config file for pam_pwhistory options. It will be used if + /etc/security/pwhistory.conf does not exist. + + + + + + + + SEE ALSO + + + pwhistory.conf5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + pam_get_authtok3 + + + + + + AUTHOR + + pam_pwhistory was written by Thorsten Kukuk <kukuk@thkukuk.de> + + + + \ No newline at end of file diff --git a/modules/pam_pwhistory/pam_pwhistory.c b/modules/pam_pwhistory/pam_pwhistory.c new file mode 100644 index 0000000..6a72092 --- /dev/null +++ b/modules/pam_pwhistory/pam_pwhistory.c @@ -0,0 +1,402 @@ +/* + * pam_pwhistory module + * + * Copyright (c) 2008, 2012 Thorsten Kukuk + * Author: Thorsten Kukuk + * Copyright (c) 2013 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "opasswd.h" +#include "pam_inline.h" +#include "pam_i18n.h" +#include "pwhistory_config.h" + + + +static void +parse_option (pam_handle_t *pamh, const char *argv, options_t *options) +{ + const char *str; + + if (strcasecmp (argv, "try_first_pass") == 0) + /* ignore */; + else if (strcasecmp (argv, "use_first_pass") == 0) + /* ignore */; + else if (strcasecmp (argv, "use_authtok") == 0) + /* ignore, handled by pam_get_authtok */; + else if (strcasecmp (argv, "debug") == 0) + options->debug = 1; + else if ((str = pam_str_skip_icase_prefix(argv, "remember=")) != NULL) + { + options->remember = strtol(str, NULL, 10); + if (options->remember < 0) + options->remember = 0; + if (options->remember > 400) + options->remember = 400; + } + else if ((str = pam_str_skip_icase_prefix(argv, "retry=")) != NULL) + { + options->tries = strtol(str, NULL, 10); + if (options->tries < 0) + options->tries = 1; + } + else if (strcasecmp (argv, "enforce_for_root") == 0) + options->enforce_for_root = 1; + else if (pam_str_skip_icase_prefix(argv, "authtok_type=") != NULL) + { /* ignore, for pam_get_authtok */; } + else if ((str = pam_str_skip_icase_prefix(argv, "file=")) != NULL) + { + if (*str != '/') + { + pam_syslog (pamh, LOG_ERR, + "pam_pwhistory: file path should be absolute: %s", argv); + } + else + options->filename = str; + } + else + pam_syslog (pamh, LOG_ERR, "pam_pwhistory: unknown option: %s", argv); +} + +#ifdef WITH_SELINUX +static int +run_save_helper(pam_handle_t *pamh, const char *user, + int howmany, const char *filename, int debug) +{ + int retval, child; + struct sigaction newsa, oldsa; + + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + + child = fork(); + if (child == 0) + { + static char *envp[] = { NULL }; + char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD) < 0) + { + _exit(PAM_SYSTEM_ERR); + } + + /* exec binary helper */ + DIAG_PUSH_IGNORE_CAST_QUAL; + args[0] = (char *)PWHISTORY_HELPER; + args[1] = (char *)"save"; + args[2] = (char *)user; + args[3] = (char *)((filename != NULL) ? filename : ""); + DIAG_POP_IGNORE_CAST_QUAL; + if (asprintf(&args[4], "%d", howmany) < 0 || + asprintf(&args[5], "%d", debug) < 0) + { + pam_syslog(pamh, LOG_ERR, "asprintf: %m"); + _exit(PAM_SYSTEM_ERR); + } + + execve(args[0], args, envp); + + pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %s: %m", args[0]); + + _exit(PAM_SYSTEM_ERR); + } + else if (child > 0) + { + /* wait for child */ + int rc = 0; + while ((rc = waitpid (child, &retval, 0)) == -1 && + errno == EINTR); + if (rc < 0) + { + pam_syslog(pamh, LOG_ERR, "pwhistory_helper save: waitpid: %m"); + retval = PAM_SYSTEM_ERR; + } + else if (!WIFEXITED(retval)) + { + pam_syslog(pamh, LOG_ERR, "pwhistory_helper save abnormal exit: %d", retval); + retval = PAM_SYSTEM_ERR; + } + else + { + retval = WEXITSTATUS(retval); + } + } + else + { + pam_syslog(pamh, LOG_ERR, "fork failed: %m"); + retval = PAM_SYSTEM_ERR; + } + + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + + return retval; +} + +static int +run_check_helper(pam_handle_t *pamh, const char *user, + const char *newpass, const char *filename, int debug) +{ + int retval, child, fds[2]; + struct sigaction newsa, oldsa; + + /* create a pipe for the password */ + if (pipe(fds) != 0) + return PAM_SYSTEM_ERR; + + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + + child = fork(); + if (child == 0) + { + static char *envp[] = { NULL }; + char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL }; + + /* reopen stdin as pipe */ + if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) + { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin"); + _exit(PAM_SYSTEM_ERR); + } + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD) < 0) + { + _exit(PAM_SYSTEM_ERR); + } + + /* exec binary helper */ + DIAG_PUSH_IGNORE_CAST_QUAL; + args[0] = (char *)PWHISTORY_HELPER; + args[1] = (char *)"check"; + args[2] = (char *)user; + args[3] = (char *)((filename != NULL) ? filename : ""); + DIAG_POP_IGNORE_CAST_QUAL; + if (asprintf(&args[4], "%d", debug) < 0) + { + pam_syslog(pamh, LOG_ERR, "asprintf: %m"); + _exit(PAM_SYSTEM_ERR); + } + + execve(args[0], args, envp); + + pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %s: %m", args[0]); + + _exit(PAM_SYSTEM_ERR); + } + else if (child > 0) + { + /* wait for child */ + int rc = 0; + if (newpass == NULL) + newpass = ""; + + /* send the password to the child */ + if (write(fds[1], newpass, strlen(newpass)+1) == -1) + { + pam_syslog(pamh, LOG_ERR, "Cannot send password to helper: %m"); + retval = PAM_SYSTEM_ERR; + } + newpass = NULL; + close(fds[0]); /* close here to avoid possible SIGPIPE above */ + close(fds[1]); + while ((rc = waitpid (child, &retval, 0)) == -1 && + errno == EINTR); + if (rc < 0) + { + pam_syslog(pamh, LOG_ERR, "pwhistory_helper check: waitpid: %m"); + retval = PAM_SYSTEM_ERR; + } + else if (!WIFEXITED(retval)) + { + pam_syslog(pamh, LOG_ERR, "pwhistory_helper check abnormal exit: %d", retval); + retval = PAM_SYSTEM_ERR; + } + else + { + retval = WEXITSTATUS(retval); + } + } + else + { + pam_syslog(pamh, LOG_ERR, "fork failed: %m"); + close(fds[0]); + close(fds[1]); + retval = PAM_SYSTEM_ERR; + } + + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + + return retval; +} +#endif + +/* This module saves the current hashed password in /etc/security/opasswd + and then compares the new password with all entries in this file. */ + +int +pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + const char *newpass; + const char *user; + int retval, tries; + options_t options; + + memset (&options, 0, sizeof (options)); + + /* Set some default values, which could be overwritten later. */ + options.remember = 10; + options.tries = 1; + + parse_config_file(pamh, argc, argv, &options); + + /* Parse parameters for module */ + for ( ; argc-- > 0; argv++) + parse_option (pamh, *argv, &options); + + if (options.debug) + pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok entered"); + + if (options.remember == 0) + return PAM_IGNORE; + + retval = pam_get_user (pamh, &user, NULL); + if (retval != PAM_SUCCESS) + return retval; + + if (flags & PAM_PRELIM_CHECK) + { + if (options.debug) + pam_syslog (pamh, LOG_DEBUG, + "pam_sm_chauthtok(PAM_PRELIM_CHECK)"); + + return PAM_SUCCESS; + } + + retval = save_old_pass (pamh, user, options.remember, options.filename, options.debug); + +#ifdef WITH_SELINUX + if (retval == PAM_PWHISTORY_RUN_HELPER) + retval = run_save_helper(pamh, user, options.remember, options.filename, options.debug); +#endif + + if (retval != PAM_SUCCESS) + return retval; + + newpass = NULL; + tries = 0; + while ((newpass == NULL) && (tries < options.tries)) + { + retval = pam_get_authtok (pamh, PAM_AUTHTOK, &newpass, NULL); + if (retval != PAM_SUCCESS && retval != PAM_TRY_AGAIN) + { + if (retval == PAM_CONV_AGAIN) + retval = PAM_INCOMPLETE; + return retval; + } + tries++; + + if (options.debug) + { + if (newpass) + pam_syslog (pamh, LOG_DEBUG, "got new auth token"); + else + pam_syslog (pamh, LOG_DEBUG, "got no auth token"); + } + + if (newpass == NULL || retval == PAM_TRY_AGAIN) + continue; + + if (options.debug) + pam_syslog (pamh, LOG_DEBUG, "check against old password file"); + + retval = check_old_pass (pamh, user, newpass, options.filename, options.debug); +#ifdef WITH_SELINUX + if (retval == PAM_PWHISTORY_RUN_HELPER) + retval = run_check_helper(pamh, user, newpass, options.filename, options.debug); +#endif + + if (retval != PAM_SUCCESS) + { + if (getuid() || options.enforce_for_root || + (flags & PAM_CHANGE_EXPIRED_AUTHTOK)) + { + pam_error (pamh, + _("Password has been already used. Choose another.")); + newpass = NULL; + /* Remove password item, else following module will use it */ + pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL); + } + else + pam_info (pamh, + _("Password has been already used.")); + } + } + + if (newpass == NULL && tries >= options.tries) + { + if (options.debug) + pam_syslog (pamh, LOG_DEBUG, "Aborted, too many tries"); + return PAM_MAXTRIES; + } + + return PAM_SUCCESS; +} diff --git a/modules/pam_pwhistory/pwhistory.conf b/modules/pam_pwhistory/pwhistory.conf new file mode 100644 index 0000000..51e783a --- /dev/null +++ b/modules/pam_pwhistory/pwhistory.conf @@ -0,0 +1,21 @@ +# Configuration for remembering the last passwords used by a user. +# +# Enable the debugging logs. +# Enabled if option is present. +# debug +# +# root account's passwords are also remembered. +# Enabled if option is present. +# enforce_for_root +# +# Number of passwords to remember. +# The default is 10. +# remember = 10 +# +# Number of times to prompt for the password. +# The default is 1. +# retry = 1 +# +# The file where the last passwords are kept. +# The default is /etc/security/opasswd. +# file = /etc/security/opasswd diff --git a/modules/pam_pwhistory/pwhistory.conf.5.xml b/modules/pam_pwhistory/pwhistory.conf.5.xml new file mode 100644 index 0000000..2a2dfd3 --- /dev/null +++ b/modules/pam_pwhistory/pwhistory.conf.5.xml @@ -0,0 +1,152 @@ + + + + pwhistory.conf + 5 + Linux-PAM + Linux-PAM Manual + + + + pwhistory.conf + pam_pwhistory configuration file + + + + + DESCRIPTION + + pwhistory.conf provides a way to configure the + default settings for saving the last passwords for each user. + This file is read by the pam_pwhistory module and is the + preferred method over configuring pam_pwhistory directly. + + + The file has a very simple name = value format with possible comments + starting with # character. The whitespace at the beginning of line, end + of line, and around the = sign is ignored. + + + + + + OPTIONS + + + + debug + + + + Turns on debugging via + + syslog3 + . + + + + + + enforce_for_root + + + + If this option is set, the check is enforced for root, too. + + + + + + remember=N + + + + The last N passwords for each + user are saved. + The default is 10. Value of + 0 makes the module to keep the existing + contents of the opasswd file unchanged. + + + + + + retry=N + + + + Prompt user at most N times + before returning with error. The default is 1. + + + + + + file=/path/filename + + + + Store password history in file + /path/filename rather than the default + location. The default location is + /etc/security/opasswd. + + + + + + + + EXAMPLES + + /etc/security/pwhistory.conf file example: + + +debug +remember=5 +file=/tmp/opasswd + + + + + FILES + + + /etc/security/pwhistory.conf + + the config file for custom options + + + + + + + SEE ALSO + + + pwhistory8 + , + + pam_pwhistory8 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_pwhistory was written by Thorsten Kukuk. The support for + pwhistory.conf was written by Iker Pedrosa. + + + + \ No newline at end of file diff --git a/modules/pam_pwhistory/pwhistory_config.c b/modules/pam_pwhistory/pwhistory_config.c new file mode 100644 index 0000000..2399bf2 --- /dev/null +++ b/modules/pam_pwhistory/pwhistory_config.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2022 Iker Pedrosa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include + +#include "pam_inline.h" +#include "pwhistory_config.h" + +#define PWHISTORY_DEFAULT_CONF SCONFIG_DIR "/pwhistory.conf" + +#ifdef VENDOR_SCONFIG_DIR +#define VENDOR_PWHISTORY_DEFAULT_CONF (VENDOR_SCONFIG_DIR "/pwhistory.conf") +#endif + +void +parse_config_file(pam_handle_t *pamh, int argc, const char **argv, + struct options_t *options) +{ + const char *fname = NULL; + int i; + char *val; + + for (i = 0; i < argc; ++i) { + const char *str = pam_str_skip_prefix(argv[i], "conf="); + + if (str != NULL) { + fname = str; + } + } + + if (fname == NULL) { + fname = PWHISTORY_DEFAULT_CONF; + +#ifdef VENDOR_PWHISTORY_DEFAULT_CONF + /* + * Check whether PWHISTORY_DEFAULT_CONF file is available. + * If it does not exist, fall back to VENDOR_PWHISTORY_DEFAULT_CONF file. + */ + struct stat buffer; + if (stat(fname, &buffer) != 0 && errno == ENOENT) { + fname = VENDOR_PWHISTORY_DEFAULT_CONF; + } +#endif + } + + val = pam_modutil_search_key (pamh, fname, "debug"); + if (val != NULL) { + options->debug = 1; + free(val); + } + + val = pam_modutil_search_key (pamh, fname, "enforce_for_root"); + if (val != NULL) { + options->enforce_for_root = 1; + free(val); + } + + val = pam_modutil_search_key (pamh, fname, "remember"); + if (val != NULL) { + unsigned int temp; + if (sscanf(val, "%u", &temp) != 1) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for remember argument"); + } else { + options->remember = temp; + } + free(val); + } + + val = pam_modutil_search_key (pamh, fname, "retry"); + if (val != NULL) { + unsigned int temp; + if (sscanf(val, "%u", &temp) != 1) { + pam_syslog(pamh, LOG_ERR, + "Bad number supplied for retry argument"); + } else { + options->tries = temp; + } + free(val); + } + + val = pam_modutil_search_key (pamh, fname, "file"); + if (val != NULL) { + if (*val != '/') { + pam_syslog (pamh, LOG_ERR, + "File path should be absolute: %s", val); + free(val); + } else { + options->filename = val; + } + } +} diff --git a/modules/pam_pwhistory/pwhistory_config.h b/modules/pam_pwhistory/pwhistory_config.h new file mode 100644 index 0000000..e2b3bc8 --- /dev/null +++ b/modules/pam_pwhistory/pwhistory_config.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022 Iker Pedrosa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PWHISTORY_CONFIG_H +#define _PWHISTORY_CONFIG_H + +#include + +struct options_t { + int debug; + int enforce_for_root; + int remember; + int tries; + const char *filename; +}; +typedef struct options_t options_t; + +void +parse_config_file(pam_handle_t *pamh, int argc, const char **argv, + struct options_t *options); + +#endif /* _PWHISTORY_CONFIG_H */ diff --git a/modules/pam_pwhistory/pwhistory_helper.8.xml b/modules/pam_pwhistory/pwhistory_helper.8.xml new file mode 100644 index 0000000..8370a48 --- /dev/null +++ b/modules/pam_pwhistory/pwhistory_helper.8.xml @@ -0,0 +1,65 @@ + + + + pwhistory_helper + 8 + Linux-PAM + Linux-PAM Manual + + + + pwhistory_helper + Helper binary that transfers password hashes from passwd or shadow to opasswd + + + + + pwhistory_helper + + ... + + + + + + + DESCRIPTION + + + pwhistory_helper is a helper program for the + pam_pwhistory module that transfers password hashes + from passwd or shadow file to the opasswd file and checks a password + supplied by user against the existing hashes in the opasswd file. + + + + The purpose of the helper is to enable tighter confinement of + login and password changing services. The helper is thus called only + when SELinux is enabled on the system. + + + + The interface of the helper - command line options, and input/output + data format are internal to the pam_pwhistory + module and it should not be called directly from applications. + + + + + SEE ALSO + + + pam_pwhistory8 + + + + + + AUTHOR + + Written by Tomas Mraz based on the code originally in + pam_pwhistory and pam_unix modules. + + + + \ No newline at end of file diff --git a/modules/pam_pwhistory/pwhistory_helper.c b/modules/pam_pwhistory/pwhistory_helper.c new file mode 100644 index 0000000..fb9a1e3 --- /dev/null +++ b/modules/pam_pwhistory/pwhistory_helper.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2013 Red Hat, Inc. + * Author: Tomas Mraz + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "opasswd.h" +#include "pam_inline.h" + + +static int +check_history(const char *user, const char *filename, const char *debug) +{ + char pass[PAM_MAX_RESP_SIZE + 1]; + char *passwords[] = { pass }; + int npass; + int dbg = atoi(debug); /* no need to be too fancy here */ + int retval; + + /* read the password from stdin (a pipe from the pam_pwhistory module) */ + npass = pam_read_passwords(STDIN_FILENO, 1, passwords); + + if (npass != 1) + { /* is it a valid password? */ + helper_log_err(LOG_DEBUG, "no password supplied"); + return PAM_AUTHTOK_ERR; + } + + retval = check_old_pass(user, pass, filename, dbg); + + pam_overwrite_array(pass); /* clear memory of the password */ + + return retval; +} + +static int +save_history(const char *user, const char *filename, const char *howmany, const char *debug) +{ + int num = atoi(howmany); + int dbg = atoi(debug); /* no need to be too fancy here */ + int retval; + + retval = save_old_pass(user, num, filename, dbg); + + return retval; +} + +int +main(int argc, char *argv[]) +{ + const char *option; + const char *user; + const char *filename; + + /* + * we establish that this program is running with non-tty stdin. + * this is to discourage casual use. + */ + + if (isatty(STDIN_FILENO) || argc < 5) + { + fprintf(stderr, + "This binary is not designed for running in this way.\n"); + return PAM_SYSTEM_ERR; + } + + option = argv[1]; + user = argv[2]; + filename = (argv[3][0] != '\0') ? argv[3] : NULL; + + if (strcmp(option, "check") == 0 && argc == 5) + return check_history(user, filename, argv[4]); + else if (strcmp(option, "save") == 0 && argc == 6) + return save_history(user, filename, argv[4], argv[5]); + + fprintf(stderr, "This binary is not designed for running in this way.\n"); + + return PAM_SYSTEM_ERR; +} diff --git a/modules/pam_pwhistory/tst-pam_pwhistory b/modules/pam_pwhistory/tst-pam_pwhistory new file mode 100755 index 0000000..3531a88 --- /dev/null +++ b/modules/pam_pwhistory/tst-pam_pwhistory @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_pwhistory.so diff --git a/modules/pam_pwhistory/tst-pam_pwhistory-retval.c b/modules/pam_pwhistory/tst-pam_pwhistory-retval.c new file mode 100644 index 0000000..aa2c24e --- /dev/null +++ b/modules/pam_pwhistory/tst-pam_pwhistory-retval.c @@ -0,0 +1,60 @@ +/* + * Check pam_pwhistory return values. + * + * Copyright (c) 2023 Stefan Schubert + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_pwhistory" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + /* PAM_USER_UNKNOWN */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, + fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_USER_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* cleanup */ + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_rhosts/README.xml b/modules/pam_rhosts/README.xml new file mode 100644 index 0000000..2345dff --- /dev/null +++ b/modules/pam_rhosts/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_rhosts.8.xml" xpointer='xpointer(id("pam_rhosts-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_rhosts/meson.build b/modules/pam_rhosts/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_rhosts/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_rhosts/pam_rhosts.8.xml b/modules/pam_rhosts/pam_rhosts.8.xml new file mode 100644 index 0000000..b8a5c1c --- /dev/null +++ b/modules/pam_rhosts/pam_rhosts.8.xml @@ -0,0 +1,168 @@ + + + + pam_rhosts + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_rhosts + The rhosts PAM module + + + + + pam_rhosts.so + + + + + + DESCRIPTION + + + This module performs the standard network authentication for services, + as used by traditional implementations of rlogin + and rsh etc. + + + The authentication mechanism of this module is based on the contents + of two files; /etc/hosts.equiv (or + and ~/.rhosts. Firstly, hosts listed in the + former file are treated as equivalent to the localhost. Secondly, + entries in the user's own copy of the latter file is used to map + "remote-host remote-user" pairs to that user's + account on the current host. Access is granted to the user if their + host is present in /etc/hosts.equiv and their + remote account is identical to their local one, or if their remote + account has an entry in their personal configuration file. + + + The module authenticates a remote user (internally specified by the + item PAM_RUSER connecting from the remote + host (internally specified by the item PAM_RHOST). + Accordingly, for applications to be compatible this authentication + module they must set these items prior to calling + pam_authenticate(). The module is not capable + of independently probing the network connection for such information. + + + + + OPTIONS + + + + debug + + + + Print debug information. + + + + + + silent + + + + Don't print informative messages. + + + + + + superuser=account + + + + Handle account as root. + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + The remote host, remote user name or the local user name + couldn't be determined or access was denied by + .rhosts file. + + + + + PAM_USER_UNKNOWN + + + User is not known to system. + + + + + + + + EXAMPLES + + To grant a remote user access by /etc/hosts.equiv + or .rhosts for rsh add the + following lines to /etc/pam.d/rsh: + +#%PAM-1.0 +# +auth required pam_rhosts.so +auth required pam_nologin.so +auth required pam_env.so +auth required pam_unix.so + + + + + + SEE ALSO + + + rootok3 + , + + hosts.equiv5 + , + + rhosts5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_rhosts was written by Thorsten Kukuk <kukuk@thkukuk.de> + + + + \ No newline at end of file diff --git a/modules/pam_rhosts/pam_rhosts.c b/modules/pam_rhosts/pam_rhosts.c new file mode 100644 index 0000000..a1b394d --- /dev/null +++ b/modules/pam_rhosts/pam_rhosts.c @@ -0,0 +1,142 @@ +/* + * pam_rhosts module + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "pam_inline.h" + +int pam_sm_authenticate (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + const char *luser = NULL; + const char *ruser = NULL, *rhost = NULL; + const char *opt_superuser = NULL; + const void *c_void; + int opt_debug = 0; + int opt_silent; + int as_root; + int retval; + + opt_silent = flags & PAM_SILENT; + + while (argc-- > 0) { + const char *str; + + if (strcmp(*argv, "debug") == 0) + opt_debug = 1; + else if (strcmp (*argv, "silent") == 0 || strcmp(*argv, "suppress") == 0) + opt_silent = 1; + else if ((str = pam_str_skip_prefix(*argv, "superuser=")) != NULL) + opt_superuser = str; + else + pam_syslog(pamh, LOG_WARNING, "unrecognized option '%s'", *argv); + + ++argv; + } + + retval = pam_get_item (pamh, PAM_RHOST, &c_void); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "could not get the remote host name"); + return retval; + } + rhost = c_void; + + retval = pam_get_item(pamh, PAM_RUSER, &c_void); + ruser = c_void; + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "could not get the remote username"); + return retval; + } + + retval = pam_get_user(pamh, &luser, NULL); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine local user name: %s", + pam_strerror(pamh, retval)); + return retval; + } + + if (rhost == NULL || ruser == NULL) + return PAM_AUTH_ERR; + + if (opt_superuser && strcmp(opt_superuser, luser) == 0) + as_root = 1; + else { + struct passwd *lpwd; + + lpwd = pam_modutil_getpwnam(pamh, luser); + if (lpwd == NULL) { + if (opt_debug) + /* don't print by default, could be the user's password */ + pam_syslog(pamh, LOG_DEBUG, + "user '%s' unknown to this system", luser); + return PAM_USER_UNKNOWN; + + } + as_root = (lpwd->pw_uid == 0); + } + +#ifdef HAVE_RUSEROK_AF + retval = ruserok_af (rhost, as_root, ruser, luser, PF_UNSPEC); +#else + retval = ruserok (rhost, as_root, ruser, luser); +#endif + if (retval != 0) { + if (!opt_silent || opt_debug) + pam_syslog(pamh, LOG_WARNING, "denied access to %s@%s as %s", + ruser, rhost, luser); + return PAM_AUTH_ERR; + } else { + if (!opt_silent || opt_debug) + pam_syslog(pamh, LOG_NOTICE, "allowed access to %s@%s as %s", + ruser, rhost, luser); + return PAM_SUCCESS; + } +} + + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} diff --git a/modules/pam_rhosts/tst-pam_rhosts b/modules/pam_rhosts/tst-pam_rhosts new file mode 100755 index 0000000..65e85a9 --- /dev/null +++ b/modules/pam_rhosts/tst-pam_rhosts @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_rhosts.so diff --git a/modules/pam_rootok/README.xml b/modules/pam_rootok/README.xml new file mode 100644 index 0000000..58f7796 --- /dev/null +++ b/modules/pam_rootok/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_rootok.8.xml" xpointer='xpointer(id("pam_rootok-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_rootok/meson.build b/modules/pam_rootok/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_rootok/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_rootok/pam_rootok.8.xml b/modules/pam_rootok/pam_rootok.8.xml new file mode 100644 index 0000000..a79c073 --- /dev/null +++ b/modules/pam_rootok/pam_rootok.8.xml @@ -0,0 +1,128 @@ + + + + pam_rootok + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_rootok + Gain only root access + + + + + pam_rootok.so + + debug + + + + + + + DESCRIPTION + + + pam_rootok is a PAM module that authenticates the user if their + UID is 0. + Applications that are created setuid-root generally retain the + UID of the user but run with the authority + of an enhanced effective-UID. It is the real UID + that is checked. + + + + + OPTIONS + + + + debug + + + + Print debug information. + + + + + + + + MODULE TYPES PROVIDED + + The , and + module types are provided. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + The UID is 0. + + + + + PAM_AUTH_ERR + + + The UID is not + 0. + + + + + + + + EXAMPLES + + In the case of the + su1 + application the historical usage is to + permit the superuser to adopt the identity of a lesser user + without the use of a password. To obtain this behavior with PAM + the following pair of lines are needed for the corresponding entry + in the /etc/pam.d/su configuration file: + +# su authentication. Root is granted access by default. +auth sufficient pam_rootok.so +auth required pam_unix.so + + + + + + SEE ALSO + + + su1 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_rootok was written by Andrew G. Morgan, <morgan@kernel.org>. + + + + \ No newline at end of file diff --git a/modules/pam_rootok/pam_rootok.c b/modules/pam_rootok/pam_rootok.c new file mode 100644 index 0000000..0c20615 --- /dev/null +++ b/modules/pam_rootok/pam_rootok.c @@ -0,0 +1,173 @@ +/* + * pam_rootok module + * + * Written by Andrew Morgan 1996/3/11 + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WITH_SELINUX +#include +#include +#endif + +#ifdef HAVE_LIBAUDIT +#include +#endif + +/* argument parsing */ + +#define PAM_DEBUG_ARG 01 + +static int +_pam_parse (const pam_handle_t *pamh, int argc, const char **argv) +{ + int ctrl=0; + + /* step through arguments */ + for (ctrl=0; argc-- > 0; ++argv) { + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_DEBUG_ARG; + else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + return ctrl; +} + +#ifdef WITH_SELINUX +static int +PAM_FORMAT((printf, 2, 3)) +log_callback (int type UNUSED, const char *fmt, ...) +{ + va_list ap; + +#ifdef HAVE_LIBAUDIT + int audit_fd = audit_open(); + + if (audit_fd >= 0) { + char *buf; + int ret; + + va_start(ap, fmt); + ret = vasprintf (&buf, fmt, ap); + va_end(ap); + if (ret < 0) { + audit_close(audit_fd); + return 0; + } + (void) !audit_log_user_avc_message(audit_fd, AUDIT_USER_AVC, buf, + NULL, NULL, NULL, 0); + audit_close(audit_fd); + free(buf); + return 0; + } + +#endif + va_start(ap, fmt); + vsyslog (LOG_USER | LOG_INFO, fmt, ap); + va_end(ap); + return 0; +} + +static int +selinux_check_root (void) +{ + int status = -1; + char *user_context_raw; + union selinux_callback old_callback; + + if (is_selinux_enabled() < 1) + return 0; + + old_callback = selinux_get_callback(SELINUX_CB_LOG); + /* setup callbacks */ + selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) &log_callback); + if ((status = getprevcon_raw(&user_context_raw)) < 0) { + selinux_set_callback(SELINUX_CB_LOG, old_callback); + return status; + } + + status = selinux_check_access(user_context_raw, user_context_raw, "passwd", "rootok", NULL); + + selinux_set_callback(SELINUX_CB_LOG, old_callback); + freecon(user_context_raw); + return status; +} +#endif + +static int +check_for_root (pam_handle_t *pamh, int ctrl) +{ + int retval = PAM_AUTH_ERR; + + if (getuid() == 0) +#ifdef WITH_SELINUX + if (selinux_check_root() == 0 || security_getenforce() == 0) +#endif + retval = PAM_SUCCESS; + + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "root check %s", + (retval==PAM_SUCCESS) ? "succeeded" : "failed"); + } + + return retval; +} + +/* --- management functions --- */ + +int +pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int ctrl; + + ctrl = _pam_parse(pamh, argc, argv); + + return check_for_root (pamh, ctrl); +} + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_acct_mgmt (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int ctrl; + + ctrl = _pam_parse(pamh, argc, argv); + + return check_for_root (pamh, ctrl); +} + +int +pam_sm_chauthtok (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int ctrl; + + ctrl = _pam_parse(pamh, argc, argv); + + return check_for_root (pamh, ctrl); +} + +/* end of module definition */ diff --git a/modules/pam_rootok/tst-pam_rootok b/modules/pam_rootok/tst-pam_rootok new file mode 100755 index 0000000..385ef76 --- /dev/null +++ b/modules/pam_rootok/tst-pam_rootok @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_rootok.so diff --git a/modules/pam_rootok/tst-pam_rootok-retval.c b/modules/pam_rootok/tst-pam_rootok-retval.c new file mode 100644 index 0000000..990ee12 --- /dev/null +++ b/modules/pam_rootok/tst-pam_rootok-retval.c @@ -0,0 +1,72 @@ +/* + * Check pam_rootok return values. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_rootok" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char user_name[] = ""; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + if (getuid() == 0) { + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + } else { + ASSERT_EQ(PAM_AUTH_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_chauthtok(pamh, 0)); + } + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_securetty/README.xml b/modules/pam_securetty/README.xml new file mode 100644 index 0000000..70176d7 --- /dev/null +++ b/modules/pam_securetty/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_securetty.8.xml" xpointer='xpointer(id("pam_securetty-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_securetty/meson.build b/modules/pam_securetty/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_securetty/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_securetty/pam_securetty.8.xml b/modules/pam_securetty/pam_securetty.8.xml new file mode 100644 index 0000000..9038f5b --- /dev/null +++ b/modules/pam_securetty/pam_securetty.8.xml @@ -0,0 +1,199 @@ + + + + pam_securetty + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_securetty + Limit root login to special devices + + + + + pam_securetty.so + + debug + + + + + + + DESCRIPTION + + + pam_securetty is a PAM module that allows root logins only if the + user is logging in on a "secure" tty, as defined by the listing + in the securetty file. pam_securetty checks at + first, if /etc/securetty exists. If not and + it was built with vendordir support, it will use + %vendordir%/securetty. pam_securetty also + checks that the securetty files are plain + files and not world writable. It will also allow root logins on + the tty specified with switch on the + kernel command line and on ttys from the + /sys/class/tty/console/active. + + + This module has no effect on non-root users and requires that the + application fills in the PAM_TTY + item correctly. + + + For canonical usage, should be listed as a + required authentication method + before any sufficient + authentication methods. + + + + + OPTIONS + + + + debug + + + + Print debug information. + + + + + + noconsole + + + + Do not automatically allow root logins on the kernel console + device, as specified on the kernel command line or by the sys file, + if it is not also specified in the + securetty file. + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + The user is allowed to continue authentication. + Either the user is not root, or the root user is + trying to log in on an acceptable device. + + + + + PAM_AUTH_ERR + + + Authentication is rejected. Either root is attempting to + log in via an unacceptable device, or the + securetty file is world writable or + not a normal file. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_CONV_ERR + + + The conversation method supplied by the application + failed to obtain the username. + + + + + PAM_INCOMPLETE + + + The conversation method supplied by the application + returned PAM_CONV_AGAIN. + + + + + PAM_SERVICE_ERR + + + An error occurred while the module was determining the + user's name or tty, or the module could not open + the securetty file. + + + + + PAM_USER_UNKNOWN + + + The module could not find the user name in the + /etc/passwd file to verify whether + the user had a UID of 0. Therefore, the results of running + this module are ignored. + + + + + + + + EXAMPLES + + +auth required pam_securetty.so +auth required pam_unix.so + + + + + + SEE ALSO + + + securetty5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_securetty was written by Elliot Lee <sopwith@cuc.edu>. + + + + \ No newline at end of file diff --git a/modules/pam_securetty/pam_securetty.c b/modules/pam_securetty/pam_securetty.c new file mode 100644 index 0000000..9264a30 --- /dev/null +++ b/modules/pam_securetty/pam_securetty.c @@ -0,0 +1,298 @@ +/* + * pam_securetty module + * + * by Elliot Lee , Red Hat Software. + * July 25, 1996. + * This code shamelessly ripped from the pam_rootok module. + * Slight modifications AGM. 1996/12/3 + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "pam_inline.h" + +#define PAM_DEBUG_ARG 0x0001 +#define PAM_NOCONSOLE_ARG 0x0002 + +#define SECURETTY_FILE "/etc/securetty" +#ifdef VENDORDIR +#define SECURETTY2_FILE VENDORDIR"/securetty" +#endif +#define TTY_PREFIX "/dev/" +#define CMDLINE_FILE "/proc/cmdline" +#define CONSOLEACTIVE_FILE "/sys/class/tty/console/active" + +static int +_pam_parse (const pam_handle_t *pamh, int argc, const char **argv) +{ + int ctrl=0; + + /* step through arguments */ + for (ctrl=0; argc-- > 0; ++argv) { + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_DEBUG_ARG; + else if (!strcmp(*argv, "noconsole")) + ctrl |= PAM_NOCONSOLE_ARG; + else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + return ctrl; +} + +static int +securetty_perform_check (pam_handle_t *pamh, int ctrl, + const char *function_name) +{ + int retval = PAM_AUTH_ERR; + const char *securettyfile; + const char *username; + const char *uttyname; + const char *str; + const void *void_uttyname; + char *ttyfileline = NULL; + size_t ttyfilelinelen = 0; + char ptname[256]; + struct stat ttyfileinfo; + struct passwd *user_pwd; + FILE *ttyfile; + + /* log a trail for debugging */ + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "pam_securetty called via %s function", + function_name); + } + + retval = pam_get_user(pamh, &username, NULL); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", + pam_strerror(pamh, retval)); + return (retval == PAM_CONV_AGAIN ? PAM_INCOMPLETE : retval); + } + + user_pwd = pam_modutil_getpwnam(pamh, username); + if (user_pwd != NULL && user_pwd->pw_uid != 0) { + /* If the user is not root, securetty's does not apply to them */ + return PAM_SUCCESS; + } + /* The user is now either root or an invalid / mistyped username */ + + retval = pam_get_item(pamh, PAM_TTY, &void_uttyname); + uttyname = void_uttyname; + if (retval != PAM_SUCCESS || uttyname == NULL) { + pam_syslog (pamh, LOG_ERR, "cannot determine user's tty"); + return PAM_SERVICE_ERR; + } + + /* The PAM_TTY item may be prefixed with "/dev/" - skip that */ + if ((str = pam_str_skip_prefix(uttyname, TTY_PREFIX)) != NULL) + uttyname = str; + + if (stat(SECURETTY_FILE, &ttyfileinfo)) { +#ifdef VENDORDIR + if (errno == ENOENT) { + if (stat(SECURETTY2_FILE, &ttyfileinfo)) { + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_DEBUG, + "Couldn't open %s: %m", SECURETTY2_FILE); + return PAM_SUCCESS; /* for compatibility with old securetty handling, + this needs to succeed. But we still log the + error. */ + } + securettyfile = SECURETTY2_FILE; + } else { +#endif + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_DEBUG, "Couldn't open %s: %m", SECURETTY_FILE); + return PAM_SUCCESS; /* for compatibility with old securetty handling, + this needs to succeed. But we still log the + error. */ +#ifdef VENDORDIR + } +#endif + } else { + securettyfile = SECURETTY_FILE; + } + + if ((ttyfileinfo.st_mode & S_IWOTH) || !S_ISREG(ttyfileinfo.st_mode)) { + /* If the file is world writable or is not a + normal file, return error */ + pam_syslog(pamh, LOG_ERR, + "%s is either world writable or not a normal file", + securettyfile); + return PAM_AUTH_ERR; + } + + ttyfile = fopen(securettyfile,"r"); + if (ttyfile == NULL) { /* Check that we opened it successfully */ + pam_syslog(pamh, LOG_ERR, "Error opening %s: %m", securettyfile); + return PAM_SERVICE_ERR; + } + + if (isdigit((unsigned char)uttyname[0])) { + snprintf(ptname, sizeof(ptname), "pts/%s", uttyname); + } else { + ptname[0] = '\0'; + } + + retval = 1; + + while (retval && getline(&ttyfileline, &ttyfilelinelen, ttyfile) != -1) { + ttyfileline[strcspn(ttyfileline, "\n")] = '\0'; + + retval = ( strcmp(ttyfileline, uttyname) + && (!ptname[0] || strcmp(ptname, uttyname)) ); + } + free(ttyfileline); + fclose(ttyfile); + + if (retval && !(ctrl & PAM_NOCONSOLE_ARG)) { + FILE *cmdlinefile; + + /* Allow access from the kernel console, if enabled */ + cmdlinefile = fopen(CMDLINE_FILE, "r"); + + if (cmdlinefile != NULL) { + char *p; + char *line = NULL; + size_t linelen = 0; + + if (getline(&line, &linelen, cmdlinefile) == -1) + p = NULL; + else + p = line; + fclose(cmdlinefile); + + for (; p; p = strstr(p+1, "console=")) { + const char *e; + + /* Test whether this is a beginning of a word? */ + if (p > line && p[-1] != ' ') + continue; + + /* Is this our console? */ + if ((e = pam_str_skip_prefix_len(p + 8, uttyname, strlen(uttyname))) == NULL) + continue; + + /* Is there any garbage after the TTY name? */ + if (*e == ',' || *e == ' ' || *e == '\n' || *e == 0) { + retval = 0; + break; + } + } + + free(line); + } + } + if (retval && !(ctrl & PAM_NOCONSOLE_ARG)) { + FILE *consoleactivefile; + + /* Allow access from the active console */ + consoleactivefile = fopen(CONSOLEACTIVE_FILE, "r"); + + if (consoleactivefile != NULL) { + char *p, *n; + char *line = NULL; + size_t linelen = 0; + + if (getline(&line, &linelen, consoleactivefile) == -1) + p = NULL; + else + p = line; + fclose(consoleactivefile); + + if (p) { + /* remove the newline character at end */ + line[strcspn(line, "\n")] = '\0'; + + for (n = p; n != NULL; p = n+1) { + if ((n = strchr(p, ' ')) != NULL) + *n = '\0'; + + if (strcmp(p, uttyname) == 0) { + retval = 0; + break; + } + } + } + + free(line); + } + } + + if (retval) { + pam_syslog(pamh, LOG_NOTICE, "access denied: tty '%s' is not secure !", + uttyname); + + retval = PAM_AUTH_ERR; + if (user_pwd == NULL) { + retval = PAM_USER_UNKNOWN; + } + } else { + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "access allowed for '%s' on '%s'", + username, uttyname); + } + retval = PAM_SUCCESS; + + } + + return retval; +} + +/* --- authentication management functions --- */ + +int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, int argc, + const char **argv) +{ + int ctrl; + + /* parse the arguments */ + ctrl = _pam_parse (pamh, argc, argv); + + return securetty_perform_check(pamh, ctrl, __FUNCTION__); +} + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +/* --- account management functions --- */ + +int +pam_sm_acct_mgmt (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int ctrl; + + /* parse the arguments */ + ctrl = _pam_parse (pamh, argc, argv); + + /* take the easy route */ + return securetty_perform_check(pamh, ctrl, __FUNCTION__); +} + +/* end of module definition */ diff --git a/modules/pam_securetty/tst-pam_securetty b/modules/pam_securetty/tst-pam_securetty new file mode 100755 index 0000000..1252f79 --- /dev/null +++ b/modules/pam_securetty/tst-pam_securetty @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_securetty.so diff --git a/modules/pam_selinux/README.xml b/modules/pam_selinux/README.xml new file mode 100644 index 0000000..dc1b569 --- /dev/null +++ b/modules/pam_selinux/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_selinux.8.xml" xpointer='xpointer(id("pam_selinux-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_selinux/meson.build b/modules/pam_selinux/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_selinux/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_selinux/pam_selinux.8.xml b/modules/pam_selinux/pam_selinux.8.xml new file mode 100644 index 0000000..3aa632c --- /dev/null +++ b/modules/pam_selinux/pam_selinux.8.xml @@ -0,0 +1,273 @@ + + + + pam_selinux + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_selinux + PAM module to set the default security context + + + + + pam_selinux.so + + open + + + close + + + restore + + + nottys + + + debug + + + verbose + + + select_context + + + env_params + + + use_current_range + + + + + + DESCRIPTION + + pam_selinux is a PAM module that sets up the default SELinux security + context for the next executed process. + + + When a new session is started, the open_session part of the module + computes and sets up the execution security context used for the next + + execve2 + + call, the file security context for the controlling terminal, and + the security context used for creating a new kernel keyring. + + + When the session is ended, the close_session part of the module restores + old security contexts that were in effect before the change made + by the open_session part of the module. + + + Adding pam_selinux into the PAM stack might disrupt behavior of other + PAM modules which execute applications. To avoid that, + pam_selinux.so open should be placed after such + modules in the PAM stack, and pam_selinux.so close + should be placed before them. When such a placement is not feasible, + pam_selinux.so restore could be used to temporary + restore original security contexts. + + + + + OPTIONS + + + + open + + + + Only execute the open_session part of the module. + + + + + + close + + + + Only execute the close_session part of the module. + + + + + + restore + + + + In open_session part of the module, temporarily restore the + security contexts as they were before the previous call of + the module. Another call of this module without the restore + option will set up the new security contexts again. + + + + + + nottys + + + + Do not setup security context of the controlling terminal. + + + + + + debug + + + + Turn on debug messages via + + syslog3 + . + + + + + + verbose + + + + Attempt to inform the user when security context is set. + + + + + + select_context + + + + Attempt to ask the user for a custom security context role. + If MLS is on, ask also for sensitivity level. + + + + + + env_params + + + + Attempt to obtain a custom security context role from PAM environment. + If MLS is on, obtain also sensitivity level. This option and the + select_context option are mutually exclusive. The respective PAM + environment variables are SELINUX_ROLE_REQUESTED, + SELINUX_LEVEL_REQUESTED, and + SELINUX_USE_CURRENT_RANGE. The first two variables + are self describing and the last one if set to 1 makes the PAM module behave as + if the use_current_range was specified on the command line of the module. + + + + + + use_current_range + + + + Use the sensitivity level of the current process for the user context + instead of the default level. Also suppresses asking of the + sensitivity level from the user or obtaining it from PAM environment. + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + The security context was set successfully. + + + + + PAM_SESSION_ERR + + + Unable to get or set a valid context. + + + + + PAM_USER_UNKNOWN + + + The user is not known to the system. + + + + + PAM_BUF_ERR + + + Memory allocation error. + + + + + + + + EXAMPLES + +auth required pam_unix.so +session required pam_permit.so +session optional pam_selinux.so + + + + + SEE ALSO + + + execve2 + , + + tty4 + , + + pam.d5 + , + + pam8 + , + + selinux8 + + + + + + AUTHOR + + pam_selinux was written by Dan Walsh <dwalsh@redhat.com>. + + + + \ No newline at end of file diff --git a/modules/pam_selinux/pam_selinux.c b/modules/pam_selinux/pam_selinux.c new file mode 100644 index 0000000..a927612 --- /dev/null +++ b/modules/pam_selinux/pam_selinux.c @@ -0,0 +1,819 @@ +/****************************************************************************** + * A module for Linux-PAM that will set the default security context after login + * via PAM. + * + * Copyright (c) 2003-2008 Red Hat, Inc. + * Written by Dan Walsh + * Additional improvements by Tomas Mraz + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "pam_inline.h" +#include "pam_i18n.h" + +#include +#include +#include +#include + +#ifdef HAVE_LIBAUDIT +#include +#include +#endif + +/* Send audit message */ +static void +send_audit_message(const pam_handle_t *pamh, int success, const char *default_context, + const char *selected_context) +{ +#ifdef HAVE_LIBAUDIT + char *msg = NULL; + int audit_fd = audit_open(); + char *default_raw = NULL; + char *selected_raw = NULL; + const void *tty = NULL, *rhost = NULL; + if (audit_fd < 0) { + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) { + goto fallback; /* No audit support in kernel */ + } + pam_syslog(pamh, LOG_ERR, "Error connecting to audit system: %m"); + goto fallback; + } + (void)pam_get_item(pamh, PAM_TTY, &tty); + (void)pam_get_item(pamh, PAM_RHOST, &rhost); + if (selinux_trans_to_raw_context(default_context, &default_raw) < 0) { + pam_syslog(pamh, LOG_ERR, "Error translating default context '%s'.", default_context); + default_raw = NULL; + } + if (selinux_trans_to_raw_context(selected_context, &selected_raw) < 0) { + pam_syslog(pamh, LOG_ERR, "Error translating selected context '%s'.", selected_context); + selected_raw = NULL; + } + if (asprintf(&msg, "op=pam_selinux default-context=%s selected-context=%s", + default_raw ? default_raw : (default_context ? default_context : "?"), + selected_raw ? selected_raw : (selected_context ? selected_context : "?")) < 0) { + msg = NULL; /* asprintf leaves msg in undefined state on failure */ + pam_syslog(pamh, LOG_ERR, "Error allocating memory."); + goto fallback; + } + if (audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE, + msg, rhost, NULL, tty, success) <= 0) { + pam_syslog(pamh, LOG_ERR, "Error sending audit message: %m"); + goto fallback; + } + goto cleanup; + + fallback: +#endif /* HAVE_LIBAUDIT */ + pam_syslog(pamh, LOG_NOTICE, "pam: default-context=%s selected-context=%s success %d", + default_context ? default_context : "(null)", + selected_context ? selected_context : "(null)", success); + +#ifdef HAVE_LIBAUDIT + cleanup: + free(msg); + freecon(default_raw); + freecon(selected_raw); + if (audit_fd >= 0) + close(audit_fd); +#endif /* HAVE_LIBAUDIT */ +} + +static int +send_text (pam_handle_t *pamh, const char *text, int debug) +{ + if (debug) + pam_syslog(pamh, LOG_NOTICE, "%s", text); + return pam_info (pamh, "%s", text); +} + +/* + * This function sends a message to the user and gets the response. The caller + * is responsible for freeing the responses. + */ +static int +query_response (pam_handle_t *pamh, const char *text, const char *def, + char **response, int debug) +{ + int rc; + if (def) + rc = pam_prompt (pamh, PAM_PROMPT_ECHO_ON, response, "%s [%s] ", text, def); + else + rc = pam_prompt (pamh, PAM_PROMPT_ECHO_ON, response, "%s ", text); + + if (*response == NULL) { + rc = PAM_CONV_ERR; + } + + if (rc != PAM_SUCCESS) { + pam_syslog(pamh, LOG_WARNING, "No response to query: %s", text); + } else if (debug) + pam_syslog(pamh, LOG_NOTICE, "%s %s", text, *response); + return rc; +} + +static char * +config_context (pam_handle_t *pamh, const char *defaultcon, int use_current_range, int debug) +{ + char *newcon = NULL; + context_t new_context; + int mls_enabled = is_selinux_mls_enabled(); + char *response=NULL; + char *type=NULL; + char resp_val = 0; + + pam_prompt (pamh, PAM_TEXT_INFO, NULL, _("The default security context is %s."), defaultcon); + + while (1) { + if (query_response(pamh, + _("Would you like to enter a different role or level?"), "n", + &response, debug) == PAM_SUCCESS) { + resp_val = response[0]; + _pam_drop(response); + } else { + resp_val = 'N'; + } + if ((resp_val == 'y') || (resp_val == 'Y')) + { + if ((new_context = context_new(defaultcon)) == NULL) + goto fail_set; + + /* Allow the user to enter role and level individually */ + if (query_response(pamh, _("role:"), context_role_get(new_context), + &response, debug) == PAM_SUCCESS && response[0]) { + if (get_default_type(response, &type)) { + pam_prompt(pamh, PAM_ERROR_MSG, NULL, + _("There is no default type for role %s."), response); + _pam_drop(response); + continue; + } else { + if (context_role_set(new_context, response)) + goto fail_set; + if (context_type_set (new_context, type)) + goto fail_set; + _pam_drop(type); + } + } + _pam_drop(response); + + if (mls_enabled) + { + if (use_current_range) { + char *mycon = NULL; + context_t my_context; + + if (getcon(&mycon) != 0) + goto fail_set; + my_context = context_new(mycon); + if (my_context == NULL) { + freecon(mycon); + goto fail_set; + } + freecon(mycon); + if (context_range_set(new_context, context_range_get(my_context))) { + context_free(my_context); + goto fail_set; + } + context_free(my_context); + } else if (query_response(pamh, _("level:"), context_range_get(new_context), + &response, debug) == PAM_SUCCESS && response[0]) { + if (context_range_set(new_context, response)) + goto fail_set; + } + _pam_drop(response); + } + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Selected Security Context %s", context_str(new_context)); + + /* Get the string value of the context and see if it is valid. */ + if (!security_check_context(context_str(new_context))) { + newcon = strdup(context_str(new_context)); + if (newcon == NULL) + goto fail_set; + context_free(new_context); + + /* we have to check that this user is allowed to go into the + range they have specified ... role is tied to an seuser, so that'll + be checked at setexeccon time */ + if (mls_enabled && + selinux_check_access(defaultcon, newcon, "context", "contains", NULL) != 0) { + pam_syslog(pamh, LOG_NOTICE, "Security context %s is not allowed for %s", defaultcon, newcon); + + send_audit_message(pamh, 0, defaultcon, newcon); + + free(newcon); + goto fail_range; + } + return newcon; + } + else { + send_audit_message(pamh, 0, defaultcon, context_str(new_context)); + send_text(pamh,_("This is not a valid security context."),debug); + } + context_free(new_context); /* next time around allocates another */ + } + else + return strdup(defaultcon); + } /* end while */ + + return NULL; + + fail_set: + free(type); + _pam_drop(response); + context_free (new_context); + send_audit_message(pamh, 0, defaultcon, NULL); + fail_range: + return NULL; +} + +static char * +context_from_env (pam_handle_t *pamh, const char *defaultcon, int env_params, int use_current_range, int debug) +{ + char *newcon = NULL; + context_t new_context; + context_t my_context = NULL; + int mls_enabled = is_selinux_mls_enabled(); + const char *env = NULL; + char *type = NULL; + int fail = 1; + + if ((new_context = context_new(defaultcon)) == NULL) + goto fail_set; + + if (env_params && (env = pam_getenv(pamh, "SELINUX_ROLE_REQUESTED")) != NULL && env[0] != '\0') { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Requested role: %s", env); + + if (get_default_type(env, &type)) { + pam_syslog(pamh, LOG_NOTICE, "No default type for role %s", env); + goto fail_set; + } else { + if (context_role_set(new_context, env)) + goto fail_set; + if (context_type_set(new_context, type)) + goto fail_set; + } + } + + if (mls_enabled) { + if ((env = pam_getenv(pamh, "SELINUX_USE_CURRENT_RANGE")) != NULL && env[0] == '1') { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "SELINUX_USE_CURRENT_RANGE is set"); + use_current_range = 1; + } + + if (use_current_range) { + char *mycon = NULL; + + if (getcon(&mycon) != 0) + goto fail_set; + my_context = context_new(mycon); + if (my_context == NULL) { + freecon(mycon); + goto fail_set; + } + freecon(mycon); + env = context_range_get(my_context); + } else { + env = pam_getenv(pamh, "SELINUX_LEVEL_REQUESTED"); + } + + if (env != NULL && env[0] != '\0') { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Requested level: %s", env); + if (context_range_set(new_context, env)) + goto fail_set; + } + } + + newcon = strdup(context_str(new_context)); + if (newcon == NULL) + goto fail_set; + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Selected Security Context %s", newcon); + + /* Get the string value of the context and see if it is valid. */ + if (security_check_context(newcon)) { + pam_syslog(pamh, LOG_NOTICE, "Not a valid security context %s", newcon); + + goto fail_set; + } + + /* we have to check that this user is allowed to go into the + range they have specified ... role is tied to an seuser, so that'll + be checked at setexeccon time */ + if (mls_enabled && + selinux_check_access(defaultcon, newcon, "context", "contains", NULL) != 0) { + pam_syslog(pamh, LOG_NOTICE, "Security context %s is not allowed for %s", defaultcon, newcon); + + goto fail_set; + } + + fail = 0; + + fail_set: + free(type); + context_free(my_context); + context_free(new_context); + if (fail) { + send_audit_message(pamh, 0, defaultcon, newcon); + freecon(newcon); + newcon = NULL; + } + return newcon; +} + +#define DATANAME "pam_selinux_context" +typedef struct { + char *exec_context; + char *prev_exec_context; + char *default_user_context; + char *tty_context; + char *prev_tty_context; + char *tty_path; +} module_data_t; + +static void +free_module_data(module_data_t *data) +{ + free(data->tty_path); + freecon(data->prev_tty_context); + freecon(data->tty_context); + freecon(data->default_user_context); + freecon(data->prev_exec_context); + if (data->exec_context != data->default_user_context) + freecon(data->exec_context); + free(data); +} + +static void +cleanup(pam_handle_t *pamh UNUSED, void *data, int err UNUSED) +{ + free_module_data(data); +} + +static const module_data_t * +get_module_data(const pam_handle_t *pamh) +{ + const void *data; + + return (pam_get_data(pamh, DATANAME, &data) == PAM_SUCCESS) ? data : NULL; +} + +static const char * +get_item(const pam_handle_t *pamh, int item_type) +{ + const void *item; + + return (pam_get_item(pamh, item_type, &item) == PAM_SUCCESS) ? item : NULL; +} + +static int +set_exec_context(const pam_handle_t *pamh, const char *context) +{ + if (setexeccon(context) == 0) + return 0; + pam_syslog(pamh, LOG_ERR, "Setting executable context \"%s\" failed: %m", + context ? context : ""); + return -1; +} + +static int +set_file_context(const pam_handle_t *pamh, const char *context, + const char *file) +{ + if (!file) + return 0; + if (setfilecon(file, context) == 0 || errno == ENOENT) + return 0; + pam_syslog(pamh, LOG_ERR, "Setting file context \"%s\" failed for %s: %m", + context ? context : "", file); + return -1; +} + +static int +compute_exec_context(pam_handle_t *pamh, module_data_t *data, + int select_context, int use_current_range, + int env_params, int debug) +{ + const char *username; + +#ifdef HAVE_GETSEUSER + const char *service; +#endif + char *seuser = NULL; + char *level = NULL; + char **contextlist = NULL; + int num_contexts = 0; + const struct passwd *pwd; + + if (!(username = get_item(pamh, PAM_USER))) { + pam_syslog(pamh, LOG_ERR, "Cannot obtain the user name"); + return PAM_USER_UNKNOWN; + } + + if ((pwd = pam_modutil_getpwnam(pamh, username)) != NULL) { + username = pwd->pw_name; + } /* ignore error and keep using original username */ + + /* compute execute context */ +#ifdef HAVE_GETSEUSER + if (!(service = get_item(pamh, PAM_SERVICE))) { + pam_syslog(pamh, LOG_ERR, "Cannot obtain the service name"); + return PAM_SESSION_ERR; + } + if (getseuser(username, service, &seuser, &level) == 0) { +#else + if (getseuserbyname(username, &seuser, &level) == 0) { +#endif + num_contexts = get_ordered_context_list_with_level(seuser, level, NULL, + &contextlist); + if (debug) + pam_syslog(pamh, LOG_DEBUG, "Username= %s SELinux User= %s Level= %s", + username, seuser, level); + free(level); + } + if (num_contexts > 0) { + free(seuser); + data->default_user_context = strdup(contextlist[0]); + freeconary(contextlist); + if (!data->default_user_context) { + pam_syslog(pamh, LOG_CRIT, "Out of memory"); + return PAM_BUF_ERR; + } + + data->exec_context = data->default_user_context; + if (select_context) + data->exec_context = config_context(pamh, data->default_user_context, + use_current_range, debug); + else if (env_params || use_current_range) + data->exec_context = context_from_env(pamh, data->default_user_context, + env_params, use_current_range, + debug); + } + + if (!data->exec_context) { + pam_syslog(pamh, LOG_ERR, "Unable to get valid context for %s", username); + pam_prompt(pamh, PAM_ERROR_MSG, NULL, + _("A valid context for %s could not be obtained."), username); + } + + if (getexeccon(&data->prev_exec_context) < 0) + data->prev_exec_context = NULL; + + return PAM_SUCCESS; +} + +static int +compute_tty_context(const pam_handle_t *pamh, module_data_t *data) +{ + const char *tty = get_item(pamh, PAM_TTY); + security_class_t tclass; + + if (!tty || !*tty || !strcmp(tty, "ssh") + || pam_str_skip_prefix(tty, "NODEV") != NULL) { + tty = ttyname(STDIN_FILENO); + if (!tty || !*tty) + tty = ttyname(STDOUT_FILENO); + if (!tty || !*tty) + tty = ttyname(STDERR_FILENO); + if (!tty || !*tty) + return PAM_SUCCESS; + } + + if (pam_str_skip_prefix(tty, "/dev/") == NULL) { + if (asprintf(&data->tty_path, "%s%s", "/dev/", tty) < 0) + data->tty_path = NULL; + } else { + data->tty_path = strdup(tty); + } + + if (!data->tty_path) { + pam_syslog(pamh, LOG_CRIT, "Out of memory"); + return PAM_BUF_ERR; + } + + if (getfilecon(data->tty_path, &data->prev_tty_context) < 0) { + data->prev_tty_context = NULL; + if (errno == ENOENT) { + free(data->tty_path); + data->tty_path = NULL; + return PAM_SUCCESS; + } + pam_syslog(pamh, LOG_ERR, "Failed to get current context for %s: %m", + data->tty_path); + return (security_getenforce() != 0) ? PAM_SESSION_ERR : PAM_SUCCESS; + } + + tclass = string_to_security_class("chr_file"); + if (tclass == 0) { + pam_syslog(pamh, LOG_ERR, "Failed to get chr_file security class"); + freecon(data->prev_tty_context); + data->prev_tty_context = NULL; + free(data->tty_path); + data->tty_path = NULL; + return (security_getenforce() != 0) ? PAM_SESSION_ERR : PAM_SUCCESS; + } + + if (security_compute_relabel(data->exec_context, data->prev_tty_context, + tclass, &data->tty_context)) { + data->tty_context = NULL; + pam_syslog(pamh, LOG_ERR, "Failed to compute new context for %s: %m", + data->tty_path); + freecon(data->prev_tty_context); + data->prev_tty_context = NULL; + free(data->tty_path); + data->tty_path = NULL; + return (security_getenforce() != 0) ? PAM_SESSION_ERR : PAM_SUCCESS; + } + + return PAM_SUCCESS; +} + +static int +restore_context(const pam_handle_t *pamh, const module_data_t *data, int debug) +{ + int err; + + if (!data) { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "No context to restore"); + return PAM_SUCCESS; + } + + if (debug && data->tty_path) + pam_syslog(pamh, LOG_NOTICE, + "Restore file context of tty %s: [%s] -> [%s]", + data->tty_path, + data->tty_context ? data->tty_context : "", + data->prev_tty_context ? data->prev_tty_context : ""); + err = set_file_context(pamh, data->prev_tty_context, data->tty_path); + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Restore executable context: [%s] -> [%s]", + data->exec_context, + data->prev_exec_context ? data->prev_exec_context : ""); + err |= set_exec_context(pamh, data->prev_exec_context); + + if (err && security_getenforce() != 0) + return PAM_SESSION_ERR; + + return PAM_SUCCESS; +} + +static int +set_context(pam_handle_t *pamh, const module_data_t *data, + int debug, int verbose) +{ + int rc, err; + + if (debug && data->tty_path) + pam_syslog(pamh, LOG_NOTICE, "Set file context of tty %s: [%s] -> [%s]", + data->tty_path, + data->prev_tty_context ? data->prev_tty_context : "", + data->tty_context ? data->tty_context : ""); + err = set_file_context(pamh, data->tty_context, data->tty_path); + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Set executable context: [%s] -> [%s]", + data->prev_exec_context ? data->prev_exec_context : "", + data->exec_context); + rc = set_exec_context(pamh, data->exec_context); + err |= rc; + + send_audit_message(pamh, !rc, data->default_user_context, data->exec_context); + if (verbose && !rc) { + char msg[PATH_MAX]; + + snprintf(msg, sizeof(msg), + _("Security context %s has been assigned."), data->exec_context); + send_text(pamh, msg, debug); + } +#ifdef HAVE_SETKEYCREATECON + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Set key creation context to %s", + data->exec_context ? data->exec_context : ""); + rc = setkeycreatecon(data->exec_context); + err |= rc; + if (rc) + pam_syslog(pamh, LOG_ERR, "Setting key creation context %s failed: %m", + data->exec_context ? data->exec_context : ""); + if (verbose && !rc) { + char msg[PATH_MAX]; + + snprintf(msg, sizeof(msg), + _("Key creation context %s has been assigned."), data->exec_context); + send_text(pamh, msg, debug); + } +#endif + + if (err && security_getenforce() != 0) + return PAM_SESSION_ERR; + + return PAM_SUCCESS; +} + +static int +create_context(pam_handle_t *pamh, int argc, const char **argv, + int debug, int verbose) +{ + int i; + int ttys = 1; + int select_context = 0; + int use_current_range = 0; + int env_params = 0; + module_data_t *data; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "nottys") == 0) { + ttys = 0; + } + if (strcmp(argv[i], "select_context") == 0) { + select_context = 1; + } + if (strcmp(argv[i], "use_current_range") == 0) { + use_current_range = 1; + } + if (strcmp(argv[i], "env_params") == 0) { + env_params = 1; + } + } + + if (is_selinux_enabled() <= 0) { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "SELinux is not enabled"); + return PAM_SUCCESS; + } + + if (select_context && env_params) { + pam_syslog(pamh, LOG_ERR, + "select_context cannot be used with env_params"); + select_context = 0; + } + + if (!(data = calloc(1, sizeof(*data)))) { + pam_syslog(pamh, LOG_CRIT, "Out of memory"); + return PAM_BUF_ERR; + } + + i = compute_exec_context(pamh, data, select_context, use_current_range, + env_params, debug); + if (i != PAM_SUCCESS) { + free_module_data(data); + return i; + } + + if (!data->exec_context) { + free_module_data(data); + return (security_getenforce() != 0) ? PAM_SESSION_ERR : PAM_SUCCESS; + } + + if (ttys && (i = compute_tty_context(pamh, data)) != PAM_SUCCESS) { + free_module_data(data); + return i; + } + + if ((i = pam_set_data(pamh, DATANAME, data, cleanup)) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "Error saving context: %m"); + free_module_data(data); + return i; + } + + return set_context(pamh, data, debug, verbose); +} + +int +pam_sm_authenticate(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + /* Fail by default. */ + return PAM_AUTH_ERR; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + const module_data_t *data; + int i, debug = 0, verbose = 0, close_session = 0, restore = 0; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } + if (strcmp(argv[i], "verbose") == 0) { + verbose = 1; + } + if (strcmp(argv[i], "close") == 0) { + close_session = 1; + } + if (strcmp(argv[i], "restore") == 0) { + restore = 1; + } + } + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Open Session"); + + /* Is this module supposed to execute close_session only? */ + if (close_session) + return PAM_SUCCESS; + + data = get_module_data(pamh); + + /* Is this module supposed only to restore original context? */ + if (restore) + return restore_context(pamh, data, debug); + + /* If there is a saved context, this module is supposed to set it again. */ + return data ? set_context(pamh, data, debug, verbose) : + create_context(pamh, argc, argv, debug, verbose); +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int i, debug = 0, open_session = 0; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } + if (strcmp(argv[i], "open") == 0) { + open_session = 1; + } + } + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Close Session"); + + /* Is this module supposed to execute open_session only? */ + if (open_session) + return PAM_SUCCESS; + + /* Restore original context. */ + return restore_context(pamh, get_module_data(pamh), debug); +} diff --git a/modules/pam_selinux/pam_selinux_check.8 b/modules/pam_selinux/pam_selinux_check.8 new file mode 100644 index 0000000..34f578d --- /dev/null +++ b/modules/pam_selinux/pam_selinux_check.8 @@ -0,0 +1,35 @@ +.TH pam_selinux_check 8 2002/05/23 "Red Hat Linux" "System Administrator's Manual" +.SH NAME +pam_selinux_check \- login program to test pam_selinux.so +.SH SYNOPSIS +.B pam_selinux_check [user] +.br + +.SH DESCRIPTION +With no arguments, +.B pam_selinux_check +will prompt for user + +.SH OPTIONS +.IP target_user +The user to login as. + +.SH DIAGNOSTICS +You must setup a /etc/pam.d/pam_selinux_check file, in order for the check to work. + +When checking if a selinux is valid, +.B pam_selinux_check +returns an exit code of 0 for success and > 0 on error: + +.nf +1: Authentication failure +.fi + +.SH SEE ALSO +pam_selinux(8) + +.SH BUGS +Let's hope not, but if you find any, please email the author. + +.SH AUTHOR +Dan Walsh diff --git a/modules/pam_selinux/pam_selinux_check.c b/modules/pam_selinux/pam_selinux_check.c new file mode 100644 index 0000000..753aa88 --- /dev/null +++ b/modules/pam_selinux/pam_selinux_check.c @@ -0,0 +1,163 @@ +/****************************************************************************** + * A module for Linux-PAM that will set the default security context after login + * via PAM. + * + * Copyright (c) 2003 Red Hat, Inc. + * Written by Dan Walsh + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/************************************************************************ + * + * All PAM code goes in this section. + * + ************************************************************************/ + +#include "config.h" + +#include +#include +#include /* for getuid(), exit(), getopt() */ +#include +#include /* for wait() */ + +#include /* for PAM functions */ +#include /* for misc_conv PAM utility function */ + +#include "pam_i18n.h" + +#define SERVICE_NAME "pam_selinux_check" /* the name of this program for PAM */ + /* The file containing the context to run + * the scripts under. */ +int authenticate_via_pam( const char *user , pam_handle_t **pamh); + +/* authenticate_via_pam() + * + * in: user + * out: nothing + * return: value condition + * ----- --------- + * 1 pam thinks that the user authenticated themselves properly + * 0 otherwise + * + * this function uses pam to authenticate the user running this + * program. this is the only function in this program that makes pam + * calls. + * + */ + +int authenticate_via_pam( const char *user , pam_handle_t **pamh) { + + struct pam_conv *conv; + int result = 0; /* our result, set to 0 (not authenticated) by default */ + + /* this is a jump table of functions for pam to use when it wants to * + * communicate with the user. we'll be using misc_conv(), which is * + * provided for us via pam_misc.h. */ + struct pam_conv pam_conversation = { + misc_conv, + NULL + }; + conv = &pam_conversation; + + + /* make `p_pam_handle' a valid pam handle so we can use it when * + * calling pam functions. */ + if( PAM_SUCCESS != pam_start( SERVICE_NAME, + user, + conv, + pamh ) ) { + fprintf( stderr, _("failed to initialize PAM\n") ); + exit( -1 ); + } + + if( PAM_SUCCESS != pam_set_item(*pamh, PAM_RUSER, user)) + { + fprintf( stderr, _("failed to pam_set_item()\n") ); + exit( -1 ); + } + + /* Ask PAM to authenticate the user running this program */ + if( PAM_SUCCESS == pam_authenticate(*pamh,0) ) { + if ( PAM_SUCCESS == pam_open_session(*pamh, 0) ) + result = 1; /* user authenticated OK! */ + } + return( result ); + +} /* authenticate_via_pam() */ + +int +main (int argc, char **argv) +{ + pam_handle_t *pamh; + int childPid; + + if (argc < 1) + exit (-1); + + if (!authenticate_via_pam(argv[1],&pamh)) + exit(-1); + + childPid = fork(); + if (childPid < 0) { + /* error in fork() */ + fprintf(stderr, _("login: failure forking: %m")); + pam_close_session(pamh, 0); + /* We're done with PAM. Free `pam_handle'. */ + pam_end( pamh, PAM_SUCCESS ); + exit(0); + } + if (childPid) { + close(0); close(1); close(2); + struct sigaction sa; + memset(&sa,0,sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + while(wait(NULL) == -1 && errno == EINTR) /**/ ; + openlog("login", LOG_ODELAY, LOG_AUTHPRIV); + pam_close_session(pamh, 0); + /* We're done with PAM. Free `pam_handle'. */ + pam_end( pamh, PAM_SUCCESS ); + exit(0); + } + argv[0]=strdup ("/bin/sh"); + argv[1]=NULL; + + /* NOTE: The environment has not been sanitized. LD_PRELOAD and other fun + * things could be set. */ + execv("/bin/sh",argv); + fprintf(stderr,"Failure\n"); + return 0; +} diff --git a/modules/pam_selinux/tst-pam_selinux b/modules/pam_selinux/tst-pam_selinux new file mode 100755 index 0000000..14c3d82 --- /dev/null +++ b/modules/pam_selinux/tst-pam_selinux @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_selinux.so diff --git a/modules/pam_sepermit/README.xml b/modules/pam_sepermit/README.xml new file mode 100644 index 0000000..a8d31d8 --- /dev/null +++ b/modules/pam_sepermit/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_sepermit.8.xml" xpointer='xpointer(id("pam_sepermit-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_sepermit/meson.build b/modules/pam_sepermit/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_sepermit/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_sepermit/pam_sepermit.8.xml b/modules/pam_sepermit/pam_sepermit.8.xml new file mode 100644 index 0000000..114864a --- /dev/null +++ b/modules/pam_sepermit/pam_sepermit.8.xml @@ -0,0 +1,195 @@ + + + + pam_sepermit + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_sepermit + PAM module to allow/deny login depending on SELinux enforcement state + + + + + pam_sepermit.so + + debug + + + conf=/path/to/config/file + + + + + + DESCRIPTION + + The pam_sepermit module allows or denies login depending on SELinux + enforcement state. + + + When the users which are logging in match an entry in the config file + they are allowed access only when SELinux is in enforcing mode. Otherwise + they are denied access. For users not matching any entry in the config file + the pam_sepermit module returns PAM_IGNORE return value. + + + The config file contains a list of user names one per line with optional arguments. If the + name is prefixed with @ character it means that all + users in the group name match. If it is prefixed + with a % character the SELinux user is used to match against the name + instead of the account name. Note that when SELinux is disabled the + SELinux user assigned to the account cannot be determined. This means that + such entries are never matched when SELinux is disabled and pam_sepermit + will return PAM_IGNORE. + + + See + sepermit.conf5 + for details. + + + If there is no explicitly specified configuration file and + /etc/security/sepermit.conf does not exist, + %vendordir%/security/sepermit.conf is used. + + + + + OPTIONS + + + + debug + + + + Turns on debugging via + + syslog3 + . + + + + + + conf=/path/to/config/file + + + + Path to alternative config file overriding the default. + + + + + + + + MODULE TYPES PROVIDED + + The and + module types are provided. + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + SELinux is disabled or in the permissive mode and the user + matches. + + + + + PAM_SUCCESS + + + SELinux is in the enforcing mode and the user matches. + + + + + PAM_IGNORE + + + The user does not match any entry in the config file. + + + + + PAM_USER_UNKNOWN + + + The module was unable to determine the user's name. + + + + + PAM_SERVICE_ERR + + + Error during reading or parsing the config file. + + + + + + + + FILES + + + /etc/security/sepermit.conf + + Default configuration file + + + + + + + EXAMPLES + +auth [success=done ignore=ignore default=bad] pam_sepermit.so +auth required pam_unix.so +account required pam_unix.so +session required pam_permit.so + + + + + SEE ALSO + + + sepermit.conf5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + selinux8 + + + + + + AUTHOR + + pam_sepermit and this manual page were written by Tomas Mraz <tmraz@redhat.com>. + + + + diff --git a/modules/pam_sepermit/pam_sepermit.c b/modules/pam_sepermit/pam_sepermit.c new file mode 100644 index 0000000..b9ed945 --- /dev/null +++ b/modules/pam_sepermit/pam_sepermit.c @@ -0,0 +1,473 @@ +/****************************************************************************** + * A module for Linux-PAM that allows/denies access based on SELinux state. + * + * Copyright (c) 2007, 2008, 2009 Red Hat, Inc. + * Originally written by Tomas Mraz + * Contributions by Dan Walsh + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "pam_inline.h" + +#define SEPERMIT_CONF_FILE (SCONFIG_DIR "/sepermit.conf") +#ifdef VENDOR_SCONFIG_DIR +# define SEPERMIT_VENDOR_CONF_FILE (VENDOR_SCONFIG_DIR "/sepermit.conf"); +#endif +#define MODULE "pam_sepermit" +#define OPT_DELIM ":" + +struct lockfd { + uid_t uid; + int fd; + int debug; +}; + +#define PROC_BASE "/proc" +#define MAX_NAMES (int)(sizeof(unsigned long)*8) + +static int +match_process_uid(pid_t pid, uid_t uid) +{ + char *buf; + size_t n; + uid_t puid; + FILE *f; + int re = 0; + + if (asprintf (&buf, PROC_BASE "/%d/status", pid) < 0) + return 0; + n = strlen(buf) + 1; + if (!(f = fopen (buf, "r"))) { + free(buf); + return 0; + } + + while (getline(&buf, &n, f) != -1) { + if (sscanf (buf, "Uid:\t%d", &puid)) { + re = uid == puid; + break; + } + } + free(buf); + fclose(f); + return re; +} + +static int +check_running (pam_handle_t *pamh, uid_t uid, int killall, int debug) +{ + DIR *dir; + struct dirent *de; + pid_t *pid_table, pid, self; + int i; + int pids, max_pids; + int running = 0; + self = getpid(); + if (!(dir = opendir(PROC_BASE))) { + pam_syslog(pamh, LOG_ERR, "Failed to open proc directory file %s:", PROC_BASE); + return -1; + } + max_pids = 256; + pid_table = malloc(max_pids * sizeof (pid_t)); + if (!pid_table) { + (void)closedir(dir); + pam_syslog(pamh, LOG_CRIT, "Memory allocation error"); + return -1; + } + pids = 0; + while ((de = readdir (dir)) != NULL) { + if (!(pid = (pid_t)atoi(de->d_name)) || pid == self) + continue; + + if (pids == max_pids) { + pid_t *npt; + + if (!(npt = realloc(pid_table, 2*pids*sizeof(pid_t)))) { + free(pid_table); + (void)closedir(dir); + pam_syslog(pamh, LOG_CRIT, "Memory allocation error"); + return -1; + } + pid_table = npt; + max_pids *= 2; + } + pid_table[pids++] = pid; + } + + (void)closedir(dir); + + for (i = 0; i < pids; i++) { + pid_t id; + + if (match_process_uid(pid_table[i], uid) == 0) + continue; + id = pid_table[i]; + + if (killall) { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Attempting to kill %d", id); + kill(id, SIGKILL); + } + running++; + } + + free(pid_table); + return running; +} + +/* + * This function reads the loginuid from the /proc system. It returns + * (uid_t)-1 on failure. + */ +static uid_t get_loginuid(pam_handle_t *pamh) +{ + int fd, count; + char loginuid[24]; + char *eptr; + uid_t rv = (uid_t)-1; + + fd = open("/proc/self/loginuid", O_NOFOLLOW|O_RDONLY); + if (fd < 0) { + if (errno != ENOENT) { + pam_syslog(pamh, LOG_ERR, + "Cannot open /proc/self/loginuid: %m"); + } + return rv; + } + if ((count = pam_modutil_read(fd, loginuid, sizeof(loginuid)-1)) < 1) { + close(fd); + return rv; + } + loginuid[count] = '\0'; + close(fd); + + errno = 0; + rv = strtoul(loginuid, &eptr, 10); + if (errno != 0 || eptr == loginuid) + rv = (uid_t) -1; + + return rv; +} + +static void +sepermit_unlock(pam_handle_t *pamh, void *plockfd, int error_status UNUSED) +{ + struct lockfd *lockfd = plockfd; + struct flock fl; + + memset(&fl, 0, sizeof(fl)); + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + + if (lockfd->debug) + pam_syslog(pamh, LOG_ERR, "Unlocking fd: %d uid: %d", lockfd->fd, lockfd->uid); + + /* Don't kill uid==0 */ + if (lockfd->uid) + /* This is a DOS but it prevents an app from forking to prevent killing */ + while(check_running(pamh, lockfd->uid, 1, lockfd->debug) > 0) + continue; + + (void)fcntl(lockfd->fd, F_SETLK, &fl); + (void)close(lockfd->fd); + free(lockfd); +} + +static int +sepermit_lock(pam_handle_t *pamh, const char *user, int debug) +{ + char buf[PATH_MAX]; + struct flock fl; + + memset(&fl, 0, sizeof(fl)); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + + struct passwd *pw = pam_modutil_getpwnam( pamh, user ); + if (!pw) { + pam_syslog(pamh, LOG_NOTICE, "Unable to find uid for user %s", + user); + return -1; + } + if (check_running(pamh, pw->pw_uid, 0, debug) > 0) { + pam_syslog(pamh, LOG_ERR, "User %s processes are running. Exclusive login not allowed", user); + return -1; + } + + snprintf(buf, sizeof(buf), "%s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid); + int fd = open(buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) { + pam_syslog(pamh, LOG_ERR, "Unable to open lock file %s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid); + return -1; + } + + /* Need to close on exec */ + fcntl(fd, F_SETFD, FD_CLOEXEC); + + if (fcntl(fd, F_SETLK, &fl) == -1) { + pam_syslog(pamh, LOG_ERR, "User %s with exclusive login already logged in", user); + close(fd); + return -1; + } + struct lockfd *lockfd=calloc(1, sizeof(struct lockfd)); + if (!lockfd) { + close(fd); + pam_syslog(pamh, LOG_CRIT, "Memory allocation error"); + return -1; + } + lockfd->uid = pw->pw_uid; + lockfd->debug = debug; + lockfd->fd=fd; + pam_set_data(pamh, MODULE, lockfd, sepermit_unlock); + return 0; +} + +/* return 0 when matched, -1 when unmatched, pam error otherwise */ +static int +sepermit_match(pam_handle_t *pamh, const char *cfgfile, const char *user, + const char *seuser, int debug, int *sense) +{ + FILE *f; + char *line = NULL; + char *start; + size_t len = 0; + int matched = 0; + int exclusive = 0; + int ignore = 0; + + f = fopen(cfgfile, "r"); + + if (!f) { + pam_syslog(pamh, LOG_ERR, "Failed to open config file %s: %m", cfgfile); + return PAM_SERVICE_ERR; + } + + while (!matched && getline(&line, &len, f) != -1) { + size_t n; + char *sptr; + char *opt; + + if (line[0] == '#') + continue; + + start = line; + while (isspace((unsigned char)*start)) + ++start; + n = strlen(start); + while (n > 0 && isspace((unsigned char)start[n-1])) { + --n; + } + if (n == 0) + continue; + + start[n] = '\0'; + start = strtok_r(start, OPT_DELIM, &sptr); + + switch (start[0]) { + case '@': + ++start; + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Matching user %s against group %s", user, start); + if (pam_modutil_user_in_group_nam_nam(pamh, user, start)) { + matched = 1; + } + break; + case '%': + if (seuser == NULL) + break; + ++start; + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Matching seuser %s against seuser %s", seuser, start); + if (strcmp(seuser, start) == 0) { + matched = 1; + } + break; + default: + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Matching user %s against user %s", user, start); + if (strcmp(user, start) == 0) { + matched = 1; + } + } + if (matched) + while ((opt=strtok_r(NULL, OPT_DELIM, &sptr)) != NULL) { + if (strcmp(opt, "exclusive") == 0) + exclusive = 1; + else if (strcmp(opt, "ignore") == 0) + ignore = 1; + else if (debug) { + pam_syslog(pamh, LOG_NOTICE, "Unknown user option: %s", opt); + } + } + } + + free(line); + fclose(f); + if (matched) { + if (*sense == PAM_SUCCESS) { + if (ignore) + *sense = PAM_IGNORE; + if (geteuid() == 0 && exclusive && get_loginuid(pamh) == (uid_t)-1) + if (sepermit_lock(pamh, user, debug) < 0) + *sense = PAM_AUTH_ERR; + } + return 0; + } + else + return -1; +} + +static int +pam_sepermit(pam_handle_t *pamh, int argc, const char **argv) +{ + int i; + int rv; + int debug = 0; + int sense = PAM_AUTH_ERR; + const char *user = NULL; + char *seuser = NULL; + char *level = NULL; + const char *cfgfile = NULL; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + const char *str; + + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } else if ((str = pam_str_skip_prefix(argv[i], "conf=")) != NULL) { + cfgfile = str; + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", argv[i]); + } + } + + if (cfgfile == NULL) { +#ifdef SEPERMIT_VENDOR_CONF_FILE + struct stat buffer; + + cfgfile = SEPERMIT_CONF_FILE; + if (stat(cfgfile, &buffer) != 0 && errno == ENOENT) + cfgfile = SEPERMIT_VENDOR_CONF_FILE; +#else + cfgfile = SEPERMIT_CONF_FILE; +#endif + } + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Parsing config file: %s", cfgfile); + + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || *user == '\0') { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name"); + return PAM_USER_UNKNOWN; + } + + if (is_selinux_enabled() > 0) { + if (security_getenforce() == 1) { + if (debug) + pam_syslog(pamh, LOG_NOTICE, "Enforcing mode, access will be allowed on match"); + sense = PAM_SUCCESS; + } + } + + if (getseuserbyname(user, &seuser, &level) != 0) { + seuser = NULL; + level = NULL; + pam_syslog(pamh, LOG_ERR, "getseuserbyname failed: %m"); + } + + if (debug && sense != PAM_SUCCESS) + pam_syslog(pamh, LOG_NOTICE, "Access will not be allowed on match"); + + rv = sepermit_match(pamh, cfgfile, user, seuser, debug, &sense); + + if (debug) + pam_syslog(pamh, LOG_NOTICE, "sepermit_match returned: %d", rv); + + free(seuser); + free(level); + + switch (rv) { + case -1: + return PAM_IGNORE; + case 0: + return sense; + } + + return rv; +} + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_sepermit(pamh, argc, argv); +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_sepermit(pamh, argc, argv); +} diff --git a/modules/pam_sepermit/sepermit.conf b/modules/pam_sepermit/sepermit.conf new file mode 100644 index 0000000..0a12cd8 --- /dev/null +++ b/modules/pam_sepermit/sepermit.conf @@ -0,0 +1,11 @@ +# /etc/security/sepermit.conf +# +# Each line contains either: +# - a user name +# - a group name, with @group syntax +# - a SELinux user name, with %seuser syntax +# Each line can contain optional arguments separated by : +# The possible arguments are: +# - exclusive - only single login session will +# be allowed for the user and the user's processes +# will be killed on logout diff --git a/modules/pam_sepermit/sepermit.conf.5.xml b/modules/pam_sepermit/sepermit.conf.5.xml new file mode 100644 index 0000000..ff924ce --- /dev/null +++ b/modules/pam_sepermit/sepermit.conf.5.xml @@ -0,0 +1,107 @@ + + + + sepermit.conf + 5 + Linux-PAM + Linux-PAM Manual + + + + sepermit.conf + configuration file for the pam_sepermit module + + + + DESCRIPTION + + The lines of the configuration file have the following syntax: + + + <user>[:<option>:<option>...] + + + The user can be specified in the following manner: + + + + + a username + + + + + a groupname, with @group syntax. + This should not be confused with netgroups. + + + + + a SELinux user name with %seuser syntax. + + + + + + The recognized options are: + + + + + exclusive + + + Only single login session will be allowed for the user + and the user's processes will be killed on logout. + + + + + ignore + + + The module will never return PAM_SUCCESS status for the user. + It will return PAM_IGNORE if SELinux is in the enforcing mode, + and PAM_AUTH_ERR otherwise. It is useful if you want to support + passwordless guest users and other confined users with passwords + simultaneously. + + + + + + + The lines which start with # character are comments and are ignored. + + + + + EXAMPLES + + These are some example lines which might be specified in + /etc/security/sepermit.conf. + + +%guest_u:exclusive +%staff_u:ignore +%user_u:ignore + + + + + SEE ALSO + + pam_sepermit8, + pam.d5, + pam8, + selinux8, + + + + + AUTHOR + + pam_sepermit and this manual page were written by Tomas Mraz <tmraz@redhat.com> + + + \ No newline at end of file diff --git a/modules/pam_sepermit/tst-pam_sepermit b/modules/pam_sepermit/tst-pam_sepermit new file mode 100755 index 0000000..6e6d236 --- /dev/null +++ b/modules/pam_sepermit/tst-pam_sepermit @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_sepermit.so diff --git a/modules/pam_sepermit/tst-pam_sepermit-retval.c b/modules/pam_sepermit/tst-pam_sepermit-retval.c new file mode 100644 index 0000000..4132f28 --- /dev/null +++ b/modules/pam_sepermit/tst-pam_sepermit-retval.c @@ -0,0 +1,158 @@ +/* + * Check pam_sepermit return values and conf= option. + * + * Copyright (c) 2020-2022 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_sepermit" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char missing_file[] = TEST_NAME ".missing"; +static const char config_file[] = TEST_NAME ".conf"; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + /* PAM_USER_UNKNOWN */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, + fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_USER_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_USER_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_NE(NULL, fp = fopen(config_file, "w")); + ASSERT_LT(0, fprintf(fp, "nosuchuser:ignore\n")); + ASSERT_EQ(0, fclose(fp)); + + /* + * conf= specifies an existing file, + * PAM_IGNORE -> PAM_PERM_DENIED + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, + fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so conf=%s\n" + "account required %s/" LTDIR "%s.so conf=%s\n" + "password required %s/" LTDIR "%s.so conf=%s\n" + "session required %s/" LTDIR "%s.so conf=%s\n", + cwd, MODULE_NAME, config_file, + cwd, MODULE_NAME, config_file, + cwd, MODULE_NAME, config_file, + cwd, MODULE_NAME, config_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "root", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* + * conf= specifies an existing file, + * PAM_IGNORE -> PAM_SUCCESS + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, + fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so conf=%s\n" + "auth required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "account required %s/" LTDIR "%s.so conf=%s\n" + "account required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "password required %s/" LTDIR "%s.so conf=%s\n" + "password required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "session required %s/" LTDIR "%s.so conf=%s\n" + "session required %s/../pam_permit/" LTDIR "pam_permit.so\n", + cwd, MODULE_NAME, config_file, cwd, + cwd, MODULE_NAME, config_file, cwd, + cwd, MODULE_NAME, config_file, cwd, + cwd, MODULE_NAME, config_file, cwd)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "root", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* + * conf= specifies a missing file, + * PAM_IGNORE -> PAM_PERM_DENIED + */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, + fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so conf=%s\n" + "account required %s/" LTDIR "%s.so conf=%s\n" + "password required %s/" LTDIR "%s.so conf=%s\n" + "session required %s/" LTDIR "%s.so conf=%s\n", + cwd, MODULE_NAME, missing_file, + cwd, MODULE_NAME, missing_file, + cwd, MODULE_NAME, missing_file, + cwd, MODULE_NAME, missing_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "root", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* cleanup */ + ASSERT_EQ(0, unlink(config_file)); + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_setquota/README.xml b/modules/pam_setquota/README.xml new file mode 100644 index 0000000..7f5e429 --- /dev/null +++ b/modules/pam_setquota/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_setquota.8.xml" xpointer='xpointer(id("pam_setquota-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_setquota/meson.build b/modules/pam_setquota/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_setquota/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_setquota/pam_setquota.8.xml b/modules/pam_setquota/pam_setquota.8.xml new file mode 100644 index 0000000..41644ee --- /dev/null +++ b/modules/pam_setquota/pam_setquota.8.xml @@ -0,0 +1,299 @@ + + + + pam_setquota + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_setquota + PAM module to set or modify disk quotas on session start + + + + + pam_setquota.so + + fs=/home + + + overwrite=0 + + + debug=0 + + + startuid=1000 + + + enduid=0 + + + bsoftlimit=19000 + + + bhardlimit=20000 + + + isoftlimit=3000 + + + ihardlimit=4000 + + + + + + + DESCRIPTION + + + pam_setquota is a PAM module to set or modify a disk quota at session start + + + This makes quotas usable with central user databases, such as MySQL or LDAP. + + + + + + + OPTIONS + + + + + fs=/home + + + + The device file or mountpoint the policy applies to. Defaults to the + filesystem containing the users home directory. + + + + + + overwrite=0 + + + + Overwrite an existing quota. Note: Enabling this will remove the ability + for the admin to manually configure different quotas for users for a + filesystem with edquota + 8. (Defaults to 0) + + + + + + debug=0 + + + + Enable debugging. A value of 1 outputs the old and new quota on a device. + A value of 2 also prints out the matched and found filesystems should + be unset. (Defaults to 0) + + + + + + startuid=1000 + + + + Describe the start of the UID range the policy is applied to. + (Defaults to UID_MIN from login.defs or the uidmin value defined + at compile-time if UID_MIN is undefined.) + + + + + + enduid=0 + + + + Describe the end of the UID range the policy is applied to. Setting + enduid=0 results in an open-ended UID + range (i.e. all uids greater than are + included). (Defaults to 0) + + + + + + bsoftlimit=19000 + + + + Soft limit for disk quota blocks, as defined by + quotactl2 + . + Note: and + must be set at the same time! + + + + + + bhardlimit=20000 + + + + Hard limit for disk quota blocks, as defined by + quotactl2 + . + Note: and + must be set at the same time! + + + + + + isoftlimit=3000 + + + + Soft limit for inodes, as defined by + quotactl2. + Note: and + must be set at the same time! + + + + + + ihardlimit=4000 + + + + Hard limit for inodes, as defined by + quotactl2. + Note: and + must be set at the same time! + + + + + + + + + MODULE TYPES PROVIDED + + Only the module type is provided. + + + + + RETURN VALUES + + + + + PAM_SUCCESS + + + The quota was set successfully. + + + + + + PAM_IGNORE + + + No action was taken because either the UID of the user was outside + of the specified range, a quota already existed and + was not configured or no limits were + configured at all. + + + + + + PAM_USER_UNKNOWN + + + The user was not found. + + + + + + PAM_PERM_DENIED + + + /proc/mounts could not be opened. + + + The filesystem or device specified was not found. + + + The limits for the user could not be retrieved. See syslog for + more information. + + + The limits for the user could not be set. See syslog for + more information. + + + Either / + or / were + not set at the same time. + + + + + + + + + + EXAMPLES + + A single invocation of `pam_setquota` applies a specific policy to a UID + range. Applying different policies to specific UID ranges is done by + invoking pam_setquota more than once. The last matching entry + defines the resulting quota. + + session required pam_setquota.so bsoftlimit=1000 bhardlimit=2000 isoftlimit=1000 ihardlimit=2000 startuid=1000 enduid=0 fs=/home + session required pam_setquota.so bsoftlimit=19000 bhardlimit=20000 isoftlimit=3000 ihardlimit=4000 startuid=2001 enduid=3000 fs=/dev/sda1 + session required pam_setquota.so bsoftlimit=19000 bhardlimit=20000 isoftlimit=3000 ihardlimit=4000 startuid=3001 enduid=4000 fs=/dev/sda1 overwrite=1 + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_setquota was originally written by + Ruslan Savchenko <savrus@mexmat.net>. + + + Further modifications were made by Shane Tzen <shane@ict.usc.edu>, + Sven Hartge <sven@svenhartge.de> + and Keller Fuchs <kellerfuchs@hashbang.sh>. + + + + \ No newline at end of file diff --git a/modules/pam_setquota/pam_setquota.c b/modules/pam_setquota/pam_setquota.c new file mode 100644 index 0000000..73445e2 --- /dev/null +++ b/modules/pam_setquota/pam_setquota.c @@ -0,0 +1,391 @@ +/* PAM setquota module + + This PAM module sets disk quota when a session begins. + + Copyright © 2006 Ruslan Savchenko + Copyright © 2010 Shane Tzen + Copyright © 2012-2020 Sven Hartge + Copyright © 2016 Keller Fuchs +*/ + +#include "pam_inline.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifndef PATH_LOGIN_DEFS +# define PATH_LOGIN_DEFS "/etc/login.defs" +#endif + +#define MAX_UID_VALUE 0xFFFFFFFFUL + +struct pam_params { + uid_t start_uid; + uid_t end_uid; + const char *fs; + size_t fs_len; + int overwrite; + int debug; +}; + +static inline void +debug(pam_handle_t *pamh, const struct if_dqblk *p, + const char *device, const char *dbgprefix) { + pam_syslog(pamh, LOG_DEBUG, "%s device=%s bsoftlimit=%llu bhardlimit=%llu " + "isoftlimit=%llu ihardlimit=%llu btime=%llu itime=%llu", + dbgprefix, device, + (unsigned long long) p->dqb_bsoftlimit, + (unsigned long long) p->dqb_bhardlimit, + (unsigned long long) p->dqb_isoftlimit, + (unsigned long long) p->dqb_ihardlimit, + (unsigned long long) p->dqb_btime, + (unsigned long long) p->dqb_itime); +} + +static unsigned long long +str_to_dqb_num(pam_handle_t *pamh, const char *str, const char *param) { + char *ep = NULL; + + errno = 0; + long long temp = strtoll(str, &ep, 10); + if (temp < 0 || str == ep || *ep != '\0' || errno !=0) { + pam_syslog(pamh, LOG_ERR, "Parameter \"%s=%s\" invalid, setting to 0", param, str); + return 0; + } + else { + return temp; + } +} + +static bool +parse_dqblk(pam_handle_t *pamh, int argc, const char **argv, struct if_dqblk *p) { + bool bhard = false, bsoft = false, ihard = false, isoft = false; + + /* step through arguments */ + for (; argc-- > 0; ++argv) { + const char *str; + if ((str = pam_str_skip_prefix(*argv, "bhardlimit=")) != NULL) { + p->dqb_bhardlimit = str_to_dqb_num(pamh, str, "bhardlimit"); + p->dqb_valid |= QIF_BLIMITS; + bhard = true; + } else if ((str = pam_str_skip_prefix(*argv, "bsoftlimit=")) != NULL) { + p->dqb_bsoftlimit = str_to_dqb_num(pamh, str, "bsoftlimit"); + p->dqb_valid |= QIF_BLIMITS; + bsoft = true; + } else if ((str = pam_str_skip_prefix(*argv, "ihardlimit=")) != NULL) { + p->dqb_ihardlimit = str_to_dqb_num(pamh, str, "ihardlimit"); + p->dqb_valid |= QIF_ILIMITS; + ihard = true; + } else if ((str = pam_str_skip_prefix(*argv, "isoftlimit=")) != NULL) { + p->dqb_isoftlimit = str_to_dqb_num(pamh, str, "isoftlimit"); + p->dqb_valid |= QIF_ILIMITS; + isoft = true; + } else if ((str = pam_str_skip_prefix(*argv, "btime=")) != NULL) { + p->dqb_btime = str_to_dqb_num(pamh, str, "btime"); + p->dqb_valid |= QIF_BTIME; + } else if ((str = pam_str_skip_prefix(*argv, "itime=")) != NULL) { + p->dqb_itime = str_to_dqb_num(pamh, str, "itime"); + p->dqb_valid |= QIF_ITIME; + } + } + + /* return FALSE if a softlimit or hardlimit has been set + * independently of its counterpart. + */ + return !(bhard ^ bsoft) && !(ihard ^ isoft); +} + +/* inspired by pam_usertype_get_id */ +static uid_t +str_to_uid(pam_handle_t *pamh, const char *value, uid_t default_value, const char *param) { + unsigned long ul; + char *ep; + uid_t uid; + + errno = 0; + ul = strtoul(value, &ep, 10); + if (!(ul >= MAX_UID_VALUE + || (uid_t)ul >= MAX_UID_VALUE + || (errno != 0 && ul == 0) + || value == ep + || *ep != '\0')) { + uid = (uid_t)ul; + } else { + pam_syslog(pamh, LOG_ERR, "Parameter \"%s=%s\" invalid, " + "setting to %u", param, value, default_value); + uid = default_value; + } + + return uid; +} + +static void +parse_params(pam_handle_t *pamh, int argc, const char **argv, struct pam_params *p) { + /* step through arguments */ + for (; argc-- > 0; ++argv) { + const char *str; + char *ep = NULL; + if ((str = pam_str_skip_prefix(*argv, "startuid=")) != NULL) { + p->start_uid = str_to_uid(pamh, str, p->start_uid, "startuid"); + } else if ((str = pam_str_skip_prefix(*argv, "enduid=")) != NULL) { + p->end_uid = str_to_uid(pamh, str, p->end_uid, "enduid"); + } else if ((str = pam_str_skip_prefix(*argv, "fs=")) != NULL) { + p->fs = str; + p->fs_len = strlen(str); + /* Mask the unnecessary '/' from the end of fs parameter */ + if (p->fs_len > 1 && p->fs[p->fs_len - 1] == '/') + --p->fs_len; + } else if ((str = pam_str_skip_prefix(*argv, "overwrite=")) != NULL) { + errno = 0; + p->overwrite = strtol(str, &ep, 10); + if (*ep != '\0' || str == ep || errno !=0 || (p->overwrite < 0)) { + pam_syslog(pamh, LOG_ERR, "Parameter \"overwrite=%s\" invalid, " + "setting to 0", str); + p->overwrite = 0; + } + } else if ((str = pam_str_skip_prefix(*argv, "debug=")) != NULL) { + errno = 0; + p->debug = strtol(str, &ep, 10); + if (*ep != '\0' || str == ep || errno != 0 || (p->debug < 0)) { + pam_syslog(pamh, LOG_ERR, "Parameter \"debug=%s\" invalid, " + "setting to 0", str); + p->debug = 0; + } + } + } +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int retval; + char *val, *mntdevice = NULL; + const void *user; + const struct passwd *pwd; + struct pam_params param = { + .start_uid = PAM_USERTYPE_UIDMIN, + .end_uid = 0, + .fs = NULL }; + struct if_dqblk ndqblk; + FILE *fp; + size_t mnt_len = 0, match_size = 0; +#ifdef HAVE_GETMNTENT_R + char buf[BUFSIZ]; + struct mntent ent; +#endif + const struct mntent *mnt; + const char *service; + + if (pam_get_item(pamh, PAM_SERVICE, (const void **)&service) != PAM_SUCCESS) + service = ""; + + /* Get UID_MIN for default start_uid from login.defs */ + val = pam_modutil_search_key(pamh, PATH_LOGIN_DEFS, "UID_MIN"); + + /* Should UID_MIN be undefined, use current value of param.start_uid + * pre-defined as PAM_USERTYPE_UIDMIN set by configure as safe + * starting UID to avoid setting a quota for root and system + * users if startuid= parameter is absent. + */ + if (val) { + param.start_uid = str_to_uid(pamh, val, param.start_uid, PATH_LOGIN_DEFS":UID_MIN"); + _pam_drop(val); + } + + /* Parse parameter values + * Must come after pam_modutil_search_key so that the current system + * default for UID_MIN is already in p.start_uid to serve as default + * for str_to_uid in case of a parse error. + * */ + parse_params(pamh, argc, argv, ¶m); + + if (param.debug >= 1) + pam_syslog(pamh, LOG_DEBUG, "Config: startuid=%u enduid=%u fs=%s " + "debug=%d overwrite=%d", + param.start_uid, param.end_uid, + param.fs ? param.fs : "(none)", + param.debug, param.overwrite); + + /* Determine the user name so we can get the home directory */ + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') { + pam_syslog(pamh, LOG_NOTICE, "user unknown"); + return PAM_USER_UNKNOWN; + } + + /* Get the password entry */ + pwd = pam_modutil_getpwnam(pamh, user); + if (pwd == NULL) { + pam_syslog(pamh, LOG_NOTICE, "user unknown"); + return PAM_USER_UNKNOWN; + } + + /* Check if we should not set quotas for user */ + if ((pwd->pw_uid < param.start_uid) || + ((param.end_uid >= param.start_uid) && (param.start_uid != 0) && + (pwd->pw_uid > param.end_uid))) + return PAM_SUCCESS; + + /* Find out what device the filesystem is hosted on */ + if ((fp = setmntent("/proc/mounts", "r")) == NULL) { + pam_syslog(pamh, LOG_ERR, "Unable to open /proc/mounts"); + return PAM_PERM_DENIED; + } + + while ( +#ifdef HAVE_GETMNTENT_R + (mnt = getmntent_r(fp, &ent, buf, sizeof(buf))) != NULL +#else + (mnt = getmntent(fp)) != NULL +#endif + ) { + /* If param.fs is not specified use filesystem with users homedir + * as default. + */ + if (param.fs == NULL) { + /* Mask trailing / from mnt->mnt_dir, to get a leading / on the + * remaining suffix returned by pam_str_skip_prefix_len() + */ + for (mnt_len = strlen(mnt->mnt_dir); mnt_len > 0; --mnt_len) + if (mnt->mnt_dir[mnt_len - 1] != '/') + break; + const char *s; + if (param.debug >= 2) + pam_syslog(pamh, LOG_DEBUG, "Trying to match pw_dir=\"%s\" " + "with mnt_dir=\"%s\"", pwd->pw_dir, mnt->mnt_dir); + /* + * (mnt_len > match_size) Only try matching the mnt_dir if its length + * is longer than the last matched length, trying to find the longest + * mnt_dir for a given pwd_dir. + * + * (mnt_len == 0 && mnt->mnt_dir[0] == '/') special-cases the + * root-dir /, which is the only mnt_dir with a trailing '/', which + * got masked earlier. + */ + if ((mnt_len > match_size || (mnt_len == 0 && mnt->mnt_dir[0] == '/')) && + (s = pam_str_skip_prefix_len(pwd->pw_dir, mnt->mnt_dir, mnt_len)) != NULL && + s[0] == '/') { + free(mntdevice); + if ((mntdevice = strdup(mnt->mnt_fsname)) == NULL) { + pam_syslog(pamh, LOG_CRIT, "Memory allocation error"); + endmntent(fp); + return PAM_PERM_DENIED; + } + match_size = mnt_len; + if (param.debug >= 2) + pam_syslog(pamh, LOG_DEBUG, "Found pw_dir=\"%s\" in mnt_dir=\"%s\" " + "with suffix=\"%s\" on device=\"%s\"", pwd->pw_dir, + mnt->mnt_dir, s, mntdevice); + } + /* param.fs has been specified, find exactly matching filesystem */ + } else if ((strncmp(param.fs, mnt->mnt_dir, param.fs_len) == 0 + && mnt->mnt_dir[param.fs_len] == '\0') || + (strncmp(param.fs, mnt->mnt_fsname, param.fs_len) == 0 + && mnt->mnt_fsname[param.fs_len] == '\0' )) { + free(mntdevice); + if ((mntdevice = strdup(mnt->mnt_fsname)) == NULL) { + pam_syslog(pamh, LOG_CRIT, "Memory allocation error"); + endmntent(fp); + return PAM_PERM_DENIED; + } + if (param.debug >= 2) + pam_syslog(pamh, LOG_DEBUG, "Found fs=\"%s\" in mnt_dir=\"%s\" " + "on device=\"%s\"", param.fs, mnt->mnt_dir, mntdevice); + } + } + + endmntent(fp); + + if (mntdevice == NULL) { + pam_syslog(pamh, LOG_ERR, "Filesystem or device not found: %s", param.fs ? param.fs : pwd->pw_dir); + return PAM_PERM_DENIED; + } + + /* Get limits */ + if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), mntdevice, pwd->pw_uid, + (void *)&ndqblk) == -1) { + pam_syslog(pamh, LOG_ERR, "fail to get limits for user %s : %m", + pwd->pw_name); + free(mntdevice); + return PAM_PERM_DENIED; + } + + if (param.debug >= 1) + debug(pamh, &ndqblk, mntdevice, "Quota read:"); + + /* Only overwrite if quotas aren't already set or if overwrite is set */ + if ((ndqblk.dqb_bsoftlimit == 0 && ndqblk.dqb_bhardlimit == 0 && + ndqblk.dqb_isoftlimit == 0 && ndqblk.dqb_ihardlimit == 0) || + param.overwrite == 1) { + + /* Parse new limits + * Exit with an error should only the hard- or softlimit be + * configured but not both. + * This avoids errors, inconsistencies and possible race conditions + * during setquota. + */ + ndqblk.dqb_valid = 0; + if (!parse_dqblk(pamh, argc, argv, &ndqblk)) { + pam_syslog(pamh, LOG_ERR, + "Both soft- and hardlimits for %s need to be configured " + "at the same time!", mntdevice); + free(mntdevice); + return PAM_PERM_DENIED; + } + + /* Nothing changed? Are no limits defined at all in configuration? */ + if (ndqblk.dqb_valid == 0) { + pam_syslog(pamh, LOG_AUTH | LOG_WARNING, "no limits defined in " + "configuration for user %s on %s", pwd->pw_name, mntdevice); + free(mntdevice); + return PAM_IGNORE; + } + + /* Set limits */ + if (quotactl(QCMD(Q_SETQUOTA, USRQUOTA), mntdevice, pwd->pw_uid, + (void *)&ndqblk) == -1) { + pam_syslog(pamh, LOG_ERR, "failed to set limits for user %s on %s: %m", + pwd->pw_name, mntdevice); + free(mntdevice); + return PAM_PERM_DENIED; + } + if (param.debug >= 1) + debug(pamh, &ndqblk, mntdevice, "Quota set:"); + + /* End module */ + free(mntdevice); + return PAM_SUCCESS; + + } else { + /* Quota exists and overwrite!=1 */ + if (param.debug >= 1) { + pam_syslog(pamh, LOG_DEBUG, "Quota already exists for user %s " + "on %s, not overwriting it without \"overwrite=1\"", + pwd->pw_name, mntdevice); + } + /* End module */ + free(mntdevice); + return PAM_IGNORE; + } + +} + +int +pam_sm_close_session(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} diff --git a/modules/pam_setquota/tst-pam_setquota b/modules/pam_setquota/tst-pam_setquota new file mode 100755 index 0000000..f50c958 --- /dev/null +++ b/modules/pam_setquota/tst-pam_setquota @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_setquota.so diff --git a/modules/pam_shells/README.xml b/modules/pam_shells/README.xml new file mode 100644 index 0000000..c4da1a0 --- /dev/null +++ b/modules/pam_shells/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_shells.8.xml" xpointer='xpointer(id("pam_shells-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_shells/meson.build b/modules/pam_shells/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_shells/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_shells/pam_shells.8.xml b/modules/pam_shells/pam_shells.8.xml new file mode 100644 index 0000000..e1b35a3 --- /dev/null +++ b/modules/pam_shells/pam_shells.8.xml @@ -0,0 +1,130 @@ + + + + pam_shells + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_shells + PAM module to check for valid login shell + + + + + pam_shells.so + + + + + + DESCRIPTION + + + pam_shells is a PAM module that only allows access to the + system if the user's shell is listed in /etc/shells. + + + + If this file does not exist, entries are taken from files + %vendordir%/shells, + %vendordir%/shells.d/* and + /etc/shells.d/* in that order. + + + + It also checks if needed files (e.g. /etc/shells) are plain + files and not world writable. + + + + + + OPTIONS + This module does not recognise any options. + + + + MODULE TYPES PROVIDED + + The and + module types are provided. + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + Access to the system was denied. + + + + + PAM_SUCCESS + + + The user's login shell was listed as valid shell in + /etc/shells. + + + + + PAM_USER_UNKNOWN + + + The user does not exist or the user's login shell could not be determined. + + + + + PAM_SERVICE_ERR + + + The module was not able to get the name of the user. + + + + + + + + EXAMPLES + + +auth required pam_shells.so + + + + + + SEE ALSO + + + shells5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_shells was written by Erik Troan <ewt@redhat.com>. + + + + \ No newline at end of file diff --git a/modules/pam_shells/pam_shells.c b/modules/pam_shells/pam_shells.c new file mode 100644 index 0000000..bacf4f5 --- /dev/null +++ b/modules/pam_shells/pam_shells.c @@ -0,0 +1,177 @@ +/* + * pam_shells module + * + * by Erik Troan , Red Hat Software. + * August 5, 1996. + * This code shamelessly ripped from the pam_securetty module. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined (USE_ECONF) && defined (VENDORDIR) +#include "pam_econf.h" +#endif + +#include +#include +#include + +#define SHELL_FILE "/etc/shells" +#define SHELLS "shells" +#define ETCDIR "/etc" +#define DEFAULT_SHELL "/bin/sh" + +static bool check_file(const char *filename, const void *pamh) +{ + struct stat sb; + + if (stat(filename, &sb)) { + pam_syslog(pamh, LOG_ERR, "Cannot stat %s: %m", filename); + return false; /* must have /etc/shells */ + } + + if ((sb.st_mode & S_IWOTH) || !S_ISREG(sb.st_mode)) { + pam_syslog(pamh, LOG_ERR, + "%s is either world writable or not a normal file", + filename); + return false; + } + return true; +} + +static int perform_check(pam_handle_t *pamh) +{ + int retval = PAM_AUTH_ERR; + const char *userName; + const char *userShell; + struct passwd * pw; + + retval = pam_get_user(pamh, &userName, NULL); + if (retval != PAM_SUCCESS) { + return PAM_SERVICE_ERR; + } + + pw = pam_modutil_getpwnam(pamh, userName); + if (pw == NULL) { + return PAM_USER_UNKNOWN; + } + if (pw->pw_shell == NULL) { + /* TODO: when does this happen? I would join it with + * the case userShell[0] == '\0' below. + * + * For now, keep the existing stricter behaviour + */ + return PAM_AUTH_ERR; + } + userShell = pw->pw_shell; + if (userShell[0] == '\0') + userShell = DEFAULT_SHELL; + +#if defined (USE_ECONF) && defined (VENDORDIR) + size_t size = 0; + econf_err error; + char **keys; + econf_file *key_file = NULL; + + error = pam_econf_readconfig(&key_file, + VENDORDIR, + ETCDIR, + SHELLS, + NULL, + "", /* key only */ + "#", /* comment */ + check_file, pamh); + if (error != ECONF_SUCCESS) { + pam_syslog(pamh, LOG_ERR, + "Cannot parse shell files: %s", + econf_errString(error)); + return PAM_AUTH_ERR; + } + + error = econf_getKeys(key_file, NULL, &size, &keys); + if (error) { + pam_syslog(pamh, LOG_ERR, + "Cannot evaluate entries in shell files: %s", + econf_errString(error)); + econf_free (key_file); + return PAM_AUTH_ERR; + } + + retval = 1; + for (size_t i = 0; i < size; i++) { + retval = strcmp(keys[i], userShell); + if (!retval) + break; + } + econf_free (keys); + econf_free (key_file); +#else + FILE *shellFile; + char *p = NULL; + size_t n = 0; + + if (!check_file(SHELL_FILE, pamh)) + return PAM_AUTH_ERR; + + shellFile = fopen(SHELL_FILE,"r"); + if (shellFile == NULL) { /* Check that we opened it successfully */ + pam_syslog(pamh, LOG_ERR, "Error opening %s: %m", SHELL_FILE); + return PAM_SERVICE_ERR; + } + + retval = 1; + + while (retval && getline(&p, &n, shellFile) != -1) { + p[strcspn(p, "\n")] = '\0'; + + if (p[0] != '/') { + continue; + } + retval = strcmp(p, userShell); + } + + free(p); + fclose(shellFile); +#endif + + if (retval) { + pam_syslog(pamh, LOG_NOTICE, "User has an invalid shell '%s'", userShell); + return PAM_AUTH_ERR; + } else { + return PAM_SUCCESS; + } +} + +/* --- authentication management functions (only) --- */ + +int pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return perform_check(pamh); +} + +int pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +/* --- account management functions (only) --- */ + +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return perform_check(pamh); +} + +/* end of module definition */ diff --git a/modules/pam_shells/tst-pam_shells b/modules/pam_shells/tst-pam_shells new file mode 100755 index 0000000..dccc33d --- /dev/null +++ b/modules/pam_shells/tst-pam_shells @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_shells.so diff --git a/modules/pam_stress/README.xml b/modules/pam_stress/README.xml new file mode 100644 index 0000000..cc7a184 --- /dev/null +++ b/modules/pam_stress/README.xml @@ -0,0 +1,19 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_stress.8.xml" xpointer='xpointer(id("pam_stress-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_stress/meson.build b/modules/pam_stress/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_stress/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_stress/pam_stress.8.xml b/modules/pam_stress/pam_stress.8.xml new file mode 100644 index 0000000..617b7aa --- /dev/null +++ b/modules/pam_stress/pam_stress.8.xml @@ -0,0 +1,353 @@ + + + + pam_stress + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_stress + The stress-testing PAM module + + + + + + + pam_stress.so + + debug + + + no_warn + + + use_first_pass + + + try_first_pass + + + rootok + + + expired + + + fail_1 + + + fail_2 + + + prelim + + + required + + + + + + DESCRIPTION + + The pam_stress PAM module is mainly intended to give the impression of failing as a fully +functioning module might. + + + + + OPTIONS + + + + + debug + + + + Put lots of information in syslog. + *NOTE* this option writes passwords to syslog, so don't use anything sensitive when testing. + + + + + + + no_warn + + + + Do not give warnings about things (otherwise warnings are issued + via the conversation function) + + + + + + + use_first_pass + + + + Do not prompt for a password, for pam_sm_authentication + function just use item PAM_AUTHTOK. + + + + + + + try_first_pass + + + + Do not prompt for a password unless there has been no + previous authentication token (item PAM_AUTHTOK is NULL) + + + + + + + rootok + + + + This is intended for the pam_sm_chauthtok function and + it instructs this function to permit root to change + the user's password without entering the old password. + + + + + + + expired + + + + An argument intended for the account and chauthtok module + parts. It instructs the module to act as if the user's + password has expired + + + + + + + fail_1 + + + + This instructs the module to make its first function fail. + + + + + + + fail_2 + + + + This instructs the module to make its second function (if there + is one) fail. + + + + + + + prelim + + + + For pam_sm_chauthtok, means fail on PAM_PRELIM_CHECK. + + + + + + + required + + + + For pam_sm_chauthtok, means fail if the user hasn't already + been authenticated by this module. (See stress_new_pwd data + string in the NOTES.) + + + + + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_PERM_DENIED + + + Permission denied. + + + + + PAM_AUTH_ERR + + + Access to the system was denied. + + + + + PAM_CONV_ERR + + + Conversation failure. + + + + + PAM_SUCCESS + + + The function passes all checks. + + + + + PAM_USER_UNKNOWN + + + The user is not known to the system. + + + + + PAM_CRED_ERR + + + Failure involving user credentials. + + + + + PAM_NEW_AUTHTOK_REQD + + + Authentication token is no longer valid; new one required. + + + + + PAM_SESSION_ERR + + + Session failure. + + + + + PAM_TRY_AGAIN + + + Failed preliminary check by service. + + + + + PAM_AUTHTOK_LOCK_BUSY + + + Authentication token lock busy. + + + + + PAM_AUTHTOK_ERR + + + Authentication token manipulation error. + + + + + PAM_SYSTEM_ERR + + + System error. + + + + + + + + NOTES + + This module uses the stress_new_pwd data string which tells + pam_sm_chauthtok that pam_sm_acct_mgmt says we need a new password. + The only possible value for this data string is 'yes'. + + + + + EXAMPLES + +#%PAM-1.0 +# +# Any of the following will suffice +account required pam_stress.so +auth required pam_stress.so +password required pam_stress.so +session required pam_stress.so + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + . + + + + + AUTHORS + + The pam_stress PAM module was developed by + Andrew Morgan <morgan@linux.kernel.org>. + The man page for pam_stress was written by + Lucas Ramage <ramage.lucas@protonmail.com>. + + + \ No newline at end of file diff --git a/modules/pam_stress/pam_stress.c b/modules/pam_stress/pam_stress.c new file mode 100644 index 0000000..d0f5401 --- /dev/null +++ b/modules/pam_stress/pam_stress.c @@ -0,0 +1,533 @@ +/* + * pam_stress module + * + * created by Andrew Morgan 1996/3/12 + */ + +#include "config.h" + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include "pam_inline.h" + +/* ---------- */ + +/* an internal function to turn all possible test arguments into bits + of a ctrl number */ + +/* generic options */ + +#define PAM_ST_DEBUG 01 +#define PAM_ST_NO_WARN 02 +#define PAM_ST_USE_PASS1 04 +#define PAM_ST_TRY_PASS1 010 +#define PAM_ST_ROOTOK 020 + +/* simulation options */ + +#define PAM_ST_EXPIRED 040 +#define PAM_ST_FAIL_1 0100 +#define PAM_ST_FAIL_2 0200 +#define PAM_ST_PRELIM 0400 +#define PAM_ST_REQUIRE_PWD 01000 + +/* some syslogging */ + +static void +_pam_report (const pam_handle_t *pamh, int ctrl, const char *name, + int flags, int argc, const char **argv) +{ + if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "CALLED: %s", name); + pam_syslog(pamh, LOG_DEBUG, "FLAGS : 0%o%s", + flags, (flags & PAM_SILENT) ? " (silent)":""); + pam_syslog(pamh, LOG_DEBUG, "CTRL = 0%o", ctrl); + pam_syslog(pamh, LOG_DEBUG, "ARGV :"); + while (argc--) { + pam_syslog(pamh, LOG_DEBUG, " \"%s\"", *argv++); + } + } +} + +static int +_pam_parse (const pam_handle_t *pamh, int argc, const char **argv) +{ + int ctrl=0; + + /* step through arguments */ + for (ctrl=0; argc-- > 0; ++argv) { + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_ST_DEBUG; + else if (!strcmp(*argv,"no_warn")) + ctrl |= PAM_ST_NO_WARN; + else if (!strcmp(*argv,"use_first_pass")) + ctrl |= PAM_ST_USE_PASS1; + else if (!strcmp(*argv,"try_first_pass")) + ctrl |= PAM_ST_TRY_PASS1; + else if (!strcmp(*argv,"rootok")) + ctrl |= PAM_ST_ROOTOK; + + /* simulation options */ + + else if (!strcmp(*argv,"expired")) /* signal password needs + renewal */ + ctrl |= PAM_ST_EXPIRED; + else if (!strcmp(*argv,"fail_1")) /* instruct fn 1 to fail */ + ctrl |= PAM_ST_FAIL_1; + else if (!strcmp(*argv,"fail_2")) /* instruct fn 2 to fail */ + ctrl |= PAM_ST_FAIL_2; + else if (!strcmp(*argv,"prelim")) /* instruct pam_sm_setcred + to fail on first call */ + ctrl |= PAM_ST_PRELIM; + else if (!strcmp(*argv,"required")) /* module is fussy about the + user being authenticated */ + ctrl |= PAM_ST_REQUIRE_PWD; + + else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + return ctrl; +} + +static int converse(pam_handle_t *pamh, int nargs + , const struct pam_message **message + , struct pam_response **response) +{ + int retval; + const void *void_conv; + const struct pam_conv *conv; + + retval = pam_get_item(pamh,PAM_CONV,&void_conv); + conv = void_conv; + if (retval == PAM_SUCCESS && conv) { + retval = conv->conv(nargs, message, response, conv->appdata_ptr); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "converse returned %d: %s", + retval, pam_strerror(pamh, retval)); + } + } else { + pam_syslog(pamh, LOG_ERR, "converse failed to get pam_conv"); + if (retval == PAM_SUCCESS) + retval = PAM_BAD_ITEM; /* conv was null */ + } + + return retval; +} + +/* authentication management functions */ + +static int stress_get_password(pam_handle_t *pamh, int flags + , int ctrl, char **password) +{ + const void *pam_pass; + char *pass; + + if ( (ctrl & (PAM_ST_TRY_PASS1|PAM_ST_USE_PASS1)) + && (pam_get_item(pamh,PAM_AUTHTOK,&pam_pass) + == PAM_SUCCESS) + && (pam_pass != NULL) ) { + if ((pass = strdup(pam_pass)) == NULL) + return PAM_BUF_ERR; + } else if ((ctrl & PAM_ST_USE_PASS1)) { + pam_syslog(pamh, LOG_WARNING, "no forwarded password"); + return PAM_PERM_DENIED; + } else { /* we will have to get one */ + struct pam_message msg[1]; + const struct pam_message *pmsg[1]; + struct pam_response *resp; + int retval; + + /* set up conversation call */ + + pmsg[0] = &msg[0]; + msg[0].msg_style = PAM_PROMPT_ECHO_OFF; + msg[0].msg = "STRESS Password: "; + resp = NULL; + + if ((retval = converse(pamh,1,pmsg,&resp)) != PAM_SUCCESS) { + return retval; + } + + if (resp) { + if ((resp[0].resp == NULL) && (ctrl & PAM_ST_DEBUG)) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_authenticate: NULL authtok given"); + } + if ((flags & PAM_DISALLOW_NULL_AUTHTOK) + && resp[0].resp == NULL) { + free(resp); + return PAM_AUTH_ERR; + } + + pass = resp[0].resp; /* remember this! */ + + resp[0].resp = NULL; + } else { + if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_authenticate: no error reported"); + pam_syslog(pamh, LOG_DEBUG, + "getting password, but NULL returned!?"); + } + return PAM_CONV_ERR; + } + free(resp); + } + + *password = pass; /* this *MUST* be free()'d by this module */ + + return PAM_SUCCESS; +} + +/* function to clean up data items */ + +static void +wipe_up (pam_handle_t *pamh UNUSED, void *data, int error UNUSED) +{ + free(data); +} + +int pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + const char *username; + int retval=PAM_SUCCESS; + char *pass; + int ctrl; + + D(("called.")); + + ctrl = _pam_parse(pamh, argc, argv); + _pam_report(pamh, ctrl, "pam_sm_authenticate", flags, argc, argv); + + /* try to get the username */ + + retval = pam_get_user(pamh, &username, "username: "); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, + "pam_sm_authenticate: cannot determine user name: %s", + pam_strerror(pamh, retval)); + return retval; + } + else if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_authenticate: username = %s", username); + } + + /* now get the password */ + + retval = stress_get_password(pamh,flags,ctrl,&pass); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_WARNING, + "pam_sm_authenticate: failed to get a password"); + return retval; + } + + /* try to set password item */ + + retval = pam_set_item(pamh,PAM_AUTHTOK,pass); + pam_overwrite_string(pass); /* clean up local copy of password */ + free(pass); + pass = NULL; + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_WARNING, + "pam_sm_authenticate: failed to store new password"); + return retval; + } + + /* if we are debugging then we print the password */ + + if (ctrl & PAM_ST_DEBUG) { + const void *pam_pass; + (void) pam_get_item(pamh,PAM_AUTHTOK,&pam_pass); + pam_syslog(pamh, LOG_DEBUG, + "pam_st_authenticate: password entered is: [%s]", + (const char *)pam_pass); + } + + /* if we signal a fail for this function then fail */ + + if ((ctrl & PAM_ST_FAIL_1) && retval == PAM_SUCCESS) + return PAM_PERM_DENIED; + + return retval; +} + +int pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, ctrl, "pam_sm_setcred", flags, argc, argv); + + if (ctrl & PAM_ST_FAIL_2) + return PAM_CRED_ERR; + + return PAM_SUCCESS; +} + +/* account management functions */ + +int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, ctrl,"pam_sm_acct_mgmt", flags, argc, argv); + + if (ctrl & PAM_ST_FAIL_1) + return PAM_PERM_DENIED; + else if (ctrl & PAM_ST_EXPIRED) { + int retval; + void *text = strdup("yes"); + if (!text) + return PAM_BUF_ERR; + retval = pam_set_data(pamh,"stress_new_pwd",text,wipe_up); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_acct_mgmt: failed setting stress_new_pwd"); + free(text); + return retval; + } + + if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_acct_mgmt: need a new password"); + } + return PAM_NEW_AUTHTOK_REQD; + } + + return PAM_SUCCESS; +} + +int pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + const void *username, *service; + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, ctrl,"pam_sm_open_session", flags, argc, argv); + + if ((pam_get_item(pamh, PAM_USER, &username) + != PAM_SUCCESS || !username) + || (pam_get_item(pamh, PAM_SERVICE, &service) + != PAM_SUCCESS || !service)) { + pam_syslog(pamh, LOG_WARNING, "pam_sm_open_session: for whom?"); + return PAM_SESSION_ERR; + } + + pam_syslog(pamh, LOG_NOTICE, "opened [%s] session for user [%s]", + (const char *)service, (const char *)username); + + if (ctrl & PAM_ST_FAIL_1) + return PAM_SESSION_ERR; + + return PAM_SUCCESS; +} + +int pam_sm_close_session(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + const void *username, *service; + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, ctrl,"pam_sm_close_session", flags, argc, argv); + + if ((pam_get_item(pamh, PAM_USER, &username) + != PAM_SUCCESS || !username) + || (pam_get_item(pamh, PAM_SERVICE, &service) + != PAM_SUCCESS || !service)) { + pam_syslog(pamh, LOG_WARNING, "pam_sm_close_session: for whom?"); + return PAM_SESSION_ERR; + } + + pam_syslog(pamh, LOG_NOTICE, "closed [%s] session for user [%s]", + (const char *)service, (const char *)username); + + if (ctrl & PAM_ST_FAIL_2) + return PAM_SESSION_ERR; + + return PAM_SUCCESS; +} + +int pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int retval; + int ctrl = _pam_parse(pamh, argc, argv); + + D(("called. [post parsing]")); + + _pam_report(pamh, ctrl,"pam_sm_chauthtok", flags, argc, argv); + + /* this function should be called twice by the Linux-PAM library */ + + if (flags & PAM_PRELIM_CHECK) { /* first call */ + if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "pam_sm_chauthtok: prelim check"); + } + if (ctrl & PAM_ST_PRELIM) + return PAM_TRY_AGAIN; + + return PAM_SUCCESS; + } else if (flags & PAM_UPDATE_AUTHTOK) { /* second call */ + struct pam_message msg[3]; + const struct pam_message *pmsg[3]; + struct pam_response *resp; + const void *text; + char *txt=NULL; + int i; + + if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, "pam_sm_chauthtok: alter password"); + } + + if (ctrl & PAM_ST_FAIL_1) + return PAM_AUTHTOK_LOCK_BUSY; + + if ( !(ctrl & PAM_ST_EXPIRED) + && (flags & PAM_CHANGE_EXPIRED_AUTHTOK) + && (pam_get_data(pamh,"stress_new_pwd", &text) + != PAM_SUCCESS || strcmp(text,"yes"))) { + return PAM_SUCCESS; /* the token has not expired */ + } + + /* the password should be changed */ + + if ((ctrl & PAM_ST_REQUIRE_PWD) + && !(getuid() == 0 && (ctrl & PAM_ST_ROOTOK)) + ) { /* first get old one? */ + char *pass; + + if (ctrl & PAM_ST_DEBUG) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_chauthtok: getting old password"); + } + retval = stress_get_password(pamh,flags,ctrl,&pass); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_chauthtok: no password obtained"); + return retval; + } + retval = pam_set_item(pamh, PAM_OLDAUTHTOK, pass); + pam_overwrite_string(pass); + free(pass); + pass = NULL; + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_chauthtok: could not set OLDAUTHTOK"); + return retval; + } + } + + /* set up for conversation */ + + if (!(flags & PAM_SILENT)) { + const void *username; + + if ( pam_get_item(pamh, PAM_USER, &username) + || username == NULL ) { + pam_syslog(pamh, LOG_ERR, "no username set"); + return PAM_USER_UNKNOWN; + } + pmsg[0] = &msg[0]; + msg[0].msg_style = PAM_TEXT_INFO; + if (asprintf(&txt, "Changing STRESS password for %s.", + (const char *)username) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + return PAM_BUF_ERR; + } + + msg[0].msg = txt; + i = 1; + } else { + i = 0; + } + + pmsg[i] = &msg[i]; + msg[i].msg_style = PAM_PROMPT_ECHO_OFF; + msg[i++].msg = "Enter new STRESS password: "; + pmsg[i] = &msg[i]; + msg[i].msg_style = PAM_PROMPT_ECHO_OFF; + msg[i++].msg = "Retype new STRESS password: "; + resp = NULL; + + retval = converse(pamh,i,pmsg,&resp); + free(txt); + txt = NULL; /* clean up */ + if (retval != PAM_SUCCESS) { + return retval; + } + + if (resp == NULL) { + pam_syslog(pamh, LOG_ERR, + "pam_sm_chauthtok: no response from conv"); + return PAM_CONV_ERR; + } + + /* store the password */ + + if (resp[i-2].resp && resp[i-1].resp) { + if (strcmp(resp[i-2].resp,resp[i-1].resp)) { + /* passwords are not the same; forget and return error */ + + pam_drop_response(resp, i); + + if (!(flags & PAM_SILENT) && !(ctrl & PAM_ST_NO_WARN)) { + pmsg[0] = &msg[0]; + msg[0].msg_style = PAM_ERROR_MSG; + msg[0].msg = "Verification mis-typed; " + "password unchanged"; + resp = NULL; + (void) converse(pamh,1,pmsg,&resp); + if (resp) { + pam_drop_response(resp, 1); + } + } + return PAM_AUTHTOK_ERR; + } + + if (pam_get_item(pamh,PAM_AUTHTOK,&text) + == PAM_SUCCESS) { + (void) pam_set_item(pamh,PAM_OLDAUTHTOK,text); + text = NULL; + } + (void) pam_set_item(pamh,PAM_AUTHTOK,resp[0].resp); + } else { + pam_syslog(pamh, LOG_DEBUG, + "pam_sm_chauthtok: problem with resp"); + retval = PAM_SYSTEM_ERR; + } + + pam_drop_response(resp, i); /* clean up the passwords */ + } else { + pam_syslog(pamh, LOG_ERR, + "pam_sm_chauthtok: this must be a Linux-PAM error"); + return PAM_SYSTEM_ERR; + } + + return retval; +} diff --git a/modules/pam_stress/tst-pam_stress b/modules/pam_stress/tst-pam_stress new file mode 100755 index 0000000..24be756 --- /dev/null +++ b/modules/pam_stress/tst-pam_stress @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_stress.so diff --git a/modules/pam_succeed_if/README.xml b/modules/pam_succeed_if/README.xml new file mode 100644 index 0000000..1c174af --- /dev/null +++ b/modules/pam_succeed_if/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_succeed_if.8.xml" xpointer='xpointer(id("pam_succeed_if-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_succeed_if/meson.build b/modules/pam_succeed_if/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_succeed_if/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_succeed_if/pam_succeed_if.8.xml b/modules/pam_succeed_if/pam_succeed_if.8.xml new file mode 100644 index 0000000..90fd114 --- /dev/null +++ b/modules/pam_succeed_if/pam_succeed_if.8.xml @@ -0,0 +1,303 @@ + + + + + + pam_succeed_if + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_succeed_if + test account characteristics + + + + + + pam_succeed_if.so + flag + condition + + + + + + DESCRIPTION + + pam_succeed_if.so is designed to succeed or fail authentication + based on characteristics of the account belonging to the user being + authenticated or values of other PAM items. One use is to select whether + to load other modules based on this test. + + + + The module should be given one or more conditions as module arguments, + and authentication will succeed only if all of the conditions are met. + + + + + OPTIONS + + The following flags are supported: + + + + + debug + + Turns on debugging messages sent to syslog. + + + + use_uid + + + Evaluate conditions using the account of the user whose UID + the application is running under instead of the user being + authenticated. + + + + + quiet + + Don't log failure or success to the system log. + + + + quiet_fail + + + Don't log failure to the system log. + + + + + quiet_success + + + Don't log success to the system log. + + + + + audit + + + Log unknown users to the system log. + + + + + + + Conditions are three words: a field, a test, + and a value to test for. + + + Available fields are user, + uid, gid, + shell, home, + ruser, rhost, + tty and service: + + + + + field < number + + Field has a value numerically less than number. + + + + field <= number + + + Field has a value numerically less than or equal to number. + + + + + field eq number + + + Field has a value numerically equal to number. + + + + + field >= number + + + Field has a value numerically greater than or equal to number. + + + + + field > number + + + Field has a value numerically greater than number. + + + + + field ne number + + + Field has a value numerically different from number. + + + + + field = string + + + Field exactly matches the given string. + + + + + field != string + + + Field does not match the given string. + + + + + field =~ glob + + Field matches the given glob. + + + + field !~ glob + + Field does not match the given glob. + + + + field in item:item:... + + Field is contained in the list of items separated by colons. + + + + field notin item:item:... + + Field is not contained in the list of items separated by colons. + + + + user ingroup group[:group:....] + + User is in given group(s). + + + + user notingroup group[:group:....] + + User is not in given group(s). + + + + user innetgr netgroup + + (user,host) is in given netgroup. + + + + user notinnetgr group + + (user,host) is not in given netgroup. + + + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + + PAM_SUCCESS + + + The condition was true. + + + + + + PAM_AUTH_ERR + + + The condition was false. + + + + + + PAM_SERVICE_ERR + + + A service error occurred or the arguments can't be + parsed correctly. + + + + + + + + + EXAMPLES + + To emulate the behaviour of pam_wheel, except + there is no fallback to group 0 being only approximated by checking also the root group membership: + + +auth required pam_succeed_if.so quiet user ingroup wheel:root + + + + Given that the type matches, only loads the othermodule rule if + the UID is over 500. Adjust the number after default to skip + several rules. + + +type [default=1 success=ignore] pam_succeed_if.so quiet uid > 500 +type required othermodule.so arguments... + + + + + SEE ALSO + + + glob7 + , + + pam8 + + + + + + AUTHOR + Nalin Dahyabhai <nalin@redhat.com> + + \ No newline at end of file diff --git a/modules/pam_succeed_if/pam_succeed_if.c b/modules/pam_succeed_if/pam_succeed_if.c new file mode 100644 index 0000000..05be13d --- /dev/null +++ b/modules/pam_succeed_if/pam_succeed_if.c @@ -0,0 +1,614 @@ +/****************************************************************************** + * A simple user-attribute based module for PAM. + * + * Copyright (c) 2003 Red Hat, Inc. + * Written by Nalin Dahyabhai + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* Basically, run cmp(atol(left), atol(right)), returning PAM_SUCCESS if + * the function returns non-zero, PAM_AUTH_ERR if it returns zero, and + * PAM_SERVICE_ERR if the arguments can't be parsed as numbers. */ +static int +evaluate_num(const pam_handle_t *pamh, const char *left, + const char *right, int (*cmp)(long long, long long)) +{ + long long l, r; + char *p; + int ret = PAM_SUCCESS; + + errno = 0; + l = strtoll(left, &p, 0); + if ((p == NULL) || (*p != '\0') || (p == left) || errno) { + pam_syslog(pamh, LOG_INFO, "\"%s\" is not a number", left); + ret = PAM_SERVICE_ERR; + } + + r = strtoll(right, &p, 0); + if ((p == NULL) || (*p != '\0') || (p == right) || errno) { + pam_syslog(pamh, LOG_INFO, "\"%s\" is not a number", right); + ret = PAM_SERVICE_ERR; + } + + if (ret != PAM_SUCCESS) { + return ret; + } + + return cmp(l, r) ? PAM_SUCCESS : PAM_AUTH_ERR; +} + +/* Simple numeric comparison callbacks. */ +static int +eq(long long i, long long j) +{ + return i == j; +} +static int +ne(long long i, long long j) +{ + return i != j; +} +static int +lt(long long i, long long j) +{ + return i < j; +} +static int +le(long long i, long long j) +{ + return lt(i, j) || eq(i, j); +} +static int +gt(long long i, long long j) +{ + return i > j; +} +static int +ge(long long i, long long j) +{ + return gt(i, j) || eq(i, j); +} + +/* Test for numeric equality. */ +static int +evaluate_eqn(const pam_handle_t *pamh, const char *left, const char *right) +{ + return evaluate_num(pamh, left, right, eq); +} +/* Test for string equality. */ +static int +evaluate_eqs(const char *left, const char *right) +{ + return (strcmp(left, right) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Test for numeric inequality. */ +static int +evaluate_nen(const pam_handle_t *pamh, const char *left, const char *right) +{ + return evaluate_num(pamh, left, right, ne); +} +/* Test for string inequality. */ +static int +evaluate_nes(const char *left, const char *right) +{ + return (strcmp(left, right) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Test for numeric less-than-ness(?) */ +static int +evaluate_lt(const pam_handle_t *pamh, const char *left, const char *right) +{ + return evaluate_num(pamh, left, right, lt); +} +/* Test for numeric less-than-or-equal-ness(?) */ +static int +evaluate_le(const pam_handle_t *pamh, const char *left, const char *right) +{ + return evaluate_num(pamh, left, right, le); +} +/* Test for numeric greater-than-ness(?) */ +static int +evaluate_gt(const pam_handle_t *pamh, const char *left, const char *right) +{ + return evaluate_num(pamh, left, right, gt); +} +/* Test for numeric greater-than-or-equal-ness(?) */ +static int +evaluate_ge(const pam_handle_t *pamh, const char *left, const char *right) +{ + return evaluate_num(pamh, left, right, ge); +} +/* Check for file glob match. */ +static int +evaluate_glob(const char *left, const char *right) +{ + return (fnmatch(right, left, 0) == 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Check for file glob mismatch. */ +static int +evaluate_noglob(const char *left, const char *right) +{ + return (fnmatch(right, left, 0) != 0) ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Check for list match. */ +static int +evaluate_inlist(const char *left, const char *right) +{ + char *p; + /* Don't care about left containing ':'. */ + while ((p=strstr(right, left)) != NULL) { + if (p == right || *(p-1) == ':') { /* ':' is a list separator */ + p += strlen(left); + if (*p == '\0' || *p == ':') { + return PAM_SUCCESS; + } + } + right = strchr(p, ':'); + if (right == NULL) + break; + else + ++right; + } + return PAM_AUTH_ERR; +} +/* Check for list mismatch. */ +static int +evaluate_notinlist(const char *left, const char *right) +{ + return evaluate_inlist(left, right) != PAM_SUCCESS ? PAM_SUCCESS : PAM_AUTH_ERR; +} +/* Return PAM_SUCCESS if the user is in the group. */ +static int +evaluate_ingroup(pam_handle_t *pamh, const char *user, const char *grouplist) +{ + char *ptr = NULL; + static const char delim[] = ":"; + char const *grp = NULL; + char *group = strdup(grouplist); + + if (group == NULL) + return PAM_BUF_ERR; + + grp = strtok_r(group, delim, &ptr); + while(grp != NULL) { + if (pam_modutil_user_in_group_nam_nam(pamh, user, grp) == 1) { + free(group); + return PAM_SUCCESS; + } + grp = strtok_r(NULL, delim, &ptr); + } + free(group); + return PAM_AUTH_ERR; +} +/* Return PAM_SUCCESS if the user is NOT in the group. */ +static int +evaluate_notingroup(pam_handle_t *pamh, const char *user, const char *grouplist) +{ + char *ptr = NULL; + static const char delim[] = ":"; + char const *grp = NULL; + char *group = strdup(grouplist); + + if (group == NULL) + return PAM_BUF_ERR; + + grp = strtok_r(group, delim, &ptr); + while(grp != NULL) { + if (pam_modutil_user_in_group_nam_nam(pamh, user, grp) == 1) { + free(group); + return PAM_AUTH_ERR; + } + grp = strtok_r(NULL, delim, &ptr); + } + free(group); + return PAM_SUCCESS; +} + +#ifdef HAVE_INNETGR +# define SOMETIMES_UNUSED UNUSED +#else +# define SOMETIMES_UNUSED +#endif + +/* Return PAM_SUCCESS if the (host,user) is in the netgroup. */ +static int +evaluate_innetgr(const pam_handle_t* pamh SOMETIMES_UNUSED, const char *host, const char *user, const char *group) +{ +#ifdef HAVE_INNETGR + if (innetgr(group, host, user, NULL) == 1) + return PAM_SUCCESS; +#else + pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support"); +#endif + + return PAM_AUTH_ERR; +} +/* Return PAM_SUCCESS if the (host,user) is NOT in the netgroup. */ +static int +evaluate_notinnetgr(const pam_handle_t* pamh SOMETIMES_UNUSED, const char *host, const char *user, const char *group) +{ +#ifdef HAVE_INNETGR + if (innetgr(group, host, user, NULL) == 0) + return PAM_SUCCESS; +#else + pam_syslog (pamh, LOG_ERR, "pam_succeed_if does not have netgroup support"); +#endif + return PAM_AUTH_ERR; +} + +/* Match a triple. */ +static int +evaluate(pam_handle_t *pamh, int debug, + const char *left, const char *qual, const char *right, + struct passwd **pwd, const char *user) +{ + char numstr[sizeof(long) * 3 + 1] = ""; + const char *attribute = left; + /* Get information about the user if needed. */ + if ((*pwd == NULL) && + ((strcasecmp(left, "uid") == 0) || + (strcasecmp(left, "gid") == 0) || + (strcasecmp(left, "shell") == 0) || + (strcasecmp(left, "home") == 0) || + (strcasecmp(left, "dir") == 0) || + (strcasecmp(left, "homedir") == 0))) { + *pwd = pam_modutil_getpwnam(pamh, user); + if (*pwd == NULL) { + return PAM_USER_UNKNOWN; + } + } + /* Figure out what we're evaluating here, and convert it to a string.*/ + if ((strcasecmp(left, "login") == 0) || + (strcasecmp(left, "name") == 0) || + (strcasecmp(left, "user") == 0)) { + left = user; + } else if (strcasecmp(left, "uid") == 0) { + snprintf(numstr, sizeof(numstr), "%lu", + (unsigned long) (*pwd)->pw_uid); + left = numstr; + } else if (strcasecmp(left, "gid") == 0) { + snprintf(numstr, sizeof(numstr), "%lu", + (unsigned long) (*pwd)->pw_gid); + left = numstr; + } else if (strcasecmp(left, "shell") == 0) { + left = (*pwd)->pw_shell; + } else if ((strcasecmp(left, "home") == 0) || + (strcasecmp(left, "dir") == 0) || + (strcasecmp(left, "homedir") == 0)) { + left = (*pwd)->pw_dir; + } else if (strcasecmp(left, "service") == 0) { + const void *svc; + if (pam_get_item(pamh, PAM_SERVICE, &svc) != PAM_SUCCESS || + svc == NULL) + svc = ""; + left = (const char *)svc; + } else if (strcasecmp(left, "ruser") == 0) { + const void *ruser; + if (pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS || + ruser == NULL) + ruser = ""; + left = (const char *)ruser; + } else if (strcasecmp(left, "rhost") == 0) { + const void *rhost; + if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS || + rhost == NULL) + rhost = ""; + left = (const char *)rhost; + } else if (strcasecmp(left, "tty") == 0) { + const void *tty; + if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS || + tty == NULL) + tty = ""; + left = (const char *)tty; + } else { + /* If we have no idea what's going on, return an error. */ + pam_syslog(pamh, LOG_ERR, "unknown attribute \"%s\"", left); + return PAM_SERVICE_ERR; + } + if (debug) { + pam_syslog(pamh, LOG_DEBUG, "'%s' resolves to '%s'", + attribute, left); + } + + /* Attribute value < some threshold. */ + if ((strcasecmp(qual, "<") == 0) || + (strcasecmp(qual, "lt") == 0)) { + return evaluate_lt(pamh, left, right); + } + /* Attribute value <= some threshold. */ + if ((strcasecmp(qual, "<=") == 0) || + (strcasecmp(qual, "le") == 0)) { + return evaluate_le(pamh, left, right); + } + /* Attribute value > some threshold. */ + if ((strcasecmp(qual, ">") == 0) || + (strcasecmp(qual, "gt") == 0)) { + return evaluate_gt(pamh, left, right); + } + /* Attribute value >= some threshold. */ + if ((strcasecmp(qual, ">=") == 0) || + (strcasecmp(qual, "ge") == 0)) { + return evaluate_ge(pamh, left, right); + } + /* Attribute value == some threshold. */ + if (strcasecmp(qual, "eq") == 0) { + return evaluate_eqn(pamh, left, right); + } + /* Attribute value = some string. */ + if (strcasecmp(qual, "=") == 0) { + return evaluate_eqs(left, right); + } + /* Attribute value != some threshold. */ + if (strcasecmp(qual, "ne") == 0) { + return evaluate_nen(pamh, left, right); + } + /* Attribute value != some string. */ + if (strcasecmp(qual, "!=") == 0) { + return evaluate_nes(left, right); + } + /* Attribute value matches some pattern. */ + if ((strcasecmp(qual, "=~") == 0) || + (strcasecmp(qual, "glob") == 0)) { + return evaluate_glob(left, right); + } + if ((strcasecmp(qual, "!~") == 0) || + (strcasecmp(qual, "noglob") == 0)) { + return evaluate_noglob(left, right); + } + /* Attribute value matches item in list. */ + if (strcasecmp(qual, "in") == 0) { + return evaluate_inlist(left, right); + } + if (strcasecmp(qual, "notin") == 0) { + return evaluate_notinlist(left, right); + } + /* User is in this group(s). */ + if (strcasecmp(qual, "ingroup") == 0) { + return evaluate_ingroup(pamh, user, right); + } + /* User is not in this group(s). */ + if (strcasecmp(qual, "notingroup") == 0) { + return evaluate_notingroup(pamh, user, right); + } + /* (Rhost, user) is in this netgroup. */ + if (strcasecmp(qual, "innetgr") == 0) { + const void *rhost; + if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS) + rhost = NULL; + return evaluate_innetgr(pamh, rhost, user, right); + } + /* (Rhost, user) is not in this group. */ + if (strcasecmp(qual, "notinnetgr") == 0) { + const void *rhost; + if (pam_get_item(pamh, PAM_RHOST, &rhost) != PAM_SUCCESS) + rhost = NULL; + return evaluate_notinnetgr(pamh, rhost, user, right); + } + /* Fail closed. */ + return PAM_SERVICE_ERR; +} + +static int +pam_succeed_if(pam_handle_t *pamh, int argc, const char **argv) +{ + const char *user; + struct passwd *pwd = NULL; + int ret, i, count, use_uid, debug; + const char *left, *right, *qual; + int quiet_fail, quiet_succ, audit; + + quiet_fail = 0; + quiet_succ = 0; + audit = 0; + for (use_uid = 0, debug = 0, i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug++; + } + if (strcmp(argv[i], "use_uid") == 0) { + use_uid++; + } + if (strcmp(argv[i], "quiet") == 0) { + quiet_fail++; + quiet_succ++; + } + if (strcmp(argv[i], "quiet_fail") == 0) { + quiet_fail++; + } + if (strcmp(argv[i], "quiet_success") == 0) { + quiet_succ++; + } + if (strcmp(argv[i], "audit") == 0) { + audit++; + } + } + + if (use_uid) { + /* Get information about the user. */ + pwd = pam_modutil_getpwuid(pamh, getuid()); + if (pwd == NULL) { + pam_syslog(pamh, LOG_ERR, + "error retrieving information about user %lu", + (unsigned long)getuid()); + return PAM_USER_UNKNOWN; + } + user = pwd->pw_name; + } else { + /* Get the user's name. */ + ret = pam_get_user(pamh, &user, NULL); + if (ret != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, + "cannot determine user name: %s", + pam_strerror(pamh, ret)); + return ret; + } + + /* Postpone requesting password data until it is needed */ + } + + /* Walk the argument list. */ + count = 0; + left = qual = right = NULL; + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + continue; + } + if (strcmp(argv[i], "use_uid") == 0) { + continue; + } + if (strcmp(argv[i], "quiet") == 0) { + continue; + } + if (strcmp(argv[i], "quiet_fail") == 0) { + continue; + } + if (strcmp(argv[i], "quiet_success") == 0) { + continue; + } + if (strcmp(argv[i], "audit") == 0) { + continue; + } + if (left == NULL) { + left = argv[i]; + continue; + } + if (qual == NULL) { + qual = argv[i]; + continue; + } + if (right == NULL) { + right = argv[i]; + if (right == NULL) + continue; + + count++; + ret = evaluate(pamh, debug, + left, qual, right, + &pwd, user); + if (ret == PAM_USER_UNKNOWN && audit) + pam_syslog(pamh, LOG_NOTICE, + "error retrieving information about user %s", + user); + if (ret != PAM_SUCCESS) { + if(!quiet_fail && ret != PAM_USER_UNKNOWN) + pam_syslog(pamh, LOG_INFO, + "requirement \"%s %s %s\" " + "not met by user \"%s\"", + left, qual, right, user); + left = qual = right = NULL; + break; + } + else + if(!quiet_succ) + pam_syslog(pamh, LOG_INFO, + "requirement \"%s %s %s\" " + "was met by user \"%s\"", + left, qual, right, user); + left = qual = right = NULL; + continue; + } + } + + if (left || qual || right) { + ret = PAM_SERVICE_ERR; + pam_syslog(pamh, LOG_ERR, + "incomplete condition detected"); + } else if (count == 0) { + ret = PAM_SUCCESS; + pam_syslog(pamh, LOG_INFO, + "no condition detected; module succeeded"); + } + + return ret; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_succeed_if(pamh, argc, argv); +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_succeed_if(pamh, argc, argv); +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_succeed_if(pamh, argc, argv); +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_succeed_if(pamh, argc, argv); +} + +int +pam_sm_chauthtok(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_succeed_if(pamh, argc, argv); +} diff --git a/modules/pam_succeed_if/tst-pam_succeed_if b/modules/pam_succeed_if/tst-pam_succeed_if new file mode 100755 index 0000000..f2b6dd3 --- /dev/null +++ b/modules/pam_succeed_if/tst-pam_succeed_if @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_succeed_if.so diff --git a/modules/pam_succeed_if/tst-pam_succeed_if-retval.c b/modules/pam_succeed_if/tst-pam_succeed_if-retval.c new file mode 100644 index 0000000..f665040 --- /dev/null +++ b/modules/pam_succeed_if/tst-pam_succeed_if-retval.c @@ -0,0 +1,87 @@ +/* + * Check pam_succeed_if return values. + * + * Copyright (c) 2020 Dmitry V. Levin + * Copyright (c) 2024 Tobias Stoeckmann + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_succeed_if" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char user_name[] = "name"; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so user = name\n" + "account required %s/" LTDIR "%s.so user notin a:b\n" + "password required %s/" LTDIR "%s.so user in x:name\n" + "session required %s/" LTDIR "%s.so rhost eq 0\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_set_item(pamh, PAM_RHOST, "0")); + + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + + /* test some illegal conditions */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so user eq name\n" + "account required %s/" LTDIR "%s.so user in a:b\n" + "password required %s/" LTDIR "%s.so user notin x:name\n" + "session required %s/" LTDIR "%s.so rhost eq []\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + + ASSERT_EQ(PAM_SERVICE_ERR, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_AUTH_ERR, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SERVICE_ERR, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_time/README.xml b/modules/pam_time/README.xml new file mode 100644 index 0000000..8a2faa0 --- /dev/null +++ b/modules/pam_time/README.xml @@ -0,0 +1,19 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_time.8.xml" xpointer='xpointer(id("pam_time-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_time/meson.build b/modules/pam_time/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_time/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_time/pam_time.8.xml b/modules/pam_time/pam_time.8.xml new file mode 100644 index 0000000..1fa60a1 --- /dev/null +++ b/modules/pam_time/pam_time.8.xml @@ -0,0 +1,200 @@ + + + + pam_time + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_time + + PAM module for time control access + + + + + + + + pam_time.so + + conffile=conf-file + + + debug + + + noaudit + + + + + + + DESCRIPTION + + The pam_time PAM module does not authenticate the user, but instead + it restricts access to a system and or specific applications at + various times of the day and on specific days or over various + terminal lines. This module can be configured to deny access to + (individual) users based on their name, the time of day, the day of + week, the service they are applying for and their terminal from which + they are making their request. + + + By default rules for time/port access are taken from config file + /etc/security/time.conf. + An alternative file can be specified with the conffile option. + + + If there is no explicitly specified configuration file and + /etc/security/time.conf does not exist, + %vendordir%/security/time.conf is used. + + + If Linux PAM is compiled with audit support the module will report + when it denies access. + + + + + OPTIONS + + + + + conffile=/path/to/time.conf + + + + Indicate an alternative time.conf style configuration file to override the default. + + + + + + + debug + + + + Some debug information is printed with + syslog3. + + + + + + + noaudit + + + + Do not report logins at disallowed time to the audit subsystem. + + + + + + + + + MODULE TYPES PROVIDED + + Only the type is provided. + + + + + RETURN VALUES + + + PAM_SUCCESS + + + Access was granted. + + + + + PAM_ABORT + + + Not all relevant data could be gotten. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_PERM_DENIED + + + Access was not granted. + + + + + PAM_USER_UNKNOWN + + + The user is not known to the system. + + + + + + + + FILES + + + /etc/security/time.conf + + Default configuration file + + + + + + + EXAMPLES + +#%PAM-1.0 +# +# apply pam_time accounting to login requests +# +login account required pam_time.so + + + + + SEE ALSO + + + time.conf5 + , + + pam.d5 + , + + pam8 + . + + + + + AUTHOR + + pam_time was written by Andrew G. Morgan <morgan@kernel.org>. + + + \ No newline at end of file diff --git a/modules/pam_time/pam_time.c b/modules/pam_time/pam_time.c new file mode 100644 index 0000000..432446a --- /dev/null +++ b/modules/pam_time/pam_time.c @@ -0,0 +1,696 @@ +/* + * pam_time module + * + * Written by Andrew Morgan 1996/6/22 + * (File syntax and much other inspiration from the shadow package + * shadow-960129) + * Field parsing rewritten by Tomas Mraz + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "pam_inline.h" + +#ifdef HAVE_LIBAUDIT +#include +#endif + +#define PAM_TIME_CONF (SCONFIG_DIR "/time.conf") +#ifdef VENDOR_SCONFIG_DIR +#define VENDOR_PAM_TIME_CONF (VENDOR_SCONFIG_DIR "/time.conf") +#endif + +#define PAM_TIME_BUFLEN 1000 +#define FIELD_SEPARATOR ';' /* this is new as of .02 */ + +#define PAM_DEBUG_ARG 0x0001 +#define PAM_NO_AUDIT 0x0002 + +#ifndef TRUE +# define TRUE 1 +#endif +#ifndef FALSE +# define FALSE 0 +#endif + +typedef enum { AND, OR } operator; + +static int +_pam_parse (const pam_handle_t *pamh, int argc, const char **argv, const char **conffile) +{ + int ctrl = 0; + + *conffile = NULL; + /* step through arguments */ + for (; argc-- > 0; ++argv) { + const char *str; + + /* generic options */ + + if (!strcmp(*argv, "debug")) { + ctrl |= PAM_DEBUG_ARG; + } else if (!strcmp(*argv, "noaudit")) { + ctrl |= PAM_NO_AUDIT; + } else if ((str = pam_str_skip_prefix(*argv, "conffile=")) != NULL) { + if (str[0] == '\0') { + pam_syslog(pamh, LOG_ERR, + "conffile= specification missing argument - ignored"); + } else { + *conffile = str; + D(("new Configuration File: %s", *conffile)); + } + } else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + if (*conffile == NULL) { + *conffile = PAM_TIME_CONF; +#ifdef VENDOR_PAM_TIME_CONF + /* + * Check whether PAM_TIME_CONF file is available. + * If it does not exist, fall back to VENDOR_PAM_TIME_CONF file. + */ + struct stat buffer; + if (stat(*conffile, &buffer) != 0 && errno == ENOENT) { + *conffile = VENDOR_PAM_TIME_CONF; + } +#endif + } + + return ctrl; +} + +/* --- static functions for checking whether the user should be let in --- */ + +static char * +shift_buf(char *mem, int from) +{ + char *start = mem; + while ((*mem = mem[from]) != '\0') + ++mem; + pam_overwrite_n(mem, PAM_TIME_BUFLEN - (mem - start)); + return mem; +} + +static void +trim_spaces(char *buf, char *from) +{ + while (from > buf) { + --from; + if (*from == ' ') + *from = '\0'; + else + break; + } +} + +#define STATE_NL 0 /* new line starting */ +#define STATE_COMMENT 1 /* inside comment */ +#define STATE_FIELD 2 /* field following */ +#define STATE_EOF 3 /* end of file or error */ + +static int +read_field(const pam_handle_t *pamh, int fd, char **buf, int *from, int *state, const char *file) +{ + char *to; + char *src; + int i; + char c; + int onspace; + + /* is buf set ? */ + if (! *buf) { + *buf = calloc(1, PAM_TIME_BUFLEN+1); + if (! *buf) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + D(("no memory")); + *state = STATE_EOF; + return -1; + } + *from = 0; + *state = STATE_NL; + fd = open(file, O_RDONLY); + if (fd < 0) { + pam_syslog(pamh, LOG_ERR, "error opening %s: %m", file); + _pam_drop(*buf); + *state = STATE_EOF; + return -1; + } + } + + + if (*from > 0) + to = shift_buf(*buf, *from); + else + to = *buf; + + while (fd != -1 && to - *buf < PAM_TIME_BUFLEN) { + i = pam_modutil_read(fd, to, PAM_TIME_BUFLEN - (to - *buf)); + if (i < 0) { + pam_syslog(pamh, LOG_ERR, "error reading %s: %m", file); + close(fd); + pam_overwrite_n(*buf, PAM_TIME_BUFLEN); + _pam_drop(*buf); + *state = STATE_EOF; + return -1; + } else if (!i) { + close(fd); + fd = -1; /* end of file reached */ + } + + to += i; + } + + if (to == *buf) { + /* nothing previously in buf, nothing read */ + _pam_drop(*buf); + *state = STATE_EOF; + return -1; + } + + pam_overwrite_n(to, PAM_TIME_BUFLEN - (to - *buf)); + + to = *buf; + onspace = 1; /* delete any leading spaces */ + + for (src = to; (c=*src) != '\0'; ++src) { + if (*state == STATE_COMMENT && c != '\n') { + continue; + } + + switch (c) { + case '\n': + *state = STATE_NL; + *to = '\0'; + *from = (src - *buf) + 1; + trim_spaces(*buf, to); + return fd; + + case '\t': + case ' ': + if (!onspace) { + onspace = 1; + *to++ = ' '; + } + break; + + case '!': + onspace = 1; /* ignore following spaces */ + *to++ = '!'; + break; + + case '#': + *state = STATE_COMMENT; + break; + + case FIELD_SEPARATOR: + *state = STATE_FIELD; + *to = '\0'; + *from = (src - *buf) + 1; + trim_spaces(*buf, to); + return fd; + + case '\\': + if (src[1] == '\n') { + ++src; /* skip it */ + break; + } + /* fallthrough */ + default: + *to++ = c; + onspace = 0; + } + if (src > to) + *src = '\0'; /* clearing */ + } + + if (*state != STATE_COMMENT) { + *state = STATE_COMMENT; + pam_syslog(pamh, LOG_ERR, "field too long - ignored"); + **buf = '\0'; + } else { + *to = '\0'; + trim_spaces(*buf, to); + } + + *from = 0; + return fd; +} + +/* read a member from a field */ + +static int +logic_member(const char *string, int *at) +{ + int c,to; + int done=0; + int token=0; + + to=*at; + do { + c = string[to++]; + + switch (c) { + + case '\0': + --to; + done = 1; + break; + + case '&': + case '|': + case '!': + if (token) { + --to; + } + done = 1; + break; + + default: + if (isalpha((unsigned char)c) || c == '*' || isdigit((unsigned char)c) || c == '_' + || c == '-' || c == '.' || c == '/' || c == ':') { + token = 1; + } else if (token) { + --to; + done = 1; + } else { + ++*at; + } + } + } while (!done); + + return to - *at; +} + +typedef enum { VAL, OP } expect; + +static int +logic_field(pam_handle_t *pamh, const void *me, const char *x, int rule, + int (*agrees)(pam_handle_t *pamh, + const void *, const char *, int, int)) +{ + int left=FALSE, right, not=FALSE; + operator oper=OR; + int at=0, l; + expect next=VAL; + + while ((l = logic_member(x,&at))) { + int c = x[at]; + + if (next == VAL) { + if (c == '!') + not = !not; + else if (isalpha((unsigned char)c) || c == '*' || isdigit((unsigned char)c) || c == '_' + || c == '-' || c == '.' || c == '/' || c == ':') { + right = not ^ agrees(pamh, me, x+at, l, rule); + if (oper == AND) + left &= right; + else + left |= right; + next = OP; + } else { + pam_syslog(pamh, LOG_ERR, + "garbled syntax; expected name (rule #%d)", + rule); + return FALSE; + } + } else { /* OP */ + switch (c) { + case '&': + oper = AND; + break; + case '|': + oper = OR; + break; + default: + pam_syslog(pamh, LOG_ERR, + "garbled syntax; expected & or | (rule #%d)", + rule); + D(("%c at %d",c,at)); + return FALSE; + } + next = VAL; + not = FALSE; + } + at += l; + } + + return left; +} + +static int +is_same(pam_handle_t *pamh UNUSED, const void *A, const char *b, + int len, int rule UNUSED) +{ + int i; + const char *a; + + a = A; + for (i=0; len > 0; ++i, --len) { + if (b[i] != a[i]) { + if (b[i++] == '*') { + return (!--len || !strncmp(b+i,a+strlen(a)-len,len)); + } else + return FALSE; + } + } + + /* Ok, we know that b is a substring from A and does not contain + wildcards, but now the length of both strings must be the same, + too. In this case it means, a[i] has to be the end of the string. */ + if (a[i] != '\0') + return FALSE; + + return ( !len ); +} + +typedef struct { + int day; /* array of 7 bits, one set for today */ + int minute; /* integer, hour*100+minute for now */ +} TIME; + +static struct day { + const char *d; + int bit; +} const days[11] = { + { "su", 01 }, + { "mo", 02 }, + { "tu", 04 }, + { "we", 010 }, + { "th", 020 }, + { "fr", 040 }, + { "sa", 0100 }, + { "wk", 076 }, + { "wd", 0101 }, + { "al", 0177 }, + { NULL, 0 } +}; + +static TIME +time_now(void) +{ + struct tm *local; + time_t the_time; + TIME this; + + the_time = time((time_t *)0); /* get the current time */ + local = localtime(&the_time); + this.day = days[local->tm_wday].bit; + this.minute = local->tm_hour*100 + local->tm_min; + + D(("day: 0%o, time: %.4d", this.day, this.minute)); + return this; +} + +/* take the current date and see if the range "date" passes it */ +static int +check_time(pam_handle_t *pamh, const void *AT, const char *times, + int len, int rule) +{ + int not,pass; + int marked_day, time_start, time_end; + const TIME *at; + int i,j=0; + + at = AT; + D(("checking: 0%o/%.4d vs. %s", at->day, at->minute, times)); + + if (times == NULL) { + /* this should not happen */ + pam_syslog(pamh, LOG_CRIT, + "internal error in file %s at line %d", + __FILE__, __LINE__); + return FALSE; + } + + if (times[j] == '!') { + ++j; + not = TRUE; + } else { + not = FALSE; + } + + for (marked_day = 0; len > 0 && isalpha((unsigned char)times[j]); --len) { + int this_day=-1; + + D(("%c%c ?", times[j], times[j+1])); + for (i=0; days[i].d != NULL; ++i) { + if (tolower((unsigned char)times[j]) == days[i].d[0] + && tolower((unsigned char)times[j+1]) == days[i].d[1] ) { + this_day = days[i].bit; + break; + } + } + j += 2; + if (this_day == -1) { + pam_syslog(pamh, LOG_ERR, "bad day specified (rule #%d)", rule); + return FALSE; + } + marked_day ^= this_day; + } + if (marked_day == 0) { + pam_syslog(pamh, LOG_ERR, "no day specified"); + return FALSE; + } + D(("day range = 0%o", marked_day)); + + time_start = 0; + for (i=0; len > 0 && i < 4 && isdigit((unsigned char)times[i+j]); ++i, --len) { + time_start *= 10; + time_start += times[i+j]-'0'; /* is this portable? */ + } + j += i; + + if (times[j] == '-') { + time_end = 0; + for (i=1; len > 0 && i < 5 && isdigit((unsigned char)times[i+j]); ++i, --len) { + time_end *= 10; + time_end += times[i+j]-'0'; /* is this portable */ + } + j += i; + } else + time_end = -1; + + D(("i=%d, time_end=%d, times[j]='%c'", i, time_end, times[j])); + if (i != 5 || time_end == -1) { + pam_syslog(pamh, LOG_ERR, "no/bad times specified (rule #%d)", rule); + return TRUE; + } + D(("times(%d to %d)", time_start,time_end)); + D(("marked_day = 0%o", marked_day)); + + /* compare with the actual time now */ + + pass = FALSE; + if (time_start < time_end) { /* start < end ? --> same day */ + if ((at->day & marked_day) && (at->minute >= time_start) + && (at->minute < time_end)) { + D(("time is listed")); + pass = TRUE; + } + } else { /* spans two days */ + if ((at->day & marked_day) && (at->minute >= time_start)) { + D(("caught on first day")); + pass = TRUE; + } else { + marked_day <<= 1; + marked_day |= (marked_day & 0200) ? 1:0; + D(("next day = 0%o", marked_day)); + if ((at->day & marked_day) && (at->minute <= time_end)) { + D(("caught on second day")); + pass = TRUE; + } + } + } + + return (not ^ pass); +} + +static int +check_account(pam_handle_t *pamh, const char *service, + const char *tty, const char *user, const char *file) +{ + int from=0, state=STATE_NL, fd=-1; + char *buffer=NULL; + int count=0; + TIME here_and_now; + int retval=PAM_SUCCESS; + + here_and_now = time_now(); /* find current time */ + do { + int good=TRUE,intime; + + /* here we get the service name field */ + + fd = read_field(pamh, fd, &buffer, &from, &state, file); + if (!buffer || !buffer[0]) { + /* empty line .. ? */ + continue; + } + ++count; + + if (state != STATE_FIELD) { + pam_syslog(pamh, LOG_ERR, + "%s: malformed rule #%d", file, count); + continue; + } + + good = logic_field(pamh, service, buffer, count, is_same); + D(("with service: %s", good ? "passes":"fails" )); + + /* here we get the terminal name field */ + + fd = read_field(pamh, fd, &buffer, &from, &state, file); + if (state != STATE_FIELD) { + pam_syslog(pamh, LOG_ERR, + "%s: malformed rule #%d", file, count); + continue; + } + good &= logic_field(pamh, tty, buffer, count, is_same); + D(("with tty: %s", good ? "passes":"fails" )); + + /* here we get the username field */ + + fd = read_field(pamh, fd, &buffer, &from, &state, file); + if (state != STATE_FIELD) { + pam_syslog(pamh, LOG_ERR, + "%s: malformed rule #%d", file, count); + continue; + } + /* If buffer starts with @, we are using netgroups */ + if (buffer[0] == '@') +#ifdef HAVE_INNETGR + good &= innetgr (&buffer[1], NULL, user, NULL); +#else + pam_syslog (pamh, LOG_ERR, "pam_time does not have netgroup support"); +#endif + else + good &= logic_field(pamh, user, buffer, count, is_same); + D(("with user: %s", good ? "passes":"fails" )); + + /* here we get the time field */ + + fd = read_field(pamh, fd, &buffer, &from, &state, file); + if (state == STATE_FIELD) { + pam_syslog(pamh, LOG_ERR, + "%s: poorly terminated rule #%d", file, count); + continue; + } + + intime = logic_field(pamh, &here_and_now, buffer, count, check_time); + D(("with time: %s", intime ? "passes":"fails" )); + + if (good && !intime) { + /* + * for security parse whole file.. also need to ensure + * that the buffer is free()'d and the file is closed. + */ + retval = PAM_PERM_DENIED; + } else { + D(("rule passed")); + } + } while (state != STATE_EOF); + + return retval; +} + +/* --- public account management functions --- */ + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + const void *service=NULL, *void_tty=NULL; + const char *tty; + const char *user=NULL; + const char *conf_file = NULL; + int ctrl; + int rv; + + ctrl = _pam_parse(pamh, argc, argv, &conf_file); + + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "conffile=%s", conf_file); + } + + /* set service name */ + + if (pam_get_item(pamh, PAM_SERVICE, &service) + != PAM_SUCCESS || service == NULL) { + pam_syslog(pamh, LOG_ERR, "cannot find the current service name"); + return PAM_ABORT; + } + + /* set username */ + + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || *user == '\0') { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name"); + return PAM_USER_UNKNOWN; + } + + /* set tty name */ + + if (pam_get_item(pamh, PAM_TTY, &void_tty) != PAM_SUCCESS + || void_tty == NULL) { + D(("PAM_TTY not set, probing stdin")); + tty = ttyname(STDIN_FILENO); + if (tty == NULL) { + tty = ""; + } + if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "couldn't set tty name"); + return PAM_ABORT; + } + } + else + tty = void_tty; + + if (tty[0] == '/') { /* full path */ + const char *t; + tty++; + if ((t = strchr(tty, '/')) != NULL) { + tty = t + 1; + } + } + + /* good, now we have the service name, the user and the terminal name */ + + D(("service=%s", (const char *) service)); + D(("user=%s", user)); + D(("tty=%s", tty)); + + rv = check_account(pamh, service, tty, user, conf_file); + if (rv != PAM_SUCCESS) { +#ifdef HAVE_LIBAUDIT + if (!(ctrl & PAM_NO_AUDIT)) { + pam_modutil_audit_write(pamh, AUDIT_ANOM_LOGIN_TIME, + "pam_time", rv); /* ignore return value as we fail anyway */ + } +#endif + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "user %s rejected", user); + } + } + return rv; +} + +/* end of module definition */ diff --git a/modules/pam_time/time.conf b/modules/pam_time/time.conf new file mode 100644 index 0000000..68d2dad --- /dev/null +++ b/modules/pam_time/time.conf @@ -0,0 +1,65 @@ +# this is an example configuration file for the pam_time module. Its syntax +# was initially based heavily on that of the shadow package (shadow-960129). +# +# the syntax of the lines is as follows: +# +# services;ttys;users;times +# +# white space is ignored and lines maybe extended with '\\n' (escaped +# newlines). As should be clear from reading these comments, +# text following a '#' is ignored to the end of the line. +# +# the combination of individual users/terminals etc is a logic list +# namely individual tokens that are optionally prefixed with '!' (logical +# not) and separated with '&' (logical and) and '|' (logical or). +# +# services +# is a logic list of PAM service names that the rule applies to. +# +# ttys +# is a logic list of terminal names that this rule applies to. +# +# users +# is a logic list of users or a netgroup of users to whom this +# rule applies. +# +# NB. For these items the simple wildcard '*' may be used only once. +# +# times +# the format here is a logic list of day/time-range +# entries the days are specified by a sequence of two character +# entries, MoTuSa for example is Monday Tuesday and Saturday. Note +# that repeated days are unset MoMo = no day, and MoWk = all weekdays +# bar Monday. The two character combinations accepted are +# +# Mo Tu We Th Fr Sa Su Wk Wd Al +# +# the last two being week-end days and all 7 days of the week +# respectively. As a final example, AlFr means all days except Friday. +# +# each day/time-range can be prefixed with a '!' to indicate "anything +# but" +# +# The time-range part is two 24-hour times HHMM separated by a hyphen +# indicating the start and finish time (if the finish time is smaller +# than the start time it is deemed to apply on the following day). +# +# for a rule to be active, ALL of service+ttys+users must be satisfied +# by the applying process. +# + +# +# Here is a simple example: running blank on tty* (any ttyXXX device), +# the users 'you' and 'me' are denied service all of the time +# + +#blank;tty* & !ttyp*;you|me;!Al0000-2400 + +# Another silly example, user 'root' is denied xsh access +# from pseudo terminals at the weekend and on mondays. + +#xsh;ttyp*;root;!WdMo0000-2400 + +# +# End of example file. +# diff --git a/modules/pam_time/time.conf.5.xml b/modules/pam_time/time.conf.5.xml new file mode 100644 index 0000000..3fe263d --- /dev/null +++ b/modules/pam_time/time.conf.5.xml @@ -0,0 +1,146 @@ + + + + time.conf + 5 + Linux-PAM + Linux-PAM Manual + + + + time.conf + configuration file for the pam_time module + + + + DESCRIPTION + + + The pam_time PAM module does not authenticate the user, but instead + it restricts access to a system and or specific applications at + various times of the day and on specific days or over various + terminal lines. This module can be configured to deny access to + (individual) users based on their name, the time of day, the day of + week, the service they are applying for and their terminal from which + they are making their request. + + + For this module to function correctly there must be a correctly + formatted /etc/security/time.conf file present. + White spaces are ignored and lines maybe extended with '\' (escaped + newlines). Text following a '#' is ignored to the end of the line. + + + + The syntax of the lines is as follows: + + + + services;ttys;users;times + + + In words, each rule occupies a line, terminated with a newline + or the beginning of a comment; a '#'. + It contains four fields separated with semicolons, + ';'. + + + + The first field, the services field, + is a logic list of PAM service names that the rule applies to. + + + + The second field, the tty + field, is a logic list of terminal names that this rule applies to. + + + + The third field, the users + field, is a logic list of users or a netgroup of users to whom this + rule applies. + + + + A logic list namely means individual tokens that are optionally prefixed + with '!' (logical not) and separated with '&' (logical and) and '|' + (logical or). + + + + For these items the simple wildcard '*' may be used only once. + With netgroups no wildcards or logic operators are allowed. + + + + The times field is used to indicate the times + at which this rule applies. The format here is a logic + list of day/time-range entries. The days are specified by a sequence of + two character entries, MoTuSa for example is Monday Tuesday and Saturday. + Note that repeated days are unset MoMo = no day, and MoWk = all weekdays + bar Monday. The two character combinations accepted are Mo Tu We Th Fr Sa + Su Wk Wd Al, the last two being week-end days and all 7 days of the week + respectively. As a final example, AlFr means all days except Friday. + + + Each day/time-range can be prefixed with a '!' to indicate + "anything but". + The time-range part is two 24-hour times HHMM, separated by a hyphen, + indicating the start and finish time (if the finish time is smaller + than the start time it is deemed to apply on the following day). + + + + For a rule to be active, ALL of service+ttys+users must be satisfied + by the applying process. + + + Note, currently there is no daemon enforcing the end of a session. + This needs to be remedied. + + + Poorly formatted rules are logged as errors using + syslog3. + + + + + EXAMPLES + + These are some example lines which might be specified in + /etc/security/time.conf. + + + All users except for root are denied access + to console-login at all times: + +login ; tty* & !ttyp* ; !root ; !Al0000-2400 + + + + + Games (configured to use PAM) are only to be accessed out of + working hours. This rule does not apply to the user + waster: + +games ; * ; !waster ; Wd0000-2400 | Wk1800-0800 + + + + + + SEE ALSO + + pam_time8, + pam.d5, + pam8 + + + + + AUTHOR + + pam_time was written by Andrew G. Morgan <morgan@kernel.org>. + + + \ No newline at end of file diff --git a/modules/pam_time/tst-pam_time b/modules/pam_time/tst-pam_time new file mode 100755 index 0000000..030717b --- /dev/null +++ b/modules/pam_time/tst-pam_time @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_time.so diff --git a/modules/pam_time/tst-pam_time-retval.c b/modules/pam_time/tst-pam_time-retval.c new file mode 100644 index 0000000..3fcf459 --- /dev/null +++ b/modules/pam_time/tst-pam_time-retval.c @@ -0,0 +1,107 @@ +/* + * Check pam_time return values. + * + * Copyright (c) 2020-2022 Dmitry V. Levin + * Copyright (c) 2022 Stefan Schubert + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_time" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char config_file[] = TEST_NAME ".conf"; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + /* PAM_USER_UNKNOWN */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, + fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_USER_UNKNOWN, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_NE(NULL, fp = fopen(config_file, "w")); + ASSERT_LT(0, fprintf(fp, "# only root can access %s\n" + "%s ; * ; !root ; !Al0000-2400\n", + service_file, service_file)); + ASSERT_EQ(0, fclose(fp)); + + /* conffile= specifies an existing file */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, + fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so conffile=%s\n" + "account required %s/" LTDIR "%s.so conffile=%s\n" + "password required %s/" LTDIR "%s.so conffile=%s\n" + "session required %s/" LTDIR "%s.so conffile=%s\n", + cwd, MODULE_NAME, config_file, + cwd, MODULE_NAME, config_file, + cwd, MODULE_NAME, config_file, + cwd, MODULE_NAME, config_file)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "root", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, "noone", &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_MODULE_UNKNOWN, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* cleanup */ + ASSERT_EQ(0, unlink(config_file)); + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_timestamp/README.xml b/modules/pam_timestamp/README.xml new file mode 100644 index 0000000..fe01080 --- /dev/null +++ b/modules/pam_timestamp/README.xml @@ -0,0 +1,31 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_timestamp.8.xml" xpointer='xpointer(id("pam_timestamp-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_timestamp/hmac_openssl_wrapper.c b/modules/pam_timestamp/hmac_openssl_wrapper.c new file mode 100644 index 0000000..8fa6068 --- /dev/null +++ b/modules/pam_timestamp/hmac_openssl_wrapper.c @@ -0,0 +1,413 @@ +/* Wrapper for hmac openssl implementation. + * + * Copyright (c) 2021 Red Hat, Inc. + * Written by Iker Pedrosa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" + +#ifdef WITH_OPENSSL + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "hmac_openssl_wrapper.h" +#include "pam_inline.h" + +#ifdef HAVE_SYS_RANDOM_H +#include +#endif + +#define LOGIN_DEFS "/etc/login.defs" +#define CRYPTO_KEY "HMAC_CRYPTO_ALGO" +#define DEFAULT_ALGORITHM "SHA512" +#define MAX_HMAC_LENGTH 512 +#define MAX_KEY_LENGTH EVP_MAX_KEY_LENGTH + +static char * +get_crypto_algorithm(pam_handle_t *pamh, int debug){ + char *config_value = NULL; + + config_value = pam_modutil_search_key(pamh, LOGIN_DEFS, CRYPTO_KEY); + + if (config_value == NULL) { + config_value = strdup(DEFAULT_ALGORITHM); + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "Key [%s] not found, falling back to default algorithm [%s]\n", + CRYPTO_KEY, DEFAULT_ALGORITHM); + } + } + + return config_value; +} + +static int +PAM_NONNULL((1, 2)) +generate_key(pam_handle_t *pamh, char **key, size_t key_size) +{ + int fd = 0; + ssize_t bytes_read = 0; + char *tmp = *key = NULL; + + tmp = calloc(1, key_size); + if (!tmp) { + pam_syslog(pamh, LOG_CRIT, "Not enough memory"); + return PAM_AUTH_ERR; + } + + /* Try to get random data from OpenSSL first */ + if (RAND_priv_bytes((unsigned char *)tmp, key_size) == 1) { + *key = tmp; + return PAM_SUCCESS; + } + +#ifdef HAVE_GETRANDOM + /* Fallback to getrandom(2) if available */ + if (getrandom(tmp, key_size, 0) == (ssize_t)key_size) { + *key = tmp; + return PAM_SUCCESS; + } +#endif + + /* Fallback to /dev/urandom */ + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) { + pam_syslog(pamh, LOG_ERR, "Cannot open /dev/urandom: %m"); + pam_overwrite_n(tmp, key_size); + free(tmp); + return PAM_AUTH_ERR; + } + + bytes_read = pam_modutil_read(fd, tmp, key_size); + close(fd); + + if (bytes_read < 0 || (size_t)bytes_read < key_size) { + pam_syslog(pamh, LOG_ERR, "Short read on random device"); + pam_overwrite_n(tmp, key_size); + free(tmp); + return PAM_AUTH_ERR; + } + + *key = tmp; + + return PAM_SUCCESS; +} + +static int +PAM_NONNULL((1, 3, 4)) +read_file(pam_handle_t *pamh, int fd, char **text, size_t *text_length) +{ + struct stat st; + ssize_t bytes_read = 0; + char *tmp = NULL; + + if (fstat(fd, &st) == -1) { + pam_syslog(pamh, LOG_ERR, "Unable to stat file: %m"); + close(fd); + return PAM_AUTH_ERR; + } + + if (st.st_size == 0) { + pam_syslog(pamh, LOG_ERR, "Key file size cannot be 0"); + close(fd); + return PAM_AUTH_ERR; + } + + if ((uintmax_t)st.st_size > (uintmax_t)INT_MAX) { + pam_syslog(pamh, LOG_ERR, "Key file is too large"); + close(fd); + return PAM_AUTH_ERR; + } + + tmp = calloc(1, st.st_size); + if (!tmp) { + pam_syslog(pamh, LOG_CRIT, "Not enough memory"); + close(fd); + return PAM_AUTH_ERR; + } + + bytes_read = pam_modutil_read(fd, tmp, st.st_size); + close(fd); + + if (bytes_read < st.st_size) { + pam_syslog(pamh, LOG_ERR, "Short read on key file"); + pam_overwrite_n(tmp, st.st_size); + free(tmp); + return PAM_AUTH_ERR; + } + + *text = tmp; + *text_length = st.st_size; + + return PAM_SUCCESS; +} + +static int +PAM_NONNULL((1, 2, 3)) +write_file(pam_handle_t *pamh, const char *file_name, char *text, + size_t text_length, uid_t owner, gid_t group) +{ + int fd = 0; + ssize_t bytes_written = 0; + + fd = open(file_name, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + if (fd == -1) { + pam_syslog(pamh, LOG_ERR, "Unable to open [%s]: %m", file_name); + pam_overwrite_n(text, text_length); + free(text); + return PAM_AUTH_ERR; + } + + if (fchown(fd, owner, group) == -1) { + pam_syslog(pamh, LOG_ERR, "Unable to change ownership [%s]: %m", file_name); + pam_overwrite_n(text, text_length); + free(text); + close(fd); + return PAM_AUTH_ERR; + } + + bytes_written = pam_modutil_write(fd, text, text_length); + close(fd); + + if (bytes_written < 0 || (size_t)bytes_written < text_length) { + pam_syslog(pamh, LOG_ERR, "Short write on %s", file_name); + pam_overwrite_n(text, text_length); + free(text); + return PAM_AUTH_ERR; + } + + return PAM_SUCCESS; +} + +static int +PAM_NONNULL((1, 2, 3)) +key_management(pam_handle_t *pamh, const char *file_name, char **text, + size_t text_length, uid_t owner, gid_t group) +{ + int fd = 0; + + fd = open(file_name, O_RDONLY | O_NOFOLLOW); + if (fd == -1) { + if (errno == ENOENT) { + if (generate_key(pamh, text, text_length)) { + pam_syslog(pamh, LOG_ERR, "Unable to generate key"); + return PAM_AUTH_ERR; + } + + if (write_file(pamh, file_name, *text, text_length, owner, group)) { + pam_syslog(pamh, LOG_ERR, "Unable to write key"); + return PAM_AUTH_ERR; + } + } else { + pam_syslog(pamh, LOG_ERR, "Unable to open %s: %m", file_name); + return PAM_AUTH_ERR; + } + } else { + if (read_file(pamh, fd, text, &text_length)) { + pam_syslog(pamh, LOG_ERR, "Error reading key file %s\n", file_name); + return PAM_AUTH_ERR; + } + } + + return PAM_SUCCESS; +} + +static int +hmac_management(pam_handle_t *pamh, int debug, void **out, size_t *out_length, + char *key, size_t key_length, + const void *text, size_t text_length) +{ + int ret = PAM_AUTH_ERR; + EVP_MAC *evp_mac = NULL; + EVP_MAC_CTX *ctx = NULL; + unsigned char *hmac_message = NULL; + size_t hmac_length; + char *algo = NULL; + OSSL_PARAM subalg_param[] = { OSSL_PARAM_END, OSSL_PARAM_END }; + + algo = get_crypto_algorithm(pamh, debug); + + subalg_param[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + algo, + 0); + + evp_mac = EVP_MAC_fetch(NULL, "HMAC", NULL); + if (evp_mac == NULL) { + pam_syslog(pamh, LOG_ERR, "Unable to create hmac implementation"); + goto done; + } + + ctx = EVP_MAC_CTX_new(evp_mac); + if (ctx == NULL) { + pam_syslog(pamh, LOG_ERR, "Unable to create hmac context"); + goto done; + } + + ret = EVP_MAC_init(ctx, (const unsigned char *)key, key_length, subalg_param); + if (ret == 0) { + pam_syslog(pamh, LOG_ERR, "Unable to initialize hmac context"); + goto done; + } + + ret = EVP_MAC_update(ctx, (const unsigned char *)text, text_length); + if (ret == 0) { + pam_syslog(pamh, LOG_ERR, "Unable to update hmac context"); + goto done; + } + + hmac_message = malloc(sizeof(unsigned char) * MAX_HMAC_LENGTH); + if (!hmac_message) { + pam_syslog(pamh, LOG_CRIT, "Not enough memory"); + goto done; + } + + ret = EVP_MAC_final(ctx, hmac_message, &hmac_length, MAX_HMAC_LENGTH); + if (ret == 0) { + pam_syslog(pamh, LOG_ERR, "Unable to calculate hmac message"); + goto done; + } + + *out_length = hmac_length; + *out = malloc(*out_length); + if (*out == NULL) { + pam_syslog(pamh, LOG_CRIT, "Not enough memory"); + goto done; + } + + memcpy(*out, hmac_message, *out_length); + ret = PAM_SUCCESS; + +done: + free(hmac_message); + if (key != NULL) { + pam_overwrite_n(key, key_length); + free(key); + } + if (ctx != NULL) { + EVP_MAC_CTX_free(ctx); + } + if (evp_mac != NULL) { + EVP_MAC_free(evp_mac); + } + free(algo); + + return ret; +} + +int +hmac_size(pam_handle_t *pamh, int debug, size_t *hmac_length) +{ + int ret = PAM_AUTH_ERR; + EVP_MAC *evp_mac = NULL; + EVP_MAC_CTX *ctx = NULL; + const unsigned char key[] = "ThisIsJustAKey"; + size_t key_length = MAX_KEY_LENGTH; + char *algo = NULL; + OSSL_PARAM subalg_param[] = { OSSL_PARAM_END, OSSL_PARAM_END }; + + algo = get_crypto_algorithm(pamh, debug); + + subalg_param[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + algo, + 0); + + evp_mac = EVP_MAC_fetch(NULL, "HMAC", NULL); + if (evp_mac == NULL) { + pam_syslog(pamh, LOG_ERR, "Unable to create hmac implementation"); + goto done; + } + + ctx = EVP_MAC_CTX_new(evp_mac); + if (ctx == NULL) { + pam_syslog(pamh, LOG_ERR, "Unable to create hmac context"); + goto done; + } + + ret = EVP_MAC_init(ctx, key, key_length, subalg_param); + if (ret == 0) { + pam_syslog(pamh, LOG_ERR, "Unable to initialize hmac context"); + goto done; + } + + *hmac_length = EVP_MAC_CTX_get_mac_size(ctx); + ret = PAM_SUCCESS; + +done: + if (ctx != NULL) { + EVP_MAC_CTX_free(ctx); + } + if (evp_mac != NULL) { + EVP_MAC_free(evp_mac); + } + free(algo); + + return ret; +} + +int +hmac_generate(pam_handle_t *pamh, int debug, void **mac, size_t *mac_length, + const char *key_file, uid_t owner, gid_t group, + const void *text, size_t text_length) +{ + char *key = NULL; + size_t key_length = MAX_KEY_LENGTH; + + if (key_management(pamh, key_file, &key, key_length, owner, group)) { + return PAM_AUTH_ERR; + } + + if (hmac_management(pamh, debug, mac, mac_length, key, key_length, + text, text_length)) { + return PAM_AUTH_ERR; + } + + return PAM_SUCCESS; +} + +#endif /* WITH_OPENSSL */ diff --git a/modules/pam_timestamp/hmac_openssl_wrapper.h b/modules/pam_timestamp/hmac_openssl_wrapper.h new file mode 100644 index 0000000..cc27c81 --- /dev/null +++ b/modules/pam_timestamp/hmac_openssl_wrapper.h @@ -0,0 +1,57 @@ +/* Wrapper for hmac openssl implementation. + * + * Copyright (c) 2021 Red Hat, Inc. + * Written by Iker Pedrosa + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef PAM_TIMESTAMP_HMAC_OPENSSL_WRAPPER_H +#define PAM_TIMESTAMP_HMAC_OPENSSL_WRAPPER_H + +#include "config.h" + +#ifdef WITH_OPENSSL + +#include +#include + +int +hmac_size(pam_handle_t *pamh, int debug, size_t *hmac_length); + +int +hmac_generate(pam_handle_t *pamh, int debug, void **mac, size_t *mac_length, + const char *key_file, uid_t owner, gid_t group, + const void *text, size_t text_length); + +#endif /* WITH_OPENSSL */ +#endif /* PAM_TIMESTAMP_HMAC_OPENSSL_WRAPPER_H */ diff --git a/modules/pam_timestamp/hmacfile.c b/modules/pam_timestamp/hmacfile.c new file mode 100644 index 0000000..371f814 --- /dev/null +++ b/modules/pam_timestamp/hmacfile.c @@ -0,0 +1,163 @@ +/* + * Copyright 2003,2004 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pam_inline.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "hmacsha1.h" + +static void +testvectors(void) +{ + void *hmac; + size_t hmac_len; + size_t i, j; + char hex[3]; + struct vector { + const char *key; + int key_len; + const char *data; + int data_len; + const char *hmac; + } vectors[] = { + { + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 20, + "Hi There", 8, + "b617318655057264e28bc0b6fb378c8ef146be00", + }, + +#ifdef HMAC_ALLOW_SHORT_KEYS + { + "Jefe", 4, + "what do ya want for nothing?", 28, + "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", + }, +#endif + + { + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 20, + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", 50, + "125d7342b9ac11cd91a39af48aa17b4f63f175d3", + }, + + { + "\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", 25, + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + 50, + "4c9007f4026250c6bc8414f9bf50c86c2d7235da", + }, + + { + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", 20, + "Test With Truncation", 20, + "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04", + }, + + { + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + 80, + "Test Using Larger Than Block-Size Key - Hash Key First", 54, + "aa4ae5e15272d00e95705637ce8a3b55ed402112", + }, + + { + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + 80, + "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 73, + "e8e99d0f45237d786d6bbaa7965c7808bbff1a91", + }, + }; + for (i = 0; i < PAM_ARRAY_SIZE(vectors); i++) { + hmac = NULL; + hmac_len = 0; + hmac_sha1_generate(&hmac, &hmac_len, + vectors[i].key, vectors[i].key_len, + vectors[i].data, vectors[i].data_len); + if (hmac != NULL) { + unsigned char *hmacc = hmac; + for (j = 0; j < hmac_len; j++) { + snprintf(hex, sizeof(hex), "%02x", + hmacc[j] & 0xff); + if (strncasecmp(hex, + vectors[i].hmac + 2 * j, + 2) != 0) { + printf("Incorrect result for vector %lu\n", + (unsigned long) i + 1); + exit(1); + + } + } + free(hmac); + } else { + printf("Error in vector %lu.\n", + (unsigned long) i + 1); + exit(1); + } + } +} + +int +main(int argc, char **argv) +{ + void *hmac; + size_t maclen; + const char *keyfile; + int i; + size_t j; + + testvectors(); + + keyfile = argv[1]; + for (i = 2; i < argc; i++) { + hmac_sha1_generate_file(NULL, &hmac, &maclen, keyfile, -1, -1, + argv[i], strlen(argv[i])); + if (hmac != NULL) { + unsigned char *hmacc = hmac; + for (j = 0; j < maclen; j++) { + printf("%02x", hmacc[j] & 0xff); + } + printf(" %s\n", argv[i]); + free(hmac); + } + } + return 0; +} diff --git a/modules/pam_timestamp/hmacsha1.c b/modules/pam_timestamp/hmacsha1.c new file mode 100644 index 0000000..384ccde --- /dev/null +++ b/modules/pam_timestamp/hmacsha1.c @@ -0,0 +1,295 @@ +/* An implementation of HMAC using SHA-1. + * + * Copyright (c) 2003 Red Hat, Inc. + * Written by Nalin Dahyabhai + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* See RFC 2104 for descriptions. */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pam_inline.h" +#include "hmacsha1.h" +#include "sha1.h" + +#define MINIMUM_KEY_SIZE SHA1_OUTPUT_SIZE +#define MAXIMUM_KEY_SIZE SHA1_BLOCK_SIZE + +static void +hmac_key_create(pam_handle_t *pamh, const char *filename, size_t key_size, + uid_t owner, gid_t group) +{ + int randfd, keyfd, i; + size_t count; + char *key; + + /* Open the destination file. */ + keyfd = open(filename, + O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, + S_IRUSR | S_IWUSR); + if (keyfd == -1) { + pam_syslog(pamh, LOG_ERR, "Cannot create %s: %m", filename); + return; + } + + + if (fchown(keyfd, owner, group) == -1) { + pam_syslog(pamh, LOG_ERR, "Cannot chown %s: %m", filename); + close(keyfd); + return; + } + + /* Open the random device to get key data. */ + randfd = open("/dev/urandom", O_RDONLY); + if (randfd == -1) { + pam_syslog(pamh, LOG_ERR, "Cannot open /dev/urandom: %m"); + close(keyfd); + return; + } + + /* Read random data for use as the key. */ + key = malloc(key_size); + count = 0; + if (!key) { + close(keyfd); + close(randfd); + return; + } + while (count < key_size) { + i = read(randfd, key + count, key_size - count); + if ((i == 0) || (i == -1)) { + break; + } + count += i; + } + + close(randfd); + + /* If we didn't get enough, stop here. */ + if (count < key_size) { + pam_syslog(pamh, LOG_ERR, "Short read on random device"); + pam_overwrite_n(key, key_size); + free(key); + close(keyfd); + return; + } + + /* Now write the key. */ + count = 0; + while (count < key_size) { + i = write(keyfd, key + count, key_size - count); + if ((i == 0) || (i == -1)) { + break; + } + count += i; + } + pam_overwrite_n(key, key_size); + free(key); + close(keyfd); +} + +static void +hmac_key_read(pam_handle_t *pamh, const char *filename, size_t default_key_size, + uid_t owner, gid_t group, + void **key, size_t *key_size) +{ + char *tmp; + int keyfd, i, count; + struct stat st; + + tmp = NULL; + *key = NULL; + *key_size = 0; + + /* Try to open the key file. */ + keyfd = open(filename, O_RDONLY); + if (keyfd == -1) { + /* No such thing? Create it. */ + if (errno == ENOENT) { + hmac_key_create(pamh, filename, default_key_size, + owner, group); + keyfd = open(filename, O_RDONLY); + } else { + pam_syslog(pamh, LOG_ERR, "Cannot open %s: %m", filename); + } + if (keyfd == -1) + return; + } + + /* If we failed to open the file, we're done. */ + if (fstat(keyfd, &st) == -1) { + close(keyfd); + return; + } + + /* Read the contents of the file. */ + tmp = malloc(st.st_size); + if (!tmp) { + close(keyfd); + return; + } + + count = 0; + while (count < st.st_size) { + i = read(keyfd, tmp + count, st.st_size - count); + if ((i == 0) || (i == -1)) { + break; + } + count += i; + } + close(keyfd); + + /* Require that we got the expected amount of data. */ + if (count < st.st_size) { + pam_overwrite_n(tmp, st.st_size); + free(tmp); + return; + } + + /* Pass the key back. */ + *key = tmp; + *key_size = st.st_size; +} + +static void +xor_block(unsigned char *p, unsigned char byte, size_t length) +{ + size_t i; + for (i = 0; i < length; i++) { + p[i] = p[i] ^ byte; + } +} + +void +hmac_sha1_generate(void **mac, size_t *mac_length, + const void *raw_key, size_t raw_key_size, + const void *text, size_t text_length) +{ + unsigned char key[MAXIMUM_KEY_SIZE] = {}, tmp_key[MAXIMUM_KEY_SIZE]; + size_t maximum_key_size = SHA1_BLOCK_SIZE, + minimum_key_size = SHA1_OUTPUT_SIZE; + const unsigned char ipad = 0x36, opad = 0x5c; + struct sha1_context sha1; + unsigned char inner[SHA1_OUTPUT_SIZE], outer[SHA1_OUTPUT_SIZE]; + + *mac = NULL; + *mac_length = 0; + +#ifndef HMAC_ALLOW_SHORT_KEYS + /* If the key is too short, don't bother. */ + if (raw_key_size < minimum_key_size) { + return; + } +#endif + + /* If the key is too long, "compress" it, else copy it and pad it + * out with zero bytes. */ + if (raw_key_size > maximum_key_size) { + sha1_init(&sha1); + sha1_update(&sha1, raw_key, raw_key_size); + sha1_output(&sha1, key); + } else { + memmove(key, raw_key, raw_key_size); + } + + /* Generate the inner sum. */ + memcpy(tmp_key, key, sizeof(tmp_key)); + xor_block(tmp_key, ipad, sizeof(tmp_key)); + + sha1_init(&sha1); + sha1_update(&sha1, tmp_key, sizeof(tmp_key)); + sha1_update(&sha1, text, text_length); + sha1_output(&sha1, inner); + + /* Generate the outer sum. */ + memcpy(tmp_key, key, sizeof(tmp_key)); + xor_block(tmp_key, opad, sizeof(tmp_key)); + + sha1_init(&sha1); + sha1_update(&sha1, tmp_key, sizeof(tmp_key)); + sha1_update(&sha1, inner, sizeof(inner)); + sha1_output(&sha1, outer); + + /* We don't need any of the keys any more. */ + pam_overwrite_array(key); + pam_overwrite_array(tmp_key); + + /* Allocate space to store the output. */ + *mac_length = sizeof(outer); + *mac = malloc(*mac_length); + if (*mac == NULL) { + *mac_length = 0; + return; + } + + memcpy(*mac, outer, *mac_length); +} + +void +hmac_sha1_generate_file(pam_handle_t *pamh, void **mac, size_t *mac_length, + const char *keyfile, uid_t owner, gid_t group, + const void *text, size_t text_length) +{ + void *key; + size_t key_length; + + hmac_key_read(pamh, keyfile, + MAXIMUM_KEY_SIZE, owner, group, + &key, &key_length); + if (key == NULL) { + *mac = NULL; + *mac_length = 0; + return; + } + hmac_sha1_generate(mac, mac_length, + key, key_length, + text, text_length); + pam_overwrite_n(key, key_length); + free(key); +} + +size_t +hmac_sha1_size(void) +{ + return SHA1_OUTPUT_SIZE; +} diff --git a/modules/pam_timestamp/hmacsha1.h b/modules/pam_timestamp/hmacsha1.h new file mode 100644 index 0000000..200d1d0 --- /dev/null +++ b/modules/pam_timestamp/hmacsha1.h @@ -0,0 +1,15 @@ +#ifndef pam_timestamp_hmacfile_h +#define pam_timestamp_hmacfile_h + +#include +#include + +size_t hmac_sha1_size(void); +void hmac_sha1_generate(void **mac, size_t *mac_length, + const void *key, size_t key_length, + const void *text, size_t text_length); +void hmac_sha1_generate_file(pam_handle_t *pamh, void **mac, size_t *mac_length, + const char *keyfile, uid_t owner, gid_t group, + const void *text, size_t text_length); + +#endif diff --git a/modules/pam_timestamp/meson.build b/modules/pam_timestamp/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_timestamp/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_timestamp/pam_timestamp.8.xml b/modules/pam_timestamp/pam_timestamp.8.xml new file mode 100644 index 0000000..a763ad8 --- /dev/null +++ b/modules/pam_timestamp/pam_timestamp.8.xml @@ -0,0 +1,205 @@ + + + + pam_timestamp + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_timestamp + Authenticate using cached successful authentication attempts + + + + + pam_timestamp.so + + timestampdir=directory + + + timestamp_timeout=number + + + verbose + + + debug + + + + + + + DESCRIPTION + + + In a nutshell, pam_timestamp caches successful +authentication attempts, and allows you to use a recent successful attempt as +the basis for authentication. This is similar mechanism which is used in +sudo. + + + When an application opens a session using pam_timestamp, +a timestamp file is created in the timestampdir directory +for the user. When an application attempts to authenticate the user, a +pam_timestamp will treat a sufficiently recent timestamp +file as grounds for succeeding. + + + The default encryption hash is taken from the + HMAC_CRYPTO_ALGO variable from + /etc/login.defs. + + + + + + OPTIONS + + + + timestampdir=directory + + + + Specify an alternate directory where + pam_timestamp creates timestamp files. + + + + + + timestamp_timeout=number + + + + How long should pam_timestamp + treat timestamp as valid after their + last modification date (in seconds). Default is 300 seconds. + + + + + + verbose + + + + Attempt to inform the user when access is granted. + + + + + + debug + + + + Turns on debugging messages sent to + syslog3 + . + + + + + + + + MODULE TYPES PROVIDED + + The and + module types are provided. + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + The module was not able to retrieve the user name or + no valid timestamp file was found. + + + + + PAM_SUCCESS + + + Everything was successful. + + + + + PAM_SESSION_ERR + + + Timestamp file could not be created or updated. + + + + + + + + NOTES + + Users can get confused when they are not always asked for passwords when +running a given program. Some users reflexively begin typing information before +noticing that it is not being asked for. + + + + + EXAMPLES + +auth sufficient pam_timestamp.so verbose +auth required pam_unix.so + +session required pam_unix.so +session optional pam_timestamp.so + + + + + FILES + + + /var/run/pam_timestamp/... + + timestamp files and directories + + + + + + + SEE ALSO + + + pam_timestamp_check8 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_timestamp was written by Nalin Dahyabhai. + + + + \ No newline at end of file diff --git a/modules/pam_timestamp/pam_timestamp.c b/modules/pam_timestamp/pam_timestamp.c new file mode 100644 index 0000000..51fcd59 --- /dev/null +++ b/modules/pam_timestamp/pam_timestamp.c @@ -0,0 +1,915 @@ +/****************************************************************************** + * A module for Linux-PAM that will cache authentication results, inspired by + * (and implemented with an eye toward being mixable with) sudo. + * + * Copyright (c) 2002 Red Hat, Inc. + * Written by Nalin Dahyabhai + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WITH_OPENSSL +#include "hmac_openssl_wrapper.h" +#else +#include "hmacsha1.h" +#endif /* WITH_OPENSSL */ + +#ifdef USE_LOGIND +#include +#else +#include +#endif + +#include +#include +#include +#include +#include "pam_inline.h" +#include "pam_i18n.h" + +/* The default timeout we use is 5 minutes, which matches the sudo default + * for the timestamp_timeout parameter. */ +#define DEFAULT_TIMESTAMP_TIMEOUT (5 * 60) +#define MODULE "pam_timestamp" +#define TIMESTAMPDIR _PATH_VARRUN MODULE +#define TIMESTAMPKEY TIMESTAMPDIR "/_pam_timestamp_key" + +/* Various buffers we use need to be at least as large as either PATH_MAX or + * LINE_MAX, so choose the larger of the two. */ +#ifndef PATH_MAX +#define BUFLEN LINE_MAX +#elif (LINE_MAX > PATH_MAX) +#define BUFLEN LINE_MAX +#else +#define BUFLEN PATH_MAX +#endif + +#define ROOT_USER 0 +#define ROOT_GROUP 0 + +/* Return PAM_SUCCESS if the given directory looks "safe". */ +static int +check_dir_perms(pam_handle_t *pamh, const char *tdir) +{ + char scratch[BUFLEN] = {}; + struct stat st; + size_t i; + /* Check that the directory is "safe". */ + if ((tdir == NULL) || (strlen(tdir) == 0)) { + return PAM_AUTH_ERR; + } + /* Iterate over the path, checking intermediate directories. */ + for (i = 0; (i < sizeof(scratch)) && (tdir[i] != '\0'); i++) { + scratch[i] = tdir[i]; + if ((scratch[i] == '/') || (tdir[i + 1] == '\0')) { + /* We now have the name of a directory in the path, so + * we need to check it. */ + if ((lstat(scratch, &st) == -1) && (errno != ENOENT)) { + pam_syslog(pamh, LOG_ERR, + "unable to read `%s': %m", + scratch); + return PAM_AUTH_ERR; + } + if (!S_ISDIR(st.st_mode)) { + pam_syslog(pamh, LOG_ERR, + "`%s' is not a directory", + scratch); + return PAM_AUTH_ERR; + } + if (S_ISLNK(st.st_mode)) { + pam_syslog(pamh, LOG_ERR, + "`%s' is a symbolic link", + scratch); + return PAM_AUTH_ERR; + } + if (st.st_uid != 0) { + pam_syslog(pamh, LOG_ERR, + "`%s' owner UID != 0", + scratch); + return PAM_AUTH_ERR; + } + if (st.st_gid != 0) { + pam_syslog(pamh, LOG_ERR, + "`%s' owner GID != 0", + scratch); + return PAM_AUTH_ERR; + } + if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) { + pam_syslog(pamh, LOG_ERR, + "`%s' permissions are lax", + scratch); + return PAM_AUTH_ERR; + } + } + } + return PAM_SUCCESS; +} + +/* Validate a tty pathname as actually belonging to a tty, and return its base + * name if it's valid. */ +static const char * +check_tty(const char *tty) +{ + /* Check that we're not being set up to take a fall. */ + if ((tty == NULL) || (strlen(tty) == 0)) { + return NULL; + } + /* Pull out the meaningful part of the tty's name. */ + if (strchr(tty, '/') != NULL) { + if (pam_str_skip_prefix(tty, "/dev/") == NULL) { + /* Make sure the device node is actually in /dev/, + * noted by Michal Zalewski. */ + return NULL; + } + tty = strrchr(tty, '/') + 1; + } + /* Make sure the tty wasn't actually a directory (no basename). */ + if (!strlen(tty) || !strcmp(tty, ".") || !strcmp(tty, "..")) { + return NULL; + } + return tty; +} + +/* Determine the right path name for a given user's timestamp. */ +static int +format_timestamp_name(char *path, size_t len, + const char *timestamp_dir, + const char *tty, + const char *ruser, + const char *user) +{ + if (strcmp(ruser, user) == 0) { + return snprintf(path, len, "%s/%s/%s", timestamp_dir, + ruser, tty); + } else { + return snprintf(path, len, "%s/%s/%s:%s", timestamp_dir, + ruser, tty, user); + } +} + +/* Check if a given timestamp date, when compared to a current time, fits + * within the given interval. */ +static int +timestamp_good(time_t then, time_t now, time_t interval) +{ + if (((now >= then) && ((now - then) < interval)) || + ((now < then) && ((then - now) < (2 * interval)))) { + return PAM_SUCCESS; + } + return PAM_AUTH_ERR; +} + +static int +check_login_time( +#ifdef USE_LOGIND + uid_t uid, +#else + const char *ruser, +#endif + time_t timestamp) +{ + time_t oldest_login = 0; +#ifdef USE_LOGIND +#define USEC_PER_SEC ((uint64_t) 1000000ULL) + uint64_t usec = 0; + + if (sd_uid_get_login_time(uid, &usec) < 0) { + return PAM_SERVICE_ERR; + } + + oldest_login = usec/USEC_PER_SEC; +#else + struct utmp utbuf, *ut; + + setutent(); + while( +#ifdef HAVE_GETUTENT_R + !getutent_r(&utbuf, &ut) +#else + (ut = getutent()) != NULL +#endif + ) { + if (ut->ut_type != USER_PROCESS) { + continue; + } + if (strncmp(ruser, ut->ut_user, sizeof(ut->ut_user)) != 0) { + continue; + } + if (oldest_login == 0 || oldest_login > ut->ut_tv.tv_sec) { + oldest_login = ut->ut_tv.tv_sec; + } + } + endutent(); +#endif + if(oldest_login == 0 || timestamp < oldest_login) { + return PAM_AUTH_ERR; + } + return PAM_SUCCESS; +} + +#ifndef PAM_TIMESTAMP_MAIN +static int +get_ruser(pam_handle_t *pamh, char *ruserbuf, size_t ruserbuflen) +{ + const void *ruser; + struct passwd *pwd; + + if (ruserbuf == NULL || ruserbuflen < 1) + return -2; + /* Get the name of the source user. */ + if (pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS) { + ruser = NULL; + } + if ((ruser == NULL) || (strlen(ruser) == 0)) { + /* Barring that, use the current RUID. */ + pwd = pam_modutil_getpwuid(pamh, getuid()); + if (pwd != NULL) { + ruser = pwd->pw_name; + } + } else { + /* + * This ruser is used by format_timestamp_name as a component + * of constructed timestamp pathname, so ".", "..", and '/' + * are disallowed to avoid potential path traversal issues. + */ + if (!strcmp(ruser, ".") || + !strcmp(ruser, "..") || + strchr(ruser, '/')) { + ruser = NULL; + } + } + if (ruser == NULL || strlen(ruser) >= ruserbuflen) { + *ruserbuf = '\0'; + return -1; + } + strcpy(ruserbuf, ruser); + return 0; +} + +/* Get the path to the timestamp to use. */ +static int +get_timestamp_name(pam_handle_t *pamh, int argc, const char **argv, + char *path, size_t len) +{ + const char *user, *tty; + const void *void_tty; + const char *tdir = TIMESTAMPDIR; + char ruser[BUFLEN]; + int i, debug = 0; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } + } + for (i = 0; i < argc; i++) { + const char *str; + + if ((str = pam_str_skip_prefix(argv[i], "timestampdir=")) != NULL) { + tdir = str; + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "storing timestamps in `%s'", + tdir); + } + } + } + i = check_dir_perms(pamh, tdir); + if (i != PAM_SUCCESS) { + return i; + } + /* Get the name of the target user. */ + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user[0] == '\0') { + return PAM_AUTH_ERR; + } + if (debug) { + pam_syslog(pamh, LOG_DEBUG, "becoming user `%s'", user); + } + /* Get the name of the source user. */ + if (get_ruser(pamh, ruser, sizeof(ruser)) || strlen(ruser) == 0) { + return PAM_AUTH_ERR; + } + if (debug) { + pam_syslog(pamh, LOG_DEBUG, "currently user `%s'", ruser); + } + /* Get the name of the terminal. */ + if (pam_get_item(pamh, PAM_TTY, &void_tty) != PAM_SUCCESS) { + tty = NULL; + } else { + tty = void_tty; + } + if ((tty == NULL) || (strlen(tty) == 0)) { + tty = ttyname(STDIN_FILENO); + if ((tty == NULL) || (strlen(tty) == 0)) { + tty = ttyname(STDOUT_FILENO); + } + if ((tty == NULL) || (strlen(tty) == 0)) { + tty = ttyname(STDERR_FILENO); + } + if ((tty == NULL) || (strlen(tty) == 0)) { + /* Match sudo's behavior for this case. */ + tty = "unknown"; + } + } + if (debug) { + pam_syslog(pamh, LOG_DEBUG, "tty is `%s'", tty); + } + /* Snip off all but the last part of the tty name. */ + tty = check_tty(tty); + if (tty == NULL) { + return PAM_AUTH_ERR; + } + /* Generate the name of the file used to cache auth results. These + * paths should jive with sudo's per-tty naming scheme. */ + if (format_timestamp_name(path, len, tdir, tty, ruser, user) >= (int)len) { + return PAM_AUTH_ERR; + } + if (debug) { + pam_syslog(pamh, LOG_DEBUG, "using timestamp file `%s'", path); + } + return PAM_SUCCESS; +} + +/* Tell the user that access has been granted. */ +static void +verbose_success(pam_handle_t *pamh, long diff) +{ + pam_info(pamh, _("Access has been granted" + " (last access was %ld seconds ago)."), diff); +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + struct stat st; + time_t interval = DEFAULT_TIMESTAMP_TIMEOUT; + int i, fd, debug = 0, verbose = 0; + char path[BUFLEN], *p, *message, *message_end; + long tmp; + const void *void_service; + const char *service; + time_t now, then; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } + } + for (i = 0; i < argc; i++) { + const char *str; + + if ((str = pam_str_skip_prefix(argv[i], "timestamp_timeout=")) != NULL) { + tmp = strtol(str, &p, 0); + if ((p != NULL) && (*p == '\0')) { + interval = tmp; + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "setting timeout to %ld" + " seconds", (long)interval); + } + } + } else + if (strcmp(argv[i], "verbose") == 0) { + verbose = 1; + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "becoming more verbose"); + } + } + } + + if (flags & PAM_SILENT) { + verbose = 0; + } + + /* Get the name of the timestamp file. */ + if (get_timestamp_name(pamh, argc, argv, + path, sizeof(path)) != PAM_SUCCESS) { + return PAM_AUTH_ERR; + } + + /* Get the name of the service. */ + if (pam_get_item(pamh, PAM_SERVICE, &void_service) != PAM_SUCCESS) { + service = NULL; + } else { + service = void_service; + } + if ((service == NULL) || (strlen(service) == 0)) { + service = "(unknown)"; + } + + /* Open the timestamp file. */ + fd = open(path, O_RDONLY | O_NOFOLLOW); + if (fd == -1) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "cannot open timestamp `%s': %m", + path); + } + return PAM_AUTH_ERR; + } + + if (fstat(fd, &st) == 0) { + int count; + void *mac; + size_t maclen; + char ruser[BUFLEN]; + + /* Check that the file is owned by the superuser. */ + if ((st.st_uid != 0) || (st.st_gid != 0)) { + pam_syslog(pamh, LOG_ERR, "timestamp file `%s' is " + "not owned by root", path); + close(fd); + return PAM_AUTH_ERR; + } + + /* Check that the file is a normal file. */ + if (!(S_ISREG(st.st_mode))) { + pam_syslog(pamh, LOG_ERR, "timestamp file `%s' is " + "not a regular file", path); + close(fd); + return PAM_AUTH_ERR; + } + +#ifdef WITH_OPENSSL + if (hmac_size(pamh, debug, &maclen)) { + close(fd); + return PAM_AUTH_ERR; + } +#else + maclen = hmac_sha1_size(); +#endif /* WITH_OPENSSL */ + /* Check that the file is the expected size. */ + if (st.st_size == 0) { + /* Invalid, but may have been created by sudo. */ + close(fd); + return PAM_AUTH_ERR; + } + if (st.st_size != + (off_t)(strlen(path) + 1 + sizeof(then) + maclen)) { + pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' " + "appears to be corrupted", path); + close(fd); + return PAM_AUTH_ERR; + } + + /* Read the file contents. */ + message = malloc(st.st_size); + count = 0; + if (!message) { + close(fd); + return PAM_BUF_ERR; + } + while (count < st.st_size) { + i = read(fd, message + count, st.st_size - count); + if ((i == 0) || (i == -1)) { + break; + } + count += i; + } + if (count < st.st_size) { + pam_syslog(pamh, LOG_NOTICE, "error reading timestamp " + "file `%s': %m", path); + close(fd); + free(message); + return PAM_AUTH_ERR; + } + message_end = message + strlen(path) + 1 + sizeof(then); + + /* Regenerate the MAC. */ +#ifdef WITH_OPENSSL + if (hmac_generate(pamh, debug, &mac, &maclen, TIMESTAMPKEY, + ROOT_USER, ROOT_GROUP, message, message_end - message)) { + close(fd); + free(message); + return PAM_AUTH_ERR; + } +#else + hmac_sha1_generate_file(pamh, &mac, &maclen, TIMESTAMPKEY, + ROOT_USER, ROOT_GROUP, message, message_end - message); +#endif /* WITH_OPENSSL */ + if ((mac == NULL) || + (memcmp(path, message, strlen(path)) != 0) || + (memcmp(mac, message_end, maclen) != 0)) { + pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is " + "corrupted", path); + close(fd); + free(mac); + free(message); + return PAM_AUTH_ERR; + } + free(mac); + memmove(&then, message + strlen(path) + 1, sizeof(then)); + free(message); + + /* Check oldest login against timestamp */ + if (get_ruser(pamh, ruser, sizeof(ruser))) + { + close(fd); + return PAM_AUTH_ERR; + } +#ifdef USE_LOGIND + struct passwd *pwd = pam_modutil_getpwnam(pamh, ruser); + if (pwd == NULL) { + return PAM_SERVICE_ERR; + } + if (check_login_time(pwd->pw_uid, then) != PAM_SUCCESS) +#else + if (check_login_time(ruser, then) != PAM_SUCCESS) +#endif + { + pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is " + "older than oldest login, disallowing " + "access to %s for user %s", + path, service, ruser); + close(fd); + return PAM_AUTH_ERR; + } + + /* Compare the dates. */ + now = time(NULL); + if (timestamp_good(then, now, interval) == PAM_SUCCESS) { + close(fd); + pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' is " + "only %ld seconds old, allowing access to %s " + "for user %s", path, (long) (now - st.st_mtime), + service, ruser); + if (verbose) { + verbose_success(pamh, now - st.st_mtime); + } + return PAM_SUCCESS; + } else { + close(fd); + pam_syslog(pamh, LOG_NOTICE, "timestamp file `%s' has " + "unacceptable age (%ld seconds), disallowing " + "access to %s for user %s", + path, (long) (now - st.st_mtime), + service, ruser); + return PAM_AUTH_ERR; + } + } + close(fd); + + /* Fail by default. */ + return PAM_AUTH_ERR; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, int argc, const char **argv) +{ + char path[BUFLEN], subdir[BUFLEN], *text, *p; + void *mac; + size_t maclen; + time_t now; + int fd, i, debug = 0; + + /* Parse arguments. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + } + } + + /* Get the name of the timestamp file. */ + if (get_timestamp_name(pamh, argc, argv, + path, sizeof(path)) != PAM_SUCCESS) { + return PAM_SESSION_ERR; + } + + /* Create the directory for the timestamp file if it doesn't already + * exist. */ + for (i = 1; i < (int) sizeof(path) && path[i] != '\0'; i++) { + if (path[i] == '/') { + /* Attempt to create the directory. */ + memcpy(subdir, path, i); + subdir[i] = '\0'; + if (mkdir(subdir, 0700) == 0) { + /* Attempt to set the owner to the superuser. */ + if (lchown(subdir, 0, 0) != 0) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "error setting permissions on `%s': %m", + subdir); + } + return PAM_SESSION_ERR; + } + } else { + if (errno != EEXIST) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "error creating directory `%s': %m", + subdir); + } + return PAM_SESSION_ERR; + } + } + } + } + +#ifdef WITH_OPENSSL + if (hmac_size(pamh, debug, &maclen)) { + return PAM_SESSION_ERR; + } +#else + maclen = hmac_sha1_size(); +#endif /* WITH_OPENSSL */ + + /* Generate the message. */ + text = malloc(strlen(path) + 1 + sizeof(now) + maclen); + if (text == NULL) { + pam_syslog(pamh, LOG_CRIT, "unable to allocate memory: %m"); + return PAM_SESSION_ERR; + } + p = text; + + strcpy(text, path); + p += strlen(path) + 1; + + now = time(NULL); + memmove(p, &now, sizeof(now)); + p += sizeof(now); + + /* Generate the MAC and append it to the plaintext. */ +#ifdef WITH_OPENSSL + if (hmac_generate(pamh, debug, &mac, &maclen, TIMESTAMPKEY, + ROOT_USER, ROOT_GROUP, text, p - text)) { + free(text); + return PAM_SESSION_ERR; + } +#else + hmac_sha1_generate_file(pamh, &mac, &maclen, TIMESTAMPKEY, + ROOT_USER, ROOT_GROUP, text, p - text); + if (mac == NULL) { + pam_syslog(pamh, LOG_ERR, "failure generating MAC: %m"); + free(text); + return PAM_SESSION_ERR; + } +#endif /* WITH_OPENSSL */ + memmove(p, mac, maclen); + p += maclen; + free(mac); + + /* Open the file. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd == -1) { + pam_syslog(pamh, LOG_ERR, "unable to open `%s': %m", path); + free(text); + return PAM_SESSION_ERR; + } + + /* Attempt to set the owner to the superuser. */ + if (fchown(fd, 0, 0) != 0) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "error setting ownership of `%s': %m", + path); + } + close(fd); + free(text); + return PAM_SESSION_ERR; + } + + + /* Write the timestamp to the file. */ + if (write(fd, text, p - text) != p - text) { + pam_syslog(pamh, LOG_ERR, "unable to write to `%s': %m", path); + close(fd); + free(text); + return PAM_SESSION_ERR; + } + + /* Close the file and return successfully. */ + close(fd); + free(text); + pam_syslog(pamh, LOG_DEBUG, "updated timestamp file `%s'", path); + return PAM_SUCCESS; +} + +int +pam_sm_close_session(pam_handle_t *pamh UNUSED, int flags UNUSED, int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +#else /* PAM_TIMESTAMP_MAIN */ + +#define USAGE "Usage: %s [[-k] | [-d]] [target user]\n" +#define CHECK_INTERVAL 7 + +int +main(int argc, char **argv) +{ + int i, retval, dflag = 0, kflag = 0; + const char *target_user = NULL, *user = NULL, *tty = NULL; + struct passwd *pwd; + struct timeval tv; + fd_set write_fds; + char path[BUFLEN]; + struct stat st; +#ifdef USE_LOGIND + uid_t uid; +#endif + + /* Check that there's nothing funny going on with stdio. */ + if ((fstat(STDIN_FILENO, &st) == -1) || + (fstat(STDOUT_FILENO, &st) == -1) || + (fstat(STDERR_FILENO, &st) == -1)) { + /* Appropriate the "no controlling tty" error code. */ + return 3; + } + + /* Parse arguments. */ + while ((i = getopt(argc, argv, "dk")) != -1) { + switch (i) { + case 'd': + dflag++; + break; + case 'k': + kflag++; + break; + default: + fprintf(stderr, USAGE, argv[0]); + return 1; + break; + } + } + + /* Bail if both -k and -d are given together. */ + if ((kflag + dflag) > 1) { + fprintf(stderr, USAGE, argv[0]); + return 1; + } + + /* Check that we're setuid. */ + if (geteuid() != 0) { + fprintf(stderr, "%s must be setuid root\n", + argv[0]); + return 2; + } + + /* Check that we have a controlling tty. */ + tty = ttyname(STDIN_FILENO); + if ((tty == NULL) || (strlen(tty) == 0)) { + tty = ttyname(STDOUT_FILENO); + } + if ((tty == NULL) || (strlen(tty) == 0)) { + tty = ttyname(STDERR_FILENO); + } + if ((tty == NULL) || (strlen(tty) == 0)) { + tty = "unknown"; + } + + /* Get the name of the invoking (requesting) user. */ + pwd = getpwuid(getuid()); + if (pwd == NULL) { + fprintf(stderr, "unknown user\n"); + return 4; + } +#ifdef USE_LOGIND + uid = pwd->pw_uid; +#endif + + /* Get the name of the target user. */ + user = strdup(pwd->pw_name); + if (user == NULL) { + fprintf(stderr, "out of memory\n"); + return 4; + } + target_user = (optind < argc) ? argv[optind] : user; + if ((strchr(target_user, '.') != NULL) || + (strchr(target_user, '/') != NULL) || + (strchr(target_user, '%') != NULL)) { + fprintf(stderr, "invalid user: %s\n", target_user); + return 4; + } + + /* Sanity check the tty to make sure we should be checking + * for timestamps which pertain to it. */ + tty = check_tty(tty); + if (tty == NULL) { + fprintf(stderr, "invalid tty\n"); + return 6; + } + + /* Generate the name of the timestamp file. */ + if (format_timestamp_name(path, sizeof(path), TIMESTAMPDIR, + tty, user, target_user) >= (int) sizeof(path)) { + fprintf(stderr, "path too long\n"); + return 4; + } + + do { + retval = 0; + do { + /* Sanity check the timestamp directory itself. */ + if (check_dir_perms(NULL, TIMESTAMPDIR) != PAM_SUCCESS) { + retval = 5; + break; + } + + + if (kflag) { + /* Remove the timestamp. */ + if (lstat(path, &st) != -1) { + retval = unlink(path); + } + } else { + /* Check the timestamp. */ + if (lstat(path, &st) != -1) { + /* Check oldest login against timestamp */ +#ifdef USE_LOGIND + if (check_login_time(uid, st.st_mtime) != PAM_SUCCESS) { +#else + if (check_login_time(user, st.st_mtime) != PAM_SUCCESS) { +#endif + retval = 7; + } else if (timestamp_good(st.st_mtime, time(NULL), + DEFAULT_TIMESTAMP_TIMEOUT) != PAM_SUCCESS) { + retval = 7; + } + } else { + retval = 7; + } + } + } while (0); + + if (dflag > 0) { + struct timeval now; + /* Send the would-be-returned value to our parent. */ + signal(SIGPIPE, SIG_DFL); + fprintf(stdout, "%d\n", retval); + fflush(stdout); + /* Wait. */ + gettimeofday(&now, NULL); + tv.tv_sec = CHECK_INTERVAL; + /* round the sleep time to get woken up on a whole second */ + tv.tv_usec = 1000000 - now.tv_usec; + if (now.tv_usec < 500000) + tv.tv_sec--; + FD_ZERO(&write_fds); + FD_SET(STDOUT_FILENO, &write_fds); + select(STDOUT_FILENO + 1, + NULL, NULL, &write_fds, + &tv); + } + } while (dflag > 0); + + return retval; +} + +#endif diff --git a/modules/pam_timestamp/pam_timestamp_check.8.xml b/modules/pam_timestamp/pam_timestamp_check.8.xml new file mode 100644 index 0000000..7f850ae --- /dev/null +++ b/modules/pam_timestamp/pam_timestamp_check.8.xml @@ -0,0 +1,204 @@ + + + + pam_timestamp_check + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_timestamp_check + Check to see if the default timestamp is valid + + + + + pam_timestamp_check + + -k + + + -d + + + target_user + + + + + + + DESCRIPTION + + + With no arguments pam_timestamp_check will check to +see if the default timestamp is valid, or optionally remove it. + + + + + + OPTIONS + + + + -k + + + + Instead of checking the validity of a timestamp, remove it. + This is analogous to sudo's -k option. + + + + + + -d + + + + Instead of returning validity using an exit status, + loop indefinitely, polling regularly and printing the status on + standard output. + + + + + + target_user + + + + By default pam_timestamp_check checks or removes + timestamps generated by pam_timestamp when + users authenticate as themselves. When the user authenticates as a + different user, the name of the timestamp file changes to + accommodate this. target_user allows + one to specify this user name. + + + + + + + + RETURN VALUES + + + 0 + + + The timestamp is valid. + + + + + 2 + + + The binary is not setuid root. + + + + + 3 + + + Invalid invocation. + + + + + 4 + + + User is unknown. + + + + + 5 + + + Permissions error. + + + + + 6 + + + Invalid controlling tty. + + + + + 7 + + + Timestamp is not valid. + + + + + + + + NOTES + + Users can get confused when they are not always asked for passwords when +running a given program. Some users reflexively begin typing information before +noticing that it is not being asked for. + + + + + EXAMPLES + +auth sufficient pam_timestamp.so verbose +auth required pam_unix.so + +session required pam_unix.so +session optional pam_timestamp.so + + + + + FILES + + + /var/run/sudo/... + + timestamp files and directories + + + + + + + SEE ALSO + + + pam_timestamp_check8 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_timestamp was written by Nalin Dahyabhai. + + + + diff --git a/modules/pam_timestamp/pam_timestamp_check.c b/modules/pam_timestamp/pam_timestamp_check.c new file mode 100644 index 0000000..52b5a95 --- /dev/null +++ b/modules/pam_timestamp/pam_timestamp_check.c @@ -0,0 +1,42 @@ +/****************************************************************************** + * A module for Linux-PAM that will cache authentication results, inspired by + * (and implemented with an eye toward being mixable with) sudo. + * + * Copyright (c) 2002 Red Hat, Inc. + * Written by Nalin Dahyabhai + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#define PAM_TIMESTAMP_MAIN 1 +#include "pam_timestamp.c" diff --git a/modules/pam_timestamp/sha1.c b/modules/pam_timestamp/sha1.c new file mode 100644 index 0000000..f21b287 --- /dev/null +++ b/modules/pam_timestamp/sha1.c @@ -0,0 +1,254 @@ +/* Yet another SHA-1 implementation. + * + * Copyright (c) 2003 Red Hat, Inc. + * Written by Nalin Dahyabhai + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* See http://www.itl.nist.gov/fipspubs/fip180-1.htm for descriptions. */ + +#include "pam_inline.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sha1.h" + +static const unsigned char +padding[SHA1_BLOCK_SIZE] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static uint32_t +F(uint32_t b, uint32_t c, uint32_t d) +{ + return (b & c) | ((~b) & d); +} + +static uint32_t +G(uint32_t b, uint32_t c, uint32_t d) +{ + return b ^ c ^ d; +} + +static uint32_t +H(uint32_t b, uint32_t c, uint32_t d) +{ + return (b & c) | (b & d) | (c & d); +} + +static uint32_t +RL(uint32_t n, uint32_t s) +{ + return (n << s) | (n >> (32 - s)); +} + +static uint32_t +sha1_round(uint32_t (*FUNC)(uint32_t, uint32_t, uint32_t), + uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, + uint32_t i, uint32_t n) +{ + return RL(a, 5) + FUNC(b, c, d) + e + i + n; +} + +void +sha1_init(struct sha1_context *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + ctx->e = 0xc3d2e1f0; +} + +static void +sha1_process(struct sha1_context *ctx, uint32_t buffer[SHA1_BLOCK_SIZE / 4]) +{ + uint32_t a, b, c, d, e, temp; + uint32_t data[80]; + int i; + + for (i = 0; i < 16; i++) { + data[i] = htonl(buffer[i]); + } + for (i = 16; i < 80; i++) { + data[i] = RL(data[i - 3] ^ data[i - 8] ^ data[i - 14] ^ data[i - 16], 1); + } + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + e = ctx->e; + + for (i = 0; i < 20; i++) { + temp = sha1_round(F, a, b, c, d, e, data[i], 0x5a827999); + e = d; d = c; c = RL(b, 30); b = a; a = temp; + } + for (i = 20; i < 40; i++) { + temp = sha1_round(G, a, b, c, d, e, data[i], 0x6ed9eba1); + e = d; d = c; c = RL(b, 30); b = a; a = temp; + } + for (i = 40; i < 60; i++) { + temp = sha1_round(H, a, b, c, d, e, data[i], 0x8f1bbcdc); + e = d; d = c; c = RL(b, 30); b = a; a = temp; + } + for (i = 60; i < 80; i++) { + temp = sha1_round(G, a, b, c, d, e, data[i], 0xca62c1d6); + e = d; d = c; c = RL(b, 30); b = a; a = temp; + } + + ctx->a += a; + ctx->b += b; + ctx->c += c; + ctx->d += d; + ctx->e += e; + + pam_overwrite_n(buffer, sizeof(buffer[0]) * SHA1_BLOCK_SIZE / 4); + pam_overwrite_array(data); +} + +void +sha1_update(struct sha1_context *ctx, const unsigned char *data, size_t length) +{ + size_t i = 0, l = length, c, t; + uint32_t count = 0; + + /* Process any pending + data blocks. */ + while (l + ctx->pending_count >= SHA1_BLOCK_SIZE) { + c = ctx->pending_count; + t = SHA1_BLOCK_SIZE - c; + memcpy(ctx->pending.c + c, &data[i], t); + sha1_process(ctx, ctx->pending.i); + i += t; + l -= t; + ctx->pending_count = 0; + } + + /* Save what's left of the data block as a pending data block. */ + c = ctx->pending_count; + memcpy(ctx->pending.c + c, &data[i], l); + ctx->pending_count += l; + + /* Update the message length. */ + ctx->count += length; + + /* Update our internal counts. */ + if (length != 0) { + count = ctx->counts[0]; + ctx->counts[0] += length; + if (count >= ctx->counts[0]) { + ctx->counts[1]++; + } + } +} + +size_t +sha1_output(struct sha1_context *ctx, unsigned char *out) +{ + struct sha1_context ctx2; + + /* Output the sum. */ + if (out != NULL) { + uint32_t c; + memcpy(&ctx2, ctx, sizeof(ctx2)); + + /* Pad this block. */ + c = ctx2.pending_count; + memcpy(ctx2.pending.c + c, + padding, SHA1_BLOCK_SIZE - c); + + /* Do we need to process two blocks now? */ + if (c >= (SHA1_BLOCK_SIZE - (sizeof(uint32_t) * 2))) { + /* Process this block. */ + sha1_process(&ctx2, ctx2.pending.i); + /* Set up another block. */ + ctx2.pending_count = 0; + memset(ctx2.pending.c, 0, SHA1_BLOCK_SIZE); + ctx2.pending.c[0] = + (c == SHA1_BLOCK_SIZE) ? 0x80 : 0; + } + + /* Process the final block. */ + ctx2.counts[1] <<= 3; + if (ctx2.counts[0] >> 29) { + ctx2.counts[1] |= + (ctx2.counts[0] >> 29); + } + ctx2.counts[0] <<= 3; + ctx2.counts[0] = htonl(ctx2.counts[0]); + ctx2.counts[1] = htonl(ctx2.counts[1]); + memcpy(ctx2.pending.c + 56, + &ctx2.counts[1], sizeof(uint32_t)); + memcpy(ctx2.pending.c + 60, + &ctx2.counts[0], sizeof(uint32_t)); + sha1_process(&ctx2, ctx2.pending.i); + + /* Output the data. */ + out[ 3] = (ctx2.a >> 0) & 0xff; + out[ 2] = (ctx2.a >> 8) & 0xff; + out[ 1] = (ctx2.a >> 16) & 0xff; + out[ 0] = (ctx2.a >> 24) & 0xff; + + out[ 7] = (ctx2.b >> 0) & 0xff; + out[ 6] = (ctx2.b >> 8) & 0xff; + out[ 5] = (ctx2.b >> 16) & 0xff; + out[ 4] = (ctx2.b >> 24) & 0xff; + + out[11] = (ctx2.c >> 0) & 0xff; + out[10] = (ctx2.c >> 8) & 0xff; + out[ 9] = (ctx2.c >> 16) & 0xff; + out[ 8] = (ctx2.c >> 24) & 0xff; + + out[15] = (ctx2.d >> 0) & 0xff; + out[14] = (ctx2.d >> 8) & 0xff; + out[13] = (ctx2.d >> 16) & 0xff; + out[12] = (ctx2.d >> 24) & 0xff; + + out[19] = (ctx2.e >> 0) & 0xff; + out[18] = (ctx2.e >> 8) & 0xff; + out[17] = (ctx2.e >> 16) & 0xff; + out[16] = (ctx2.e >> 24) & 0xff; + } + + return SHA1_OUTPUT_SIZE; +} diff --git a/modules/pam_timestamp/sha1.h b/modules/pam_timestamp/sha1.h new file mode 100644 index 0000000..69f432e --- /dev/null +++ b/modules/pam_timestamp/sha1.h @@ -0,0 +1,65 @@ +/* Yet another SHA-1 implementation. + * + * Copyright (c) 2003 Red Hat, Inc. + * Written by Nalin Dahyabhai + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef pam_timestamp_sha1_h +#define pam_timestamp_sha1_h + +#include +#include +#include "pam_cc_compat.h" + +#define SHA1_BLOCK_SIZE 64 + +struct sha1_context { + size_t count; + union { + unsigned char c[SHA1_BLOCK_SIZE]; + uint32_t i[SHA1_BLOCK_SIZE / sizeof(uint32_t)]; + } pending; + uint32_t counts[2]; + size_t pending_count; + uint32_t a, b, c, d, e; +}; + +#define SHA1_OUTPUT_SIZE 20 + +void sha1_init(struct sha1_context *ctx); +void sha1_update(struct sha1_context *ctx, + const unsigned char *data, size_t length); +size_t sha1_output(struct sha1_context *ctx, unsigned char *out); + +#endif diff --git a/modules/pam_timestamp/tst-pam_timestamp b/modules/pam_timestamp/tst-pam_timestamp new file mode 100755 index 0000000..1d425b8 --- /dev/null +++ b/modules/pam_timestamp/tst-pam_timestamp @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_timestamp.so diff --git a/modules/pam_tty_audit/README.xml b/modules/pam_tty_audit/README.xml new file mode 100644 index 0000000..95b851c --- /dev/null +++ b/modules/pam_tty_audit/README.xml @@ -0,0 +1,31 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_tty_audit.8.xml" xpointer='xpointer(id("pam_tty_audit-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_tty_audit/meson.build b/modules/pam_tty_audit/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_tty_audit/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_tty_audit/pam_tty_audit.8.xml b/modules/pam_tty_audit/pam_tty_audit.8.xml new file mode 100644 index 0000000..b46bbf7 --- /dev/null +++ b/modules/pam_tty_audit/pam_tty_audit.8.xml @@ -0,0 +1,196 @@ + + + + pam_tty_audit + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_tty_audit + Enable or disable TTY auditing for specified users + + + + + pam_tty_audit.so + + disable=patterns + + + enable=patterns + + + + + + DESCRIPTION + + The pam_tty_audit PAM module is used to enable or disable TTY auditing. + By default, the kernel does not audit input on any TTY. + + + + + OPTIONS + + + + disable=patterns + + + + For each user matching , + disable TTY auditing. This overrides any previous + option matching the same user name on the command line. See NOTES + for further description of . + + + + + + enable=patterns + + + + For each user matching , + enable TTY auditing. This overrides any previous + option matching the same user name on the command line. See NOTES + for further description of . + + + + + + open_only + + + + Set the TTY audit flag when opening the session, but do not restore + it when closing the session. Using this option is necessary for + some services that don't fork() to run the + authenticated session, such as sudo. + + + + + + log_passwd + + + + Log keystrokes when ECHO mode is off but ICANON mode is active. + This is the mode in which the tty is placed during password entry. + By default, passwords are not logged. This option may not be + available on older kernels (3.9?). + + + + + + + + MODULE TYPES PROVIDED + + Only the session type is supported. + + + + + RETURN VALUES + + + PAM_SESSION_ERR + + + Error reading or modifying the TTY audit flag. See the system log + for more details. + + + + + + PAM_SUCCESS + + + Success. + + + + + + + + + NOTES + + When TTY auditing is enabled, it is inherited by all processes started by + that user. In particular, daemons restarted by a user will still have + TTY auditing enabled, and audit TTY input even by other users unless + auditing for these users is explicitly disabled. Therefore, it is + recommended to use as the first option for + most daemons using PAM. + + + To view the data that was logged by the kernel to audit use + the command aureport --tty. + + + The are comma separated + lists of glob patterns or ranges of uids. A range is specified as + min_uid:max_uid where + one of these values can be empty. If min_uid is + empty only user with the uid max_uid will be + matched. If max_uid is empty users with the uid + greater than or equal to min_uid will be + matched. + + + Please note that passwords in some circumstances may be logged by TTY auditing + even if the is not used. For example, all input to + an ssh session will be logged - even if there is a password being typed into + some software running at the remote host because only the local TTY state + affects the local TTY auditing. + + + + + EXAMPLES + + Audit all administrative actions. + +session required pam_tty_audit.so disable=* enable=root + + + + + + SEE ALSO + + + aureport8 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_tty_audit was written by Miloslav Trmač + <mitr@redhat.com>. + The log_passwd option was added by Richard Guy Briggs + <rgb@redhat.com>. + + + + \ No newline at end of file diff --git a/modules/pam_tty_audit/pam_tty_audit.c b/modules/pam_tty_audit/pam_tty_audit.c new file mode 100644 index 0000000..15fb910 --- /dev/null +++ b/modules/pam_tty_audit/pam_tty_audit.c @@ -0,0 +1,450 @@ +/* Copyright © 2007, 2008 Red Hat, Inc. All rights reserved. + Red Hat author: Miloslav Trmač + + Redistribution and use in source and binary forms of Linux-PAM, with + or without modification, are permitted provided that the following + conditions are met: + + 1. Redistributions of source code must retain any existing copyright + notice, and this entire permission notice in its entirety, + including the disclaimer of warranties. + + 2. Redistributions in binary form must reproduce all prior and current + copyright notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + 3. The name of any author may not be used to endorse or promote + products derived from this software without their specific prior + written permission. + + ALTERNATIVELY, this product may be distributed under the terms of the + GNU General Public License, in which case the provisions of the GNU + GPL are required INSTEAD OF the above restrictions. (This clause is + necessary due to a potential conflict between the GNU GPL and the + restrictions contained in a BSD-style copyright.) + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "pam_cc_compat.h" +#include "pam_inline.h" + +#define DATANAME "pam_tty_audit_last_state" + +/* Open an audit netlink socket */ +static int +nl_open (void) +{ + return socket (AF_NETLINK, SOCK_RAW, NETLINK_AUDIT); +} + +static int +nl_send (int fd, unsigned type, unsigned flags, const void *data, size_t size) +{ + struct sockaddr_nl addr; + struct msghdr msg; + struct nlmsghdr nlm; + struct iovec iov[2]; + ssize_t res; + + nlm.nlmsg_len = NLMSG_LENGTH (size); + nlm.nlmsg_type = type; + nlm.nlmsg_flags = NLM_F_REQUEST | flags; + nlm.nlmsg_seq = 0; + nlm.nlmsg_pid = 0; + iov[0].iov_base = &nlm; + iov[0].iov_len = sizeof (nlm); + DIAG_PUSH_IGNORE_CAST_QUAL; + iov[1].iov_base = (void *)data; + DIAG_POP_IGNORE_CAST_QUAL; + iov[1].iov_len = size; + addr.nl_family = AF_NETLINK; + addr.nl_pid = 0; + addr.nl_groups = 0; + msg.msg_name = &addr; + msg.msg_namelen = sizeof (addr); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + res = sendmsg (fd, &msg, 0); + if (res == -1) + return -1; + if ((size_t)res != nlm.nlmsg_len) + { + errno = EIO; + return -1; + } + return 0; +} + +static int +nl_recv (int fd, unsigned type, void *buf, size_t size) +{ + struct sockaddr_nl addr; + struct msghdr msg; + struct nlmsghdr nlm; + struct iovec iov[2]; + ssize_t res, resdiff; + + again: + iov[0].iov_base = &nlm; + iov[0].iov_len = sizeof (nlm); + msg.msg_name = &addr; + msg.msg_namelen = sizeof (addr); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + if (type != NLMSG_ERROR) + { + res = recvmsg (fd, &msg, MSG_PEEK); + if (res == -1) + return -1; + if (res != NLMSG_LENGTH (0)) + { + errno = EIO; + return -1; + } + if (nlm.nlmsg_type == NLMSG_ERROR) + { + struct nlmsgerr err; + + iov[1].iov_base = &err; + iov[1].iov_len = sizeof (err); + msg.msg_iovlen = 2; + res = recvmsg (fd, &msg, 0); + if (res == -1) + return -1; + if ((size_t)res != NLMSG_LENGTH (sizeof (err)) + || nlm.nlmsg_type != NLMSG_ERROR) + { + errno = EIO; + return -1; + } + if (err.error == 0) + goto again; + errno = -err.error; + return -1; + } + } + if (size != 0) + { + iov[1].iov_base = buf; + iov[1].iov_len = size; + msg.msg_iovlen = 2; + } + res = recvmsg (fd, &msg, 0); + if (res == -1) + return -1; + resdiff = NLMSG_LENGTH(size) - (size_t)res; + if (resdiff < 0 + || nlm.nlmsg_type != type) + { + errno = EIO; + return -1; + } + else if (resdiff > 0) + { + memset((char *)buf + size - resdiff, 0, resdiff); + } + return 0; +} + +static int +nl_recv_ack (int fd) +{ + struct nlmsgerr err; + + if (nl_recv (fd, NLMSG_ERROR, &err, sizeof (err)) != 0) + return -1; + if (err.error != 0) + { + errno = -err.error; + return -1; + } + return 0; +} + +static void +cleanup_old_status (pam_handle_t *pamh, void *data, int error_status) +{ + (void)pamh; + (void)error_status; + free (data); +} + +enum uid_range { UID_RANGE_NONE, UID_RANGE_MM, UID_RANGE_MIN, + UID_RANGE_ONE, UID_RANGE_ERR }; + +static enum uid_range +parse_uid_range(pam_handle_t *pamh, const char *s, + uid_t *min_uid, uid_t *max_uid) +{ + const char *range = s; + const char *pmax; + char *endptr; + enum uid_range rv = UID_RANGE_MM; + + if ((pmax=strchr(range, ':')) == NULL) + return UID_RANGE_NONE; + ++pmax; + + if (range[0] == ':') + rv = UID_RANGE_ONE; + else { + errno = 0; + *min_uid = strtoul (range, &endptr, 10); + if (errno != 0 || (range == endptr) || *endptr != ':') { + pam_syslog(pamh, LOG_DEBUG, + "wrong min_uid value in '%s'", s); + return UID_RANGE_ERR; + } + } + + if (*pmax == '\0') { + if (rv == UID_RANGE_ONE) + return UID_RANGE_ERR; + + return UID_RANGE_MIN; + } + + errno = 0; + *max_uid = strtoul (pmax, &endptr, 10); + if (errno != 0 || (pmax == endptr) || *endptr != '\0') { + pam_syslog(pamh, LOG_DEBUG, + "wrong max_uid value in '%s'", s); + return UID_RANGE_ERR; + } + + if (rv == UID_RANGE_ONE) + *min_uid = *max_uid; + return rv; +} + +int +pam_sm_open_session (pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + enum command { CMD_NONE, CMD_ENABLE, CMD_DISABLE }; + + enum command command; + struct audit_tty_status *old_status, new_status; + const char *user; + int i, fd, open_only; + struct passwd *pwd; +#ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD + int log_passwd; +#endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */ + + (void)flags; + + if (pam_get_user (pamh, &user, NULL) != PAM_SUCCESS) + { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name"); + return PAM_SESSION_ERR; + } + + pwd = pam_modutil_getpwnam(pamh, user); + if (pwd == NULL) + { + pam_syslog(pamh, LOG_NOTICE, + "open_session unknown user '%s'", user); + return PAM_SESSION_ERR; + } + + command = CMD_NONE; + open_only = 0; +#ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD + log_passwd = 0; +#endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */ + for (i = 0; i < argc; i++) + { + const char *str; + + if ((str = pam_str_skip_prefix(argv[i], "enable=")) != NULL + || (str = pam_str_skip_prefix(argv[i], "disable=")) != NULL) + { + enum command this_command; + char *copy, *tok_data, *tok; + + this_command = *argv[i] == 'e' ? CMD_ENABLE : CMD_DISABLE; + copy = strdup (str); + if (copy == NULL) + return PAM_SESSION_ERR; + for (tok = strtok_r (copy, ",", &tok_data); + tok != NULL && command != this_command; + tok = strtok_r (NULL, ",", &tok_data)) + { + uid_t min_uid = 0, max_uid = 0; + switch (parse_uid_range(pamh, tok, &min_uid, &max_uid)) + { + case UID_RANGE_NONE: + if (fnmatch (tok, user, 0) == 0) + command = this_command; + break; + case UID_RANGE_MM: + if (pwd->pw_uid >= min_uid && pwd->pw_uid <= max_uid) + command = this_command; + break; + case UID_RANGE_MIN: + if (pwd->pw_uid >= min_uid) + command = this_command; + break; + case UID_RANGE_ONE: + if (pwd->pw_uid == max_uid) + command = this_command; + break; + case UID_RANGE_ERR: + break; + } + } + free (copy); + } + else if (strcmp (argv[i], "open_only") == 0) + open_only = 1; + else if (strcmp (argv[i], "log_passwd") == 0) +#ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD + log_passwd = 1; +#else /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */ + pam_syslog (pamh, LOG_WARNING, + "The log_passwd option was not available at compile time."); +#warning "pam_tty_audit: The log_passwd option is not available. Please upgrade your headers/kernel." +#endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */ + else + { + pam_syslog (pamh, LOG_ERR, "unknown option `%s'", argv[i]); + } + } + if (command == CMD_NONE) + return PAM_SUCCESS; + + old_status = malloc (sizeof (*old_status)); + if (old_status == NULL) + return PAM_SESSION_ERR; + + fd = nl_open (); + if (fd == -1 + && errno == EPROTONOSUPPORT) + { + pam_syslog (pamh, LOG_WARNING, "unable to open audit socket, audit not " + "supported; tty_audit skipped"); + free (old_status); + return PAM_IGNORE; + } + else if (fd == -1 + || nl_send (fd, AUDIT_TTY_GET, 0, NULL, 0) != 0 + || nl_recv (fd, AUDIT_TTY_GET, old_status, sizeof (*old_status)) != 0) + { + pam_syslog (pamh, LOG_ERR, "error reading current audit status: %m"); + if (fd != -1) + close (fd); + free (old_status); + return PAM_SESSION_ERR; + } + + memcpy(&new_status, old_status, sizeof(new_status)); + + new_status.enabled = (command == CMD_ENABLE ? 1 : 0); +#ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD + new_status.log_passwd = log_passwd; +#endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */ + if (old_status->enabled == new_status.enabled +#ifdef HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD + && old_status->log_passwd == new_status.log_passwd +#endif /* HAVE_STRUCT_AUDIT_TTY_STATUS_LOG_PASSWD */ + ) + { + open_only = 1; /* to clean up old_status */ + goto ok_fd; + } + + if (open_only == 0 + && pam_set_data (pamh, DATANAME, old_status, cleanup_old_status) + != PAM_SUCCESS) + { + pam_syslog (pamh, LOG_ERR, "error saving old audit status"); + close (fd); + free (old_status); + return PAM_SESSION_ERR; + } + + if (nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, &new_status, + sizeof (new_status)) != 0 + || nl_recv_ack (fd) != 0) + { + pam_syslog (pamh, LOG_ERR, "error setting current audit status: %m"); + close (fd); + if (open_only != 0) + free (old_status); + return PAM_SESSION_ERR; + } + /* Fall through */ + ok_fd: + close (fd); + pam_syslog (pamh, LOG_DEBUG, "changed status from %d to %d", + old_status->enabled, new_status.enabled); + if (open_only != 0) + free (old_status); + return PAM_SUCCESS; +} + +int +pam_sm_close_session (pam_handle_t *pamh, int flags, int argc, + const char **argv) +{ + const void *status_; + + (void)flags; + (void)argc; + (void)argv; + if (pam_get_data (pamh, DATANAME, &status_) == PAM_SUCCESS) + { + const struct audit_tty_status *status; + int fd; + + status = status_; + + fd = nl_open (); + if (fd == -1 + || nl_send (fd, AUDIT_TTY_SET, NLM_F_ACK, status, + sizeof (*status)) != 0 + || nl_recv_ack (fd) != 0) + { + pam_syslog (pamh, LOG_ERR, "error restoring audit status: %m"); + if (fd != -1) + close (fd); + return PAM_SESSION_ERR; + } + close (fd); + pam_syslog (pamh, LOG_DEBUG, "restored status to %d", status->enabled); + } + return PAM_SUCCESS; +} diff --git a/modules/pam_tty_audit/tst-pam_tty_audit b/modules/pam_tty_audit/tst-pam_tty_audit new file mode 100755 index 0000000..2b8ab4b --- /dev/null +++ b/modules/pam_tty_audit/tst-pam_tty_audit @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_tty_audit.so diff --git a/modules/pam_umask/README.xml b/modules/pam_umask/README.xml new file mode 100644 index 0000000..d2b82d1 --- /dev/null +++ b/modules/pam_umask/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_umask.8.xml" xpointer='xpointer(id("pam_umask-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_umask/meson.build b/modules/pam_umask/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_umask/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_umask/pam_umask.8.xml b/modules/pam_umask/pam_umask.8.xml new file mode 100644 index 0000000..d2cead4 --- /dev/null +++ b/modules/pam_umask/pam_umask.8.xml @@ -0,0 +1,260 @@ + + + + pam_umask + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_umask + PAM module to set the file mode creation mask + + + + + pam_umask.so + + debug + + + silent + + + usergroups + + + nousergroups + + + umask=mask + + + + + + + DESCRIPTION + + + pam_umask is a PAM module to set the file mode creation mask + of the current environment. The umask affects the default + permissions assigned to newly created files. + + + The PAM module tries to get the umask value from the + following places in the following order: + + + + umask= entry in the user's GECOS field (see below for details) + + + + + umask= argument + + + + + UMASK entry from /etc/login.defs + + + + + UMASK= entry from /etc/default/login + + + + + + The GECOS field is split on comma ',' characters. Entries must be set in + its 'other' (sub-)field (the 5th field within the GECOS field), which + could be done, for example, using chfn --other. + In addition to the umask= entry, the module also recognizes the pri= + entry, which sets the nice priority value for the session, and the ulimit= + entry, which sets the maximum size of files the processes in the session + can create. + + + + + + + OPTIONS + + + + + + debug + + + + Print debug information. + + + + + + + silent + + + + Don't print informative messages. + + + + + + + usergroups + + + + If the user is not root and the username is the same as + primary group name, the umask group bits are set to be the + same as owner bits (examples: 022 -> 002, 077 -> 007). + + + + + + + nousergroups + + + + This is the direct opposite of the usergroups option described above, + which can be useful in case pam_umask has been compiled with + usergroups enabled by default and you want to disable it at runtime. + + + + + + + umask=mask + + + + Sets the calling process's file mode creation mask (umask) + to & 0777. The value is interpreted + as Octal. + + + + + + + + + + + MODULE TYPES PROVIDED + + Only the type is provided. + + + + + RETURN VALUES + + + + + PAM_SUCCESS + + + The new umask was set successfully. + + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_CONV_ERR + + + The conversation method supplied by the application + failed to obtain the username. + + + + + + PAM_INCOMPLETE + + + The conversation method supplied by the application + returned PAM_CONV_AGAIN. + + + + + + PAM_SERVICE_ERR + + + No username was given. + + + + + + PAM_USER_UNKNOWN + + + User not known. + + + + + + + + + + EXAMPLES + + Add the following line to /etc/pam.d/login to + set the user specific umask at login: + + session optional pam_umask.so umask=0022 + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_umask was written by Thorsten Kukuk <kukuk@thkukuk.de>. + + + + diff --git a/modules/pam_umask/pam_umask.c b/modules/pam_umask/pam_umask.c new file mode 100644 index 0000000..8ba5de9 --- /dev/null +++ b/modules/pam_umask/pam_umask.c @@ -0,0 +1,237 @@ +/* + * pam_umask module + * + * Copyright (c) 2005, 2006, 2007, 2010, 2013 Thorsten Kukuk + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License V2, in which case the provisions of the GPL + * are required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "pam_inline.h" + +#define LOGIN_DEFS "/etc/login.defs" +#define LOGIN_CONF "/etc/default/login" + +struct options_t { + int debug; + int usergroups; + int silent; + const char *umask; + char *login_umask; +}; +typedef struct options_t options_t; + +static void +parse_option (const pam_handle_t *pamh, const char *argv, options_t *options) +{ + const char *str; + + if (argv == NULL || argv[0] == '\0') + return; + + if (strcasecmp (argv, "debug") == 0) + options->debug = 1; + else if ((str = pam_str_skip_icase_prefix (argv, "umask=")) != NULL) + options->umask = str; + else if (strcasecmp (argv, "usergroups") == 0) + options->usergroups = 1; + else if (strcasecmp (argv, "nousergroups") == 0) + options->usergroups = 0; + else if (strcasecmp (argv, "silent") == 0) + options->silent = 1; + else + pam_syslog (pamh, LOG_ERR, "Unknown option: `%s'", argv); +} + +static int +get_options (pam_handle_t *pamh, options_t *options, + int argc, const char **argv) +{ + memset (options, 0, sizeof (options_t)); + + options->usergroups = DEFAULT_USERGROUPS_SETTING; + + /* Parse parameters for module */ + for ( ; argc-- > 0; argv++) + parse_option (pamh, *argv, options); + + if (options->umask == NULL) { + options->login_umask = pam_modutil_search_key (pamh, LOGIN_DEFS, "UMASK"); + if (options->login_umask == NULL) + options->login_umask = pam_modutil_search_key (pamh, LOGIN_CONF, "UMASK"); + options->umask = options->login_umask; + } + + return 0; +} + +static void +set_umask (const char *value) +{ + const char *value_orig = value; + mode_t mask; + char *endptr; + + mask = strtoul (value, &endptr, 8) & 0777; + if (((mask == 0) && (value_orig == endptr)) || + ((mask == UINT_MAX) && (errno == ERANGE))) + return; + umask (mask); +} + +/* Set the process nice, ulimit, and umask from the + password file entry. */ +static void +setup_limits_from_gecos (pam_handle_t *pamh, options_t *options, + struct passwd *pw) +{ + char *cp; + + if (options->usergroups) + { + /* if not root and username is the same as primary group name, + set umask group bits to be the same as owner bits + (examples: 022 -> 002, 077 -> 007). */ + if (pw->pw_uid != 0) + { + struct group *grp = pam_modutil_getgrgid (pamh, pw->pw_gid); + if (grp && (strcmp (pw->pw_name, grp->gr_name) == 0)) + { + mode_t oldmask = umask (0777); + umask ((oldmask & ~070) | ((oldmask >> 3) & 070)); + } + } + } + + /* See if the GECOS field contains values for NICE, UMASK or ULIMIT. */ + for (cp = pw->pw_gecos; cp != NULL; cp = strchr (cp, ',')) + { + const char *str; + + if (*cp == ',') + cp++; + + if ((str = pam_str_skip_icase_prefix (cp, "umask=")) != NULL) + umask (strtol (str, NULL, 8) & 0777); + else if ((str = pam_str_skip_icase_prefix (cp, "pri=")) != NULL) + { + errno = 0; + if (nice (strtol (str, NULL, 10)) == -1 && errno != 0) + { + if (!options->silent || options->debug) + pam_error (pamh, "nice failed: %m\n"); + pam_syslog (pamh, LOG_ERR, "nice failed: %m"); + } + } + else if ((str = pam_str_skip_icase_prefix (cp, "ulimit=")) != NULL) + { + struct rlimit rlimit_fsize; + rlimit_fsize.rlim_cur = 512L * strtol (str, NULL, 10); + rlimit_fsize.rlim_max = rlimit_fsize.rlim_cur; + if (setrlimit (RLIMIT_FSIZE, &rlimit_fsize) == -1) + { + if (!options->silent || options->debug) + pam_error (pamh, "setrlimit failed: %m\n"); + pam_syslog (pamh, LOG_ERR, "setrlimit failed: %m"); + } + } + } +} + + +int +pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + struct passwd *pw; + options_t options; + const char *name; + int retval = PAM_SUCCESS; + + get_options (pamh, &options, argc, argv); + if (flags & PAM_SILENT) + options.silent = 1; + + /* get the user name. */ + if ((retval = pam_get_user (pamh, &name, NULL)) != PAM_SUCCESS) + { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", + pam_strerror(pamh, retval)); + return (retval == PAM_CONV_AGAIN ? PAM_INCOMPLETE:retval); + } + + pw = pam_modutil_getpwnam (pamh, name); + if (pw == NULL) + { + pam_syslog (pamh, LOG_NOTICE, "account for %s not found", name); + return PAM_USER_UNKNOWN; + } + + if (options.umask != NULL) + { + set_umask (options.umask); + free (options.login_umask); + options.umask = options.login_umask = NULL; + } + + setup_limits_from_gecos (pamh, &options, pw); + + return retval; +} + +int +pam_sm_close_session (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +/* end of module definition */ diff --git a/modules/pam_umask/tst-pam_umask b/modules/pam_umask/tst-pam_umask new file mode 100755 index 0000000..3608a9d --- /dev/null +++ b/modules/pam_umask/tst-pam_umask @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_umask.so diff --git a/modules/pam_unix/README.xml b/modules/pam_unix/README.xml new file mode 100644 index 0000000..49a6594 --- /dev/null +++ b/modules/pam_unix/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_unix.8.xml" xpointer='xpointer(id("pam_unix-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_unix/audit.c b/modules/pam_unix/audit.c new file mode 100644 index 0000000..9513aaa --- /dev/null +++ b/modules/pam_unix/audit.c @@ -0,0 +1,44 @@ +#include "config.h" + +#ifdef HAVE_LIBAUDIT + +#include +#include + +#include + +#include + +#include "audit.h" +#include "passverify.h" + +int audit_log(int type, const char *uname, int retval) +{ + int audit_fd, rc; + + audit_fd = audit_open(); + if (audit_fd < 0) { + /* You get these error codes only when the kernel doesn't have + * audit compiled in. */ + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) + return PAM_SUCCESS; + + helper_log_err(LOG_CRIT, "audit_open() failed: %m"); + return PAM_AUTH_ERR; + } + + + + rc = audit_log_acct_message(audit_fd, type, NULL, "PAM:" HELPER_COMPILE, + uname, -1, NULL, NULL, NULL, retval == PAM_SUCCESS); + if (rc == -EPERM && geteuid() != 0) { + rc = 0; + } + + audit_close(audit_fd); + + return rc < 0 ? PAM_AUTH_ERR : PAM_SUCCESS; +} + +#endif /* HAVE_LIBAUDIT */ diff --git a/modules/pam_unix/audit.h b/modules/pam_unix/audit.h new file mode 100644 index 0000000..321232a --- /dev/null +++ b/modules/pam_unix/audit.h @@ -0,0 +1,7 @@ +#ifndef PAM_UNIX_AUDIT_H +#define PAM_UNIX_AUDIT_H + +int +audit_log(int type, const char *uname, int rc); + +#endif /* PAM_UNIX_AUDIT_H */ diff --git a/modules/pam_unix/bigcrypt.c b/modules/pam_unix/bigcrypt.c new file mode 100644 index 0000000..296e01f --- /dev/null +++ b/modules/pam_unix/bigcrypt.c @@ -0,0 +1,169 @@ +/* + * This function implements the "bigcrypt" algorithm specifically for + * Linux-PAM. + * + * This algorithm is algorithm 0 (default) shipped with the C2 secure + * implementation of Digital UNIX. + * + * Disclaimer: This work is not based on the source code to Digital + * UNIX, nor am I connected to Digital Equipment Corp, in any way + * other than as a customer. This code is based on published + * interfaces and reasonable guesswork. + * + * Description: The cleartext is divided into blocks of SEGMENT_SIZE=8 + * characters or less. Each block is encrypted using the standard UNIX + * libc crypt function. The result of the encryption for one block + * provides the salt for the succeeding block. + * + * Restrictions: The buffer used to hold the encrypted result is + * statically allocated. (see MAX_PASS_LEN below). This is necessary, + * as the returned pointer points to "static data that are overwritten + * by each call", (XPG3: XSI System Interface + Headers pg 109), and + * this is a drop in replacement for crypt(); + * + * Andy Phillips + */ + +#include "config.h" + +#include +#include +#include +#include "pam_inline.h" +#ifdef HAVE_CRYPT_H +#include +#endif + +#include "bigcrypt.h" + +/* + * Max cleartext password length in segments of 8 characters this + * function can deal with (16 segments of 8 chars= max 128 character + * password). + */ + +#define MAX_PASS_LEN 16 +#define SEGMENT_SIZE 8 +#define SALT_SIZE 2 +#define KEYBUF_SIZE ((MAX_PASS_LEN*SEGMENT_SIZE)+SALT_SIZE) +#define ESEGMENT_SIZE 11 +#define CBUF_SIZE ((MAX_PASS_LEN*ESEGMENT_SIZE)+SALT_SIZE+1) + +char *bigcrypt(const char *key, const char *salt) +{ + char *dec_c2_cryptbuf; +#ifdef HAVE_CRYPT_R + struct crypt_data *cdata; +#endif + size_t keylen, n_seg, j; + char *cipher_ptr, *plaintext_ptr, *tmp_ptr, *salt_ptr; + char keybuf[KEYBUF_SIZE + 1] = {}; + + D(("called with key='%s', salt='%s'.", key, salt)); + + /* reset arrays */ + dec_c2_cryptbuf = calloc(1, CBUF_SIZE); + if (!dec_c2_cryptbuf) { + return NULL; + } +#ifdef HAVE_CRYPT_R + cdata = calloc(1, sizeof(*cdata)); + if(!cdata) { + free(dec_c2_cryptbuf); + return NULL; + } +#endif + + /* fill KEYBUF_SIZE with key */ + strncpy(keybuf, key, KEYBUF_SIZE); + + /* deal with case that we are doing a password check for a + conventially encrypted password: the salt will be + SALT_SIZE+ESEGMENT_SIZE long. */ + if (strlen(salt) == (SALT_SIZE + ESEGMENT_SIZE)) + keybuf[SEGMENT_SIZE] = '\0'; /* terminate password early(?) */ + + keylen = strlen(keybuf); + + if (!keylen) { + n_seg = 1; + } else { + /* work out how many segments */ + n_seg = 1 + ((keylen - 1) / SEGMENT_SIZE); + } + + if (n_seg > MAX_PASS_LEN) + n_seg = MAX_PASS_LEN; /* truncate at max length */ + + /* set up some pointers */ + cipher_ptr = dec_c2_cryptbuf; + plaintext_ptr = keybuf; + + /* do the first block with supplied salt */ +#ifdef HAVE_CRYPT_R + tmp_ptr = crypt_r(plaintext_ptr, salt, cdata); /* libc crypt_r() */ +#else + tmp_ptr = crypt(plaintext_ptr, salt); /* libc crypt() */ +#endif + if (tmp_ptr == NULL) { + pam_overwrite_array(keybuf); + free(dec_c2_cryptbuf); +#ifdef HAVE_CRYPT_R + pam_overwrite_object(cdata); + free(cdata); +#endif + return NULL; + } + /* and place in the static area */ + strncpy(cipher_ptr, tmp_ptr, 13); + pam_overwrite_string(tmp_ptr); + cipher_ptr += ESEGMENT_SIZE + SALT_SIZE; + plaintext_ptr += SEGMENT_SIZE; /* first block of SEGMENT_SIZE */ + + /* change the salt (1st 2 chars of previous block) - this was found + by dowsing */ + + salt_ptr = cipher_ptr - ESEGMENT_SIZE; + + /* so far this is identical to "return crypt(key, salt);", if + there is more than one block encrypt them... */ + + if (n_seg > 1) { + for (j = 2; j <= n_seg; j++) { + +#ifdef HAVE_CRYPT_R + tmp_ptr = crypt_r(plaintext_ptr, salt_ptr, cdata); +#else + tmp_ptr = crypt(plaintext_ptr, salt_ptr); +#endif + if (tmp_ptr == NULL) { + pam_overwrite_array(keybuf); + pam_overwrite_string(dec_c2_cryptbuf); + free(dec_c2_cryptbuf); +#ifdef HAVE_CRYPT_R + pam_overwrite_object(cdata); + free(cdata); +#endif + return NULL; + } + + /* skip the salt for seg!=0 */ + strncpy(cipher_ptr, (tmp_ptr + SALT_SIZE), ESEGMENT_SIZE); + pam_overwrite_string(tmp_ptr); + + cipher_ptr += ESEGMENT_SIZE; + plaintext_ptr += SEGMENT_SIZE; + salt_ptr = cipher_ptr - ESEGMENT_SIZE; + } + } + D(("key=|%s|, salt=|%s|\nbuf=|%s|\n", key, salt, dec_c2_cryptbuf)); + + pam_overwrite_array(keybuf); +#ifdef HAVE_CRYPT_R + pam_overwrite_object(cdata); + free(cdata); +#endif + + /* this is the terminated encrypted password */ + return dec_c2_cryptbuf; +} diff --git a/modules/pam_unix/bigcrypt.h b/modules/pam_unix/bigcrypt.h new file mode 100644 index 0000000..a66a96e --- /dev/null +++ b/modules/pam_unix/bigcrypt.h @@ -0,0 +1 @@ +extern char *bigcrypt(const char *key, const char *salt); diff --git a/modules/pam_unix/bigcrypt_main.c b/modules/pam_unix/bigcrypt_main.c new file mode 100644 index 0000000..22d325d --- /dev/null +++ b/modules/pam_unix/bigcrypt_main.c @@ -0,0 +1,20 @@ +#include "config.h" + +#include +#include + +#include "bigcrypt.h" + +int +main(int argc, char **argv) +{ + if (argc < 3) { + fprintf(stderr, "Usage: %s password salt\n", + strchr(argv[0], '/') ? + (strchr(argv[0], '/') + 1) : + argv[0]); + return 0; + } + fprintf(stdout, "%s\n", bigcrypt(argv[1], argv[2])); + return 0; +} diff --git a/modules/pam_unix/lckpwdf.-c b/modules/pam_unix/lckpwdf.-c new file mode 100644 index 0000000..4d0f0ad --- /dev/null +++ b/modules/pam_unix/lckpwdf.-c @@ -0,0 +1,131 @@ +/* + * This is a hack, but until libc and glibc both include this function + * by default (libc only includes it if nys is not being used, at the + * moment, and glibc doesn't appear to have it at all) we need to have + * it here, too. :-( + * + * This should not become an official part of PAM. + * + * BEGIN_HACK + */ + +/* + * lckpwdf.c -- prevent simultaneous updates of password files + * + * Before modifying any of the password files, call lckpwdf(). It may block + * for up to 15 seconds trying to get the lock. Return value is 0 on success + * or -1 on failure. When you are done, call ulckpwdf() to release the lock. + * The lock is also released automatically when the process exits. Only one + * process at a time may hold the lock. + * + * These functions are supposed to be conformant with AT&T SVID Issue 3. + * + * Written by Marek Michalkiewicz , + * public domain. + */ + +#include +#include +#ifdef WITH_SELINUX +#include +#endif + +#define LOCKFILE "/etc/.pwd.lock" +#define TIMEOUT 15 + +static int lockfd = -1; + +static int do_lock(int fd) +{ + struct flock fl; + + memset(&fl, 0, sizeof fl); + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + return fcntl(fd, F_SETLKW, &fl); +} + +static void alarm_catch(int sig) +{ +/* does nothing, but fcntl F_SETLKW will fail with EINTR */ +} + +static int lckpwdf(void) +{ + struct sigaction act, oldact; + sigset_t set, oldset; + + if (lockfd != -1) + return -1; + +#ifdef WITH_SELINUX + if(is_selinux_enabled()>0) + { + lockfd = open(LOCKFILE, O_WRONLY | O_CLOEXEC); + if(lockfd == -1 && errno == ENOENT) + { + char *create_context_raw; + int rc; + + if(getfilecon_raw("/etc/passwd", &create_context_raw)) + return -1; + rc = setfscreatecon_raw(create_context_raw); + freecon(create_context_raw); + if(rc) + return -1; + lockfd = open(LOCKFILE, O_CREAT | O_WRONLY | O_CLOEXEC, 0600); + if(setfscreatecon_raw(NULL)) + return -1; + } + } + else +#endif + lockfd = open(LOCKFILE, O_CREAT | O_WRONLY | O_CLOEXEC, 0600); + if (lockfd == -1) + return -1; + + memset(&act, 0, sizeof act); + act.sa_handler = alarm_catch; + act.sa_flags = 0; + sigfillset(&act.sa_mask); + if (sigaction(SIGALRM, &act, &oldact) == -1) + goto cleanup_fd; + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1) + goto cleanup_sig; + + alarm(TIMEOUT); + if (do_lock(lockfd) == -1) + goto cleanup_alarm; + alarm(0); + sigprocmask(SIG_SETMASK, &oldset, NULL); + sigaction(SIGALRM, &oldact, NULL); + return 0; + + cleanup_alarm: + alarm(0); + sigprocmask(SIG_SETMASK, &oldset, NULL); + cleanup_sig: + sigaction(SIGALRM, &oldact, NULL); + cleanup_fd: + close(lockfd); + lockfd = -1; + return -1; +} + +static int ulckpwdf(void) +{ + unlink(LOCKFILE); + if (lockfd == -1) + return -1; + + if (close(lockfd) == -1) { + lockfd = -1; + return -1; + } + lockfd = -1; + return 0; +} +/* END_HACK */ diff --git a/modules/pam_unix/md5.c b/modules/pam_unix/md5.c new file mode 100644 index 0000000..78e9af2 --- /dev/null +++ b/modules/pam_unix/md5.c @@ -0,0 +1,259 @@ +/* + * $Id$ + * + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + */ + +#include "pam_inline.h" + +#include +#include "md5.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else + +static void byteReverse(uint32 *buf, unsigned longs); + +#ifndef ASM_MD5 +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(uint32 *buf, unsigned longs) +{ + uint32 t; + do { + unsigned char *p = (unsigned char *) buf; + t = (uint32) ((unsigned) p[3] << 8 | p[2]) << 16 | + ((unsigned) p[1] << 8 | p[0]); + *buf = t; + ++buf; + } while (--longs); +} +#endif +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Name(MD5Init)(struct MD5Context *ctx) +{ + ctx->buf.i[0] = 0x67452301U; + ctx->buf.i[1] = 0xefcdab89U; + ctx->buf.i[2] = 0x98badcfeU; + ctx->buf.i[3] = 0x10325476U; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Name(MD5Update)(struct MD5Context *ctx, unsigned const char *buf, unsigned len) +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = ctx->in.c + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in.i, 16); + MD5Name(MD5Transform)(ctx->buf.i, ctx->in.i); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in.c, buf, 64); + byteReverse(ctx->in.i, 16); + MD5Name(MD5Transform)(ctx->buf.i, ctx->in.i); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in.c, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Name(MD5Final)(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in.c + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in.i, 16); + MD5Name(MD5Transform)(ctx->buf.i, ctx->in.i); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in.c, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in.i, 14); + + /* Append length in bits and transform */ + memcpy(ctx->in.i + 14, ctx->bits, 2*sizeof(uint32)); + + MD5Name(MD5Transform)(ctx->buf.i, ctx->in.i); + byteReverse(ctx->buf.i, 4); + memcpy(digest, ctx->buf.c, 16); + pam_overwrite_object(ctx); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Name(MD5Transform)(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478U, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756U, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070dbU, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceeeU, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0fafU, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62aU, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613U, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501U, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8U, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7afU, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1U, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7beU, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122U, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193U, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438eU, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821U, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562U, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340U, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51U, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aaU, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105dU, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453U, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681U, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8U, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6U, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6U, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87U, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14edU, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905U, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8U, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9U, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8aU, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942U, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681U, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122U, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380cU, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44U, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9U, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60U, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70U, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6U, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127faU, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085U, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05U, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039U, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5U, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8U, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665U, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244U, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97U, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7U, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039U, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3U, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92U, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47dU, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1U, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4fU, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0U, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314U, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1U, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82U, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235U, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bbU, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391U, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif diff --git a/modules/pam_unix/md5.h b/modules/pam_unix/md5.h new file mode 100644 index 0000000..3dc54bd --- /dev/null +++ b/modules/pam_unix/md5.h @@ -0,0 +1,39 @@ + +#ifndef MD5_H +#define MD5_H + +#include "pam_cc_compat.h" + +typedef unsigned int uint32; + +struct MD5Context { + union { + uint32 i[4]; + unsigned char c[16] PAM_ATTRIBUTE_ALIGNED(4); + } buf; + uint32 bits[2]; + union { + uint32 i[16]; + unsigned char c[64] PAM_ATTRIBUTE_ALIGNED(4); + } in; +}; + +void GoodMD5Init(struct MD5Context *); +void GoodMD5Update(struct MD5Context *, unsigned const char *, unsigned); +void GoodMD5Final(unsigned char digest[16], struct MD5Context *); +void GoodMD5Transform(uint32 buf[4], uint32 const in[16]); +void BrokenMD5Init(struct MD5Context *); +void BrokenMD5Update(struct MD5Context *, unsigned const char *, unsigned); +void BrokenMD5Final(unsigned char digest[16], struct MD5Context *); +void BrokenMD5Transform(uint32 buf[4], uint32 const in[16]); + +char *Goodcrypt_md5(const char *pw, const char *salt); +char *Brokencrypt_md5(const char *pw, const char *salt); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ + +typedef struct MD5Context MD5_CTX; + +#endif /* MD5_H */ diff --git a/modules/pam_unix/md5_broken.c b/modules/pam_unix/md5_broken.c new file mode 100644 index 0000000..193daeb --- /dev/null +++ b/modules/pam_unix/md5_broken.c @@ -0,0 +1,4 @@ +#define MD5Name(x) Broken##x + +#include "md5.c" +#include "md5_crypt.c" diff --git a/modules/pam_unix/md5_crypt.c b/modules/pam_unix/md5_crypt.c new file mode 100644 index 0000000..9451f37 --- /dev/null +++ b/modules/pam_unix/md5_crypt.c @@ -0,0 +1,153 @@ +/* + * $Id$ + * + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + * Origin: Id: crypt.c,v 1.3 1995/05/30 05:42:22 rgrimes Exp + * + */ + +#include "pam_inline.h" +#include +#include +#include +#include "md5.h" + +static const unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void to64(char *s, unsigned long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v & 0x3f]; + v >>= 6; + } +} + +/* + * UNIX password + * + * Use MD5 for what it is best at... + */ + +char *MD5Name(crypt_md5)(const char *pw, const char *salt) +{ + const char *magic = "$1$"; + /* This string is magic for this algorithm. Having + * it this way, we can get better later on */ + char *passwd, *p; + const char *sp, *ep; + char buf[23]; + unsigned char final[16]; + int sl, pl, i, j; + MD5_CTX ctx, ctx1; + unsigned long l; + + /* Refine the Salt first */ + sp = salt; + + /* If it starts with the magic string, then skip that */ + if ((ep = pam_str_skip_prefix_len(sp, magic, strlen(magic))) != NULL) + sp = ep; + + /* It stops at the first '$', max 8 chars */ + for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++) + continue; + + /* get the length of the true salt */ + sl = ep - sp; + + MD5Name(MD5Init)(&ctx); + + /* The password first, since that is what is most unknown */ + MD5Name(MD5Update)(&ctx,(unsigned const char *)pw,strlen(pw)); + + /* Then our magic string */ + MD5Name(MD5Update)(&ctx,(unsigned const char *)magic,strlen(magic)); + + /* Then the raw salt */ + MD5Name(MD5Update)(&ctx,(unsigned const char *)sp,sl); + + /* Then just as many characters of the MD5(pw,salt,pw) */ + MD5Name(MD5Init)(&ctx1); + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl); + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + MD5Name(MD5Final)(final,&ctx1); + for (pl = strlen(pw); pl > 0; pl -= 16) + MD5Name(MD5Update)(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl); + + /* Don't leave anything around in vm they could use. */ + pam_overwrite_array(final); + + /* Then something really weird... */ + for (j = 0, i = strlen(pw); i; i >>= 1) + if (i & 1) + MD5Name(MD5Update)(&ctx, (unsigned const char *)final+j, 1); + else + MD5Name(MD5Update)(&ctx, (unsigned const char *)pw+j, 1); + + MD5Name(MD5Final)(final,&ctx); + + /* + * and now, just to make sure things don't run too fast + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for (i = 0; i < 1000; i++) { + MD5Name(MD5Init)(&ctx1); + if (i & 1) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + else + MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16); + + if (i % 3) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)sp,sl); + + if (i % 7) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + + if (i & 1) + MD5Name(MD5Update)(&ctx1,(unsigned const char *)final,16); + else + MD5Name(MD5Update)(&ctx1,(unsigned const char *)pw,strlen(pw)); + MD5Name(MD5Final)(final,&ctx1); + } + + p = buf; + + l = (final[0] << 16) | (final[6] << 8) | final[12]; + to64(p, l, 4); + p += 4; + l = (final[1] << 16) | (final[7] << 8) | final[13]; + to64(p, l, 4); + p += 4; + l = (final[2] << 16) | (final[8] << 8) | final[14]; + to64(p, l, 4); + p += 4; + l = (final[3] << 16) | (final[9] << 8) | final[15]; + to64(p, l, 4); + p += 4; + l = (final[4] << 16) | (final[10] << 8) | final[5]; + to64(p, l, 4); + p += 4; + l = final[11]; + to64(p, l, 2); + p += 2; + *p = '\0'; + + /* Now make the output string */ + if (asprintf(&passwd, "%s%.*s$%s", magic, sl, sp, buf) < 0) + passwd = NULL; + + /* Don't leave anything around in vm they could use. */ + pam_overwrite_array(buf); + pam_overwrite_array(final); + + return passwd; +} diff --git a/modules/pam_unix/md5_good.c b/modules/pam_unix/md5_good.c new file mode 100644 index 0000000..131e451 --- /dev/null +++ b/modules/pam_unix/md5_good.c @@ -0,0 +1,5 @@ +#define HIGHFIRST +#define MD5Name(x) Good##x + +#include "md5.c" +#include "md5_crypt.c" diff --git a/modules/pam_unix/meson.build b/modules/pam_unix/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_unix/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_unix/pam_unix.8.xml b/modules/pam_unix/pam_unix.8.xml new file mode 100644 index 0000000..d2cd198 --- /dev/null +++ b/modules/pam_unix/pam_unix.8.xml @@ -0,0 +1,498 @@ + + + + pam_unix + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_unix + Module for traditional password authentication + + + + + pam_unix.so + + ... + + + + + + + DESCRIPTION + + + This is the standard Unix authentication module. It uses standard + calls from the system's libraries to retrieve and set account + information as well as authentication. Usually this is obtained + from the /etc/passwd and the /etc/shadow file as well if shadow is + enabled. + + + + The account component performs the task of establishing the status + of the user's account and password based on the following + shadow elements: expire, last_change, max_change, + min_change, warn_change. In the case of the latter, it may offer advice + to the user on changing their password or, through the + PAM_AUTHTOKEN_REQD return, delay + giving service to the user until they have established a new password. + The entries listed above are documented in the + shadow5 + manual page. Should the user's record not contain + one or more of these entries, the corresponding + shadow check is not performed. + + + + The authentication component performs the task of checking the + users credentials (password). The default action of this module + is to not permit the user access to a service if their official + password is blank. + + + + A helper binary, + unix_chkpwd8 + , is provided + to check the user's password when it is stored in a read + protected database. This binary is very simple and will only + check the password of the user invoking it. It is called + transparently on behalf of the user by the authenticating + component of this module. In this way it is possible + for applications like + xlock1 + to work without + being setuid-root. The module, by default, will temporarily turn + off SIGCHLD handling for the duration of execution of the helper + binary. This is generally the right thing to do, as many applications + are not prepared to handle this signal from a child they didn't know + was fork()d. The module + argument can be used to suppress this temporary shielding and may be + needed for use with certain applications. + + + + The maximum length of a password supported by the pam_unix module + via the helper binary is PAM_MAX_RESP_SIZE + - currently 512 bytes. The rest of the password provided by the + conversation function to the module will be ignored. + + + + The password component of this module performs the task of updating + the user's password. The default encryption hash is taken from the + ENCRYPT_METHOD variable from + /etc/login.defs + + + + The session component of this module logs when a user logins + or leave the system. + + + + Remaining arguments, supported by others functions of this + module, are silently ignored. Other arguments are logged as + errors through + syslog3 + . + + + + + + OPTIONS + + + + debug + + + + Turns on debugging via + + syslog3 + . + + + + + + + audit + + + + A little more extreme than debug. + + + + + + + quiet + + + + Turns off informational messages namely messages about + session open and close via + + syslog3 + . + + + + + + + nullok + + + + The default action of this module is to not permit the + user access to a service if their official password is blank. + The argument overrides this default. + + + + + + nullresetok + + + + Allow users to authenticate with blank password if password reset + is enforced even if is not set. If password + reset is not required and is not set the + authentication with blank password will be denied. + + + + + + try_first_pass + + + + Before prompting the user for their password, the module first + tries the previous stacked module's password in case that + satisfies this module as well. + + + + + + use_first_pass + + + + The argument forces the module + to use a previous stacked modules password and will never prompt + the user - if no password is available or the password is not + appropriate, the user will be denied access. + + + + + + nodelay + + + + This argument can be used to discourage the authentication + component from requesting a delay should the authentication + as a whole fail. The default action is for the module to + request a delay-on-failure of the order of two seconds. + + + + + + use_authtok + + + + When password changing enforce the module to set the new + password to the one provided by a previously stacked + module (this is used in the + example of the stacking of the pam_passwdqc + module documented below). + + + + + + authtok_type=type + + + + This argument can be used to modify the password prompt + when changing passwords to include the type of the password. + Empty by default. + + + + + + nis + + + + NIS RPC is used for setting new passwords. + + + + + + remember=n + + + + The last n passwords for each + user are saved in /etc/security/opasswd + in order to force password change history and keep the user + from alternating between the same password too frequently. + The MD5 password hash algorithm is used for storing the + old passwords. + Instead of this option the pam_pwhistory + module should be used. + + + + + + shadow + + + + Try to maintain a shadow based system. + + + + + + md5 + + + + When a user changes their password next, encrypt + it with the MD5 algorithm. + + + + + + bigcrypt + + + + When a user changes their password next, + encrypt it with the DEC C2 algorithm. + + + + + + sha256 + + + + When a user changes their password next, + encrypt it with the SHA256 algorithm. The + SHA256 algorithm must be supported by the + crypt3 + function. + + + + + + sha512 + + + + When a user changes their password next, + encrypt it with the SHA512 algorithm. The + SHA512 algorithm must be supported by the + crypt3 + function. + + + + + + blowfish + + + + When a user changes their password next, + encrypt it with the blowfish algorithm. The + blowfish algorithm must be supported by the + crypt3 + function. + + + + + + gost_yescrypt + + + + When a user changes their password next, + encrypt it with the gost-yescrypt algorithm. The + gost-yescrypt algorithm must be supported by the + crypt3 + function. + + + + + + yescrypt + + + + When a user changes their password next, + encrypt it with the yescrypt algorithm. The + yescrypt algorithm must be supported by the + crypt3 + function. + + + + + + rounds=n + + + + Set the optional number of rounds of the SHA256, SHA512, + blowfish, gost-yescrypt, and yescrypt password hashing + algorithms to + n. + + + + + + broken_shadow + + + + Ignore errors reading shadow information for + users in the account management module. + + + + + + minlen=n + + + + Set a minimum password length of n + characters. The max. for DES crypt based passwords is 8 + characters. + + + + + + no_pass_expiry + + + + When set ignore password expiration as defined by the + shadow entry of the user. The option has an + effect only in case pam_unix was not used + for the authentication or it returned authentication failure + meaning that other authentication source or method succeeded. + The example can be public key authentication in + sshd. The module will return + PAM_SUCCESS instead of eventual + PAM_NEW_AUTHTOK_REQD or + PAM_AUTHTOK_EXPIRED. + + + + + + Invalid arguments are logged with + syslog3 + . + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + PAM_IGNORE + + + Ignore this module. + + + + + + + + EXAMPLES + + An example usage for /etc/pam.d/login + would be: + +# Authenticate the user +auth required pam_unix.so +# Ensure user's account and password are still active +account required pam_unix.so +# Change the user's password, but at first check the strength +# with pam_passwdqc(8) +password required pam_passwdqc.so config=/etc/passwdqc.conf +password required pam_unix.so use_authtok nullok yescrypt +session required pam_unix.so + + + + + + SEE ALSO + + + login.defs5 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_unix was written by various people. + + + + \ No newline at end of file diff --git a/modules/pam_unix/pam_unix_acct.c b/modules/pam_unix/pam_unix_acct.c new file mode 100644 index 0000000..befd3c9 --- /dev/null +++ b/modules/pam_unix/pam_unix_acct.c @@ -0,0 +1,293 @@ +/* + * pam_unix account management + * + * Copyright Elliot Lee, 1996. All rights reserved. + * Copyright Jan Rękorajski, 1999. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for time() */ +#include +#include + +#include + +#include +#include +#include + +#include "pam_i18n.h" +#include "pam_cc_compat.h" +#include "support.h" +#include "passverify.h" + +int _unix_run_verify_binary(pam_handle_t *pamh, unsigned long long ctrl, + const char *user, int *daysleft) +{ + int retval=0, child, fds[2]; + struct sigaction newsa, oldsa; + D(("running verify_binary")); + + /* create a pipe for the messages */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + pam_syslog(pamh, LOG_ERR, "Could not make pipe: %m"); + return PAM_AUTH_ERR; + } + D(("called.")); + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + } + + /* fork */ + child = fork(); + if (child == 0) { + static char *envp[] = { NULL }; + const char *args[] = { NULL, NULL, NULL, NULL }; + + /* XXX - should really tidy up PAM here too */ + + /* reopen stdout as pipe */ + if (dup2(fds[1], STDOUT_FILENO) != STDOUT_FILENO) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdout"); + _exit(PAM_AUTHINFO_UNAVAIL); + } + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_PIPE_FD) < 0) { + _exit(PAM_AUTHINFO_UNAVAIL); + } + + /* must set the real uid to 0 so the helper will not error + out if pam is called from setuid binary (su, sudo...) */ + if (setuid(0) == -1) { + uid_t euid = geteuid(); + pam_syslog(pamh, euid == 0 ? LOG_ERR : LOG_DEBUG, "setuid failed: %m"); + if (euid == 0) { + printf("-1\n"); + fflush(stdout); + _exit(PAM_AUTHINFO_UNAVAIL); + } + } + + /* exec binary helper */ + args[0] = CHKPWD_HELPER; + args[1] = user; + args[2] = "chkexpiry"; + + DIAG_PUSH_IGNORE_CAST_QUAL; + execve(CHKPWD_HELPER, (char *const *) args, envp); + DIAG_POP_IGNORE_CAST_QUAL; + + pam_syslog(pamh, LOG_ERR, "helper binary execve failed: %m"); + /* should not get here: exit with error */ + D(("helper binary is not available")); + printf("-1\n"); + fflush(stdout); + _exit(PAM_AUTHINFO_UNAVAIL); + } else { + close(fds[1]); + if (child > 0) { + char buf[32]; + int rc=0; + /* wait for helper to complete: */ + while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR); + if (rc<0) { + pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc); + retval = PAM_AUTH_ERR; + } else if (!WIFEXITED(retval)) { + pam_syslog(pamh, LOG_ERR, "unix_chkpwd abnormal exit: %d", retval); + retval = PAM_AUTH_ERR; + } else { + retval = WEXITSTATUS(retval); + rc = pam_modutil_read(fds[0], buf, sizeof(buf) - 1); + if(rc > 0) { + buf[rc] = '\0'; + if (sscanf(buf,"%d", daysleft) != 1 ) + retval = PAM_AUTH_ERR; + } + else { + pam_syslog(pamh, LOG_ERR, "read unix_chkpwd output error %d: %m", rc); + retval = PAM_AUTH_ERR; + } + } + } else { + pam_syslog(pamh, LOG_ERR, "Fork failed: %m"); + D(("fork failed")); + retval = PAM_AUTH_ERR; + } + close(fds[0]); + } + + if (off(UNIX_NOREAP, ctrl)) { + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + } + + D(("Returning %d",retval)); + return retval; +} + +/* + * PAM framework looks for this entry-point to pass control to the + * account management module. + */ + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + unsigned long long ctrl; + const void *void_uname; + const char *uname; + int retval, daysleft = -1; + char buf[256]; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + + retval = pam_get_item(pamh, PAM_USER, &void_uname); + uname = void_uname; + D(("user = `%s'", uname)); + if (retval != PAM_SUCCESS || uname == NULL) { + pam_syslog(pamh, LOG_ERR, + "could not identify user (from uid=%lu)", + (unsigned long int)getuid()); + return PAM_USER_UNKNOWN; + } + + retval = _unix_verify_user(pamh, ctrl, uname, &daysleft); + + if (on(UNIX_NO_PASS_EXPIRY, ctrl)) { + const void *pretval = NULL; + int authrv = PAM_AUTHINFO_UNAVAIL; /* authentication not called */ + + if (pam_get_data(pamh, "unix_setcred_return", &pretval) == PAM_SUCCESS + && pretval) + authrv = *(const int *)pretval; + + if (authrv != PAM_SUCCESS + && (retval == PAM_NEW_AUTHTOK_REQD || retval == PAM_AUTHTOK_EXPIRED)) + retval = PAM_SUCCESS; + } + + switch (retval) { + case PAM_ACCT_EXPIRED: + pam_syslog(pamh, LOG_NOTICE, + "account %s has expired (account expired)", + uname); + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("Your account has expired; please contact your system administrator.")); + break; + case PAM_NEW_AUTHTOK_REQD: + if (daysleft == 0) { + pam_syslog(pamh, LOG_NOTICE, + "expired password for user %s (root enforced)", + uname); + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("You are required to change your password immediately (administrator enforced).")); + } else { + pam_syslog(pamh, LOG_DEBUG, + "expired password for user %s (password aged)", + uname); + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("You are required to change your password immediately (password expired).")); + } + break; + case PAM_AUTHTOK_EXPIRED: + pam_syslog(pamh, LOG_NOTICE, + "account %s has expired (failed to change password)", + uname); + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("Your account has expired; please contact your system administrator.")); + break; + case PAM_AUTHTOK_ERR: + /* + * We ignore "password changed too early" error + * as it is relevant only for password change. + */ + retval = PAM_SUCCESS; + /* fallthrough */ + case PAM_SUCCESS: + if (daysleft >= 0) { + pam_syslog(pamh, LOG_DEBUG, + "password for user %s will expire in %d days", + uname, daysleft); +#if defined HAVE_DNGETTEXT && defined ENABLE_NLS + snprintf (buf, sizeof (buf), + dngettext(PACKAGE, + "Warning: your password will expire in %d day.", + "Warning: your password will expire in %d days.", + daysleft), + daysleft); +#else + if (daysleft == 1) + snprintf(buf, sizeof (buf), + _("Warning: your password will expire in %d day."), + daysleft); + else + snprintf(buf, sizeof (buf), + /* TRANSLATORS: only used if dngettext is not supported */ + _("Warning: your password will expire in %d days."), + daysleft); +#endif + _make_remark(pamh, ctrl, PAM_TEXT_INFO, buf); + } + } + + D(("all done")); + + return retval; +} diff --git a/modules/pam_unix/pam_unix_auth.c b/modules/pam_unix/pam_unix_auth.c new file mode 100644 index 0000000..ffb6154 --- /dev/null +++ b/modules/pam_unix/pam_unix_auth.c @@ -0,0 +1,214 @@ +/* + * pam_unix authentication management + * + * Copyright Alexander O. Yuriev, 1996. All rights reserved. + * NIS+ support by Thorsten Kukuk + * Copyright Jan Rękorajski, 1999. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "support.h" + +/* + * PAM framework looks for these entry-points to pass control to the + * authentication module. + */ + +/* Fun starts here :) + + * pam_sm_authenticate() performs UNIX/shadow authentication + * + * First, if shadow support is available, attempt to perform + * authentication using shadow passwords. If shadow is not + * available, or user does not have a shadow password, fallback + * onto a normal UNIX authentication + */ + +#define AUTH_RETURN \ +do { \ + D(("recording return code for next time [%d]", \ + retval)); \ + *ret_data = retval; \ + pam_set_data(pamh, "unix_setcred_return", \ + (void *) ret_data, setcred_free); \ + D(("done. [%s]", pam_strerror(pamh, retval))); \ + return retval; \ +} while (0) + + +static void +setcred_free (pam_handle_t *pamh UNUSED, void *ptr, int err UNUSED) +{ + free (ptr); +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + unsigned long long ctrl; + int retval, *ret_data = NULL; + const char *name; + const char *p; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + + /* Get a few bytes so we can pass our return value to + pam_sm_setcred() and pam_sm_acct_mgmt(). */ + ret_data = malloc(sizeof(int)); + if (!ret_data) { + D(("cannot malloc ret_data")); + pam_syslog(pamh, LOG_CRIT, + "pam_unix_auth: cannot allocate ret_data"); + return PAM_BUF_ERR; + } + + /* get the user'name' */ + + retval = pam_get_user(pamh, &name, NULL); + if (retval == PAM_SUCCESS) { + /* + * Various libraries at various times have had bugs related to + * '+' or '-' as the first character of a user name. Don't + * allow these characters here. + */ + if (name[0] == '-' || name[0] == '+') { + pam_syslog(pamh, LOG_NOTICE, "bad username [%s]", name); + retval = PAM_USER_UNKNOWN; + AUTH_RETURN; + } + if (on(UNIX_DEBUG, ctrl)) + pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained", name); + } else { + if (retval == PAM_CONV_AGAIN) { + D(("pam_get_user/conv() function is not ready yet")); + /* it is safe to resume this function so we translate this + * retval to the value that indicates we're happy to resume. + */ + retval = PAM_INCOMPLETE; + } else if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_DEBUG, "could not obtain username"); + } + AUTH_RETURN; + } + + /* if this user does not have a password... */ + + if (_unix_blankpasswd(pamh, ctrl, name)) { + pam_syslog(pamh, LOG_DEBUG, "user [%s] has blank password; authenticated without it", name); + name = NULL; + retval = PAM_SUCCESS; + AUTH_RETURN; + } + /* get this user's authentication token */ + + retval = pam_get_authtok(pamh, PAM_AUTHTOK, &p , NULL); + if (retval != PAM_SUCCESS) { + if (retval != PAM_CONV_AGAIN) { + pam_syslog(pamh, LOG_CRIT, + "auth could not identify password for [%s]", name); + } else { + D(("conversation function is not ready yet")); + /* + * it is safe to resume this function so we translate this + * retval to the value that indicates we're happy to resume. + */ + retval = PAM_INCOMPLETE; + } + name = NULL; + AUTH_RETURN; + } + D(("user=%s, password=[%s]", name, p)); + + /* verify the password of this user */ + retval = _unix_verify_password(pamh, name, p, ctrl); + name = p = NULL; + + AUTH_RETURN; +} + + +/* + * The only thing _pam_set_credentials_unix() does is initialization of + * UNIX group IDs. + * + * Well, everybody but me on linux-pam is convinced that it should not + * initialize group IDs, so I am not doing it but don't say that I haven't + * warned you. -- AOY + */ + +int +pam_sm_setcred (pam_handle_t *pamh, int flags, + int argc, const char **argv) +{ + int retval; + const void *pretval = NULL; + unsigned long long ctrl; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + + retval = PAM_SUCCESS; + + D(("recovering return code from auth call")); + /* We will only find something here if UNIX_LIKE_AUTH is set -- + don't worry about an explicit check of argv. */ + if (on(UNIX_LIKE_AUTH, ctrl) + && pam_get_data(pamh, "unix_setcred_return", &pretval) == PAM_SUCCESS + && pretval) { + retval = *(const int *)pretval; + pam_set_data(pamh, "unix_setcred_return", NULL, NULL); + D(("recovered data indicates that old retval was %d", retval)); + } + + return retval; +} diff --git a/modules/pam_unix/pam_unix_passwd.c b/modules/pam_unix/pam_unix_passwd.c new file mode 100644 index 0000000..4a3784a --- /dev/null +++ b/modules/pam_unix/pam_unix_passwd.c @@ -0,0 +1,867 @@ +/* + * pam_unix password management + * + * Main coding by Elliot Lee , Red Hat Software. + * Copyright (C) 1996. + * Copyright (c) Jan Rękorajski, 1999. + * Copyright (c) Red Hat, Inc., 2007, 2008. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for time() */ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "pam_inline.h" +#include "pam_i18n.h" +#include "pam_cc_compat.h" +#include "md5.h" +#include "support.h" +#include "passverify.h" +#include "bigcrypt.h" + +#ifdef HAVE_NIS +# include +# include +# include + +# include "yppasswd.h" + +# if !defined(HAVE_DECL_GETRPCPORT) &&!defined(HAVE_RPCB_GETADDR) +extern int getrpcport(const char *host, unsigned long prognum, + unsigned long versnum, unsigned int proto); +# endif /* GNU libc 2.1 */ +#endif + +/* + How it works: + Gets in username (has to be done) from the calling program + Does authentication of user (only if we are not running as root) + Gets new password/checks for sanity + Sets it. + */ + +#define MAX_PASSWD_TRIES 3 + +#ifdef HAVE_NIS +#ifdef HAVE_RPCB_GETADDR +static unsigned short +__taddr2port (const struct netconfig *nconf, const struct netbuf *nbuf) +{ + unsigned short port = 0; + struct __rpc_sockinfo si; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + if (!__rpc_nconf2sockinfo(nconf, &si)) + return 0; + + switch (si.si_af) + { + case AF_INET: + sin = nbuf->buf; + port = sin->sin_port; + break; + case AF_INET6: + sin6 = nbuf->buf; + port = sin6->sin6_port; + break; + default: + break; + } + + return htons (port); +} +#endif + +static char *getNISserver(pam_handle_t *pamh, unsigned long long ctrl) +{ + char *master; + char *domainname; + int port, err; +#if defined(HAVE_RPCB_GETADDR) + struct netconfig *nconf; + struct netbuf svcaddr; + char addrbuf[INET6_ADDRSTRLEN]; + void *handle; + int found; +#endif + + +#ifdef HAVE_YP_GET_DEFAULT_DOMAIN + if ((err = yp_get_default_domain(&domainname)) != 0) { + pam_syslog(pamh, LOG_WARNING, "can't get local yp domain: %s", + yperr_string(err)); + return NULL; + } +#elif defined(HAVE_GETDOMAINNAME) + char domainname_res[256]; + + if (getdomainname (domainname_res, sizeof (domainname_res)) == 0) + { + if (strcmp (domainname_res, "(none)") == 0) + { + /* If domainname is not set, some systems will return "(none)" */ + domainname_res[0] = '\0'; + } + domainname = domainname_res; + } + else domainname = NULL; +#endif + + if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) { + pam_syslog(pamh, LOG_WARNING, "can't find the master ypserver: %s", + yperr_string(err)); + return NULL; + } +#ifdef HAVE_RPCB_GETADDR + svcaddr.len = 0; + svcaddr.maxlen = sizeof (addrbuf); + svcaddr.buf = addrbuf; + port = 0; + found = 0; + + handle = setnetconfig(); + while ((nconf = getnetconfig(handle)) != NULL) { + if (!strcmp(nconf->nc_proto, "udp")) { + if (rpcb_getaddr(YPPASSWDPROG, YPPASSWDPROC_UPDATE, + nconf, &svcaddr, master)) { + port = __taddr2port (nconf, &svcaddr); + endnetconfig (handle); + found=1; + break; + } + + if (rpc_createerr.cf_stat != RPC_UNKNOWNHOST) { + clnt_pcreateerror (master); + pam_syslog (pamh, LOG_ERR, + "rpcb_getaddr (%s) failed!", master); + return NULL; + } + } + } + + if (!found) { + pam_syslog (pamh, LOG_ERR, + "Cannot find suitable transport for protocol 'udp'"); + return NULL; + } +#else + port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP); +#endif + if (port == 0) { + pam_syslog(pamh, LOG_WARNING, + "yppasswdd not running on NIS master host"); + return NULL; + } + if (port >= IPPORT_RESERVED) { + pam_syslog(pamh, LOG_WARNING, + "yppasswd daemon running on illegal port"); + return NULL; + } + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_DEBUG, "Use NIS server on %s with port %d", + master, port); + } + return master; +} +#endif + +#ifdef WITH_SELINUX + +static int _unix_run_update_binary(pam_handle_t *pamh, unsigned long long ctrl, const char *user, + const char *fromwhat, const char *towhat, int remember) +{ + int retval, child, fds[2]; + struct sigaction newsa, oldsa; + + D(("called.")); + /* create a pipe for the password */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + return PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + } + + /* fork */ + child = fork(); + if (child == 0) { + static char *envp[] = { NULL }; + const char *args[] = { NULL, NULL, NULL, NULL, NULL, NULL }; + char buffer[16]; + + /* XXX - should really tidy up PAM here too */ + + /* reopen stdin as pipe */ + if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin"); + _exit(PAM_AUTHINFO_UNAVAIL); + } + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD) < 0) { + _exit(PAM_AUTHINFO_UNAVAIL); + } + + /* exec binary helper */ + args[0] = UPDATE_HELPER; + args[1] = user; + args[2] = "update"; + if (on(UNIX_SHADOW, ctrl)) + args[3] = "1"; + else + args[3] = "0"; + + snprintf(buffer, sizeof(buffer), "%d", remember); + args[4] = buffer; + + DIAG_PUSH_IGNORE_CAST_QUAL; + execve(UPDATE_HELPER, (char *const *) args, envp); + DIAG_POP_IGNORE_CAST_QUAL; + + /* should not get here: exit with error */ + pam_syslog(pamh, LOG_ERR, "failed to execute %s: %m", UPDATE_HELPER); + _exit(PAM_AUTHINFO_UNAVAIL); + } else if (child > 0) { + /* wait for child */ + /* if the stored password is NULL */ + int rc=0; + if (fromwhat) { + int len = strlen(fromwhat); + + if (len > PAM_MAX_RESP_SIZE) + len = PAM_MAX_RESP_SIZE; + pam_modutil_write(fds[1], fromwhat, len); + } + pam_modutil_write(fds[1], "", 1); + if (towhat) { + int len = strlen(towhat); + + if (len > PAM_MAX_RESP_SIZE) + len = PAM_MAX_RESP_SIZE; + pam_modutil_write(fds[1], towhat, len); + } + pam_modutil_write(fds[1], "", 1); + + close(fds[0]); /* close here to avoid possible SIGPIPE above */ + close(fds[1]); + /* wait for helper to complete: */ + while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR); + if (rc<0) { + pam_syslog(pamh, LOG_ERR, "unix_update waitpid failed: %m"); + retval = PAM_AUTHTOK_ERR; + } else if (!WIFEXITED(retval)) { + pam_syslog(pamh, LOG_ERR, "unix_update abnormal exit: %d", retval); + retval = PAM_AUTHTOK_ERR; + } else { + retval = WEXITSTATUS(retval); + } + } else { + D(("fork failed")); + close(fds[0]); + close(fds[1]); + retval = PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + } + + return retval; +} +#endif + +static int check_old_password(const char *forwho, const char *newpass) +{ + char *buf = NULL; + char *s_pas; + int retval = PAM_SUCCESS; + FILE *opwfile; + size_t n = 0; + size_t len = strlen(forwho); + + opwfile = fopen(OLD_PASSWORDS_FILE, "re"); + if (opwfile == NULL) + return PAM_ABORT; + + for (; getline(&buf, &n, opwfile) != -1; pam_overwrite_n(buf, n)) { + if (!strncmp(buf, forwho, len) && buf[len] == ':') { + char *sptr; + buf[strlen(buf) - 1] = '\0'; + /* s_luser = */ strtok_r(buf, ":", &sptr); + /* s_uid = */ strtok_r(NULL, ":", &sptr); + /* s_npas = */ strtok_r(NULL, ":", &sptr); + s_pas = strtok_r(NULL, ",", &sptr); + while (s_pas != NULL) { + char *md5pass = Goodcrypt_md5(newpass, s_pas); + if (md5pass == NULL || !strcmp(md5pass, s_pas)) { + _pam_delete(md5pass); + retval = PAM_AUTHTOK_ERR; + break; + } + s_pas = strtok_r(NULL, ",", &sptr); + _pam_delete(md5pass); + } + break; + } + } + pam_overwrite_n(buf, n); + free(buf); + fclose(opwfile); + + return retval; +} + +static int _do_setpass(pam_handle_t* pamh, const char *forwho, + const char *fromwhat, + char *towhat, unsigned long long ctrl, int remember) +{ + struct passwd *pwd = NULL; + int retval = 0; + int unlocked = 0; + + D(("called")); + + pwd = getpwnam(forwho); + + if (pwd == NULL) { + retval = PAM_AUTHTOK_ERR; + goto done; + } + + if (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, forwho, 0, 1)) { +#ifdef HAVE_NIS + char *master; + + if ((master=getNISserver(pamh, ctrl)) != NULL) { + struct timeval timeout; + struct yppasswd yppwd; + CLIENT *clnt; + int status; + enum clnt_stat err; + + /* Unlock passwd file to avoid deadlock */ + unlock_pwdf(); + unlocked = 1; + + /* Initialize password information */ + yppwd.newpw.pw_passwd = pwd->pw_passwd; + yppwd.newpw.pw_name = pwd->pw_name; + yppwd.newpw.pw_uid = pwd->pw_uid; + yppwd.newpw.pw_gid = pwd->pw_gid; + yppwd.newpw.pw_gecos = pwd->pw_gecos; + yppwd.newpw.pw_dir = pwd->pw_dir; + yppwd.newpw.pw_shell = pwd->pw_shell; + yppwd.oldpass = fromwhat ? strdup (fromwhat) : strdup (""); + yppwd.newpw.pw_passwd = towhat; + + D(("Set password %s for %s", yppwd.newpw.pw_passwd, forwho)); + + /* The yppasswd.x file said `unix authentication required', + * so I added it. This is the only reason it is in here. + * My yppasswdd doesn't use it, but maybe some others out there + * do. --okir + */ + clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp"); + clnt->cl_auth = authunix_create_default(); + memset((char *) &status, '\0', sizeof(status)); + timeout.tv_sec = 25; + timeout.tv_usec = 0; + err = clnt_call(clnt, YPPASSWDPROC_UPDATE, + (xdrproc_t) xdr_yppasswd, (char *) &yppwd, + (xdrproc_t) xdr_int, (char *) &status, + timeout); + + free (yppwd.oldpass); + + if (err) { + _make_remark(pamh, ctrl, PAM_TEXT_INFO, + clnt_sperrno(err)); + } else if (status) { + D(("Error while changing NIS password.\n")); + } + D(("The password has%s been changed on %s.", + (err || status) ? " not" : "", master)); + pam_syslog(pamh, LOG_NOTICE, "password%s changed for %s on %s", + (err || status) ? " not" : "", pwd->pw_name, master); + + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + if (err || status) { + _make_remark(pamh, ctrl, PAM_TEXT_INFO, + _("NIS password could not be changed.")); + retval = PAM_TRY_AGAIN; + } +#ifdef PAM_DEBUG + sleep(5); +#endif + } else { + retval = PAM_TRY_AGAIN; + } +#else + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_DEBUG, "No NIS support available"); + } + + retval = PAM_TRY_AGAIN; +#endif + } + + if (_unix_comesfromsource(pamh, forwho, 1, 0)) { + if(unlocked) { + if (lock_pwdf() != PAM_SUCCESS) { + return PAM_AUTHTOK_LOCK_BUSY; + } + } +#ifdef WITH_SELINUX + if (unix_selinux_confined()) + return _unix_run_update_binary(pamh, ctrl, forwho, fromwhat, towhat, remember); +#endif + /* first, save old password */ + if (save_old_password(pamh, forwho, fromwhat, remember)) { + retval = PAM_AUTHTOK_ERR; + goto done; + } + if (on(UNIX_SHADOW, ctrl) || is_pwd_shadowed(pwd)) { + retval = unix_update_shadow(pamh, forwho, towhat); + if (retval == PAM_SUCCESS) + if (!is_pwd_shadowed(pwd)) + retval = unix_update_passwd(pamh, forwho, "x"); + } else { + retval = unix_update_passwd(pamh, forwho, towhat); + } + } + + +done: + unlock_pwdf(); + + return retval; +} + +static int _unix_verify_shadow(pam_handle_t *pamh, const char *user, unsigned long long ctrl) +{ + struct passwd *pwent = NULL; /* Password and shadow password */ + struct spwd *spent = NULL; /* file entries for the user */ + int daysleft; + int retval; + + retval = get_account_info(pamh, user, &pwent, &spent); + if (retval == PAM_USER_UNKNOWN) { + return retval; + } + + if (retval == PAM_SUCCESS && spent == NULL) + return PAM_SUCCESS; + + if (retval == PAM_UNIX_RUN_HELPER) { + retval = _unix_run_verify_binary(pamh, ctrl, user, &daysleft); + if (retval == PAM_AUTH_ERR || retval == PAM_USER_UNKNOWN) + return retval; + } + else if (retval == PAM_SUCCESS) + retval = check_shadow_expiry(pamh, spent, &daysleft); + + if (on(UNIX__IAMROOT, ctrl) || retval == PAM_NEW_AUTHTOK_REQD) + return PAM_SUCCESS; + + return retval; +} + +static int _pam_unix_approve_pass(pam_handle_t * pamh + ,unsigned long long ctrl + ,const char *pass_old + ,const char *pass_new, + int pass_min_len) +{ + const void *user; + const char *remark = NULL; + int retval = PAM_SUCCESS; + + D(("&new=%p, &old=%p", pass_old, pass_new)); + D(("new=[%s]", pass_new)); + D(("old=[%s]", pass_old)); + + if (pass_new == NULL || (pass_old && !strcmp(pass_old, pass_new))) { + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_DEBUG, "bad authentication token"); + } + _make_remark(pamh, ctrl, PAM_ERROR_MSG, pass_new == NULL ? + _("No password has been supplied.") : + _("The password has not been changed.")); + return PAM_AUTHTOK_ERR; + } + /* + * if one wanted to hardwire authentication token strength + * checking this would be the place - AGM + */ + + retval = pam_get_item(pamh, PAM_USER, &user); + if (retval != PAM_SUCCESS) { + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_ERR, "Can not get username"); + return PAM_AUTHTOK_ERR; + } + } + + if (strlen(pass_new) > PAM_MAX_RESP_SIZE) { + remark = _("You must choose a shorter password."); + D(("length exceeded [%s]", remark)); + } else if (off(UNIX__IAMROOT, ctrl)) { + if ((int)strlen(pass_new) < pass_min_len) { + remark = _("You must choose a longer password."); + D(("length check [%s]", remark)); + } + if (on(UNIX_REMEMBER_PASSWD, ctrl)) { + if ((retval = check_old_password(user, pass_new)) == PAM_AUTHTOK_ERR) + remark = _("Password has been already used. Choose another."); + if (retval == PAM_ABORT) { + pam_syslog(pamh, LOG_ERR, "can't open %s file to check old passwords", + OLD_PASSWORDS_FILE); + return retval; + } + } + } + if (remark) { + _make_remark(pamh, ctrl, PAM_ERROR_MSG, remark); + retval = PAM_AUTHTOK_ERR; + } + return retval; +} + +int +pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + unsigned long long ctrl, lctrl; + int retval; + int remember = -1; + int rounds = 0; + int pass_min_len = 0; + struct passwd *pwd; + + /* */ + const char *user; + const void *item; + const char *pass_old, *pass_new; + /* */ + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, &remember, &rounds, &pass_min_len, + argc, argv); + + /* + * First get the name of a user + */ + retval = pam_get_user(pamh, &user, NULL); + if (retval == PAM_SUCCESS) { + /* + * Various libraries at various times have had bugs related to + * '+' or '-' as the first character of a user name. Don't + * allow them. + */ + if (user[0] == '-' || user[0] == '+') { + pam_syslog(pamh, LOG_NOTICE, "bad username [%s]", user); + return PAM_USER_UNKNOWN; + } + if (retval == PAM_SUCCESS && on(UNIX_DEBUG, ctrl)) + pam_syslog(pamh, LOG_DEBUG, "username [%s] obtained", + user); + } else { + if (on(UNIX_DEBUG, ctrl)) + pam_syslog(pamh, LOG_DEBUG, + "password - could not identify user"); + return retval; + } + + D(("Got username of %s", user)); + + /* + * Before we do anything else, check to make sure that the user's + * info is in one of the databases we can modify from this module, + * which currently is 'files' and 'nis'. We have to do this because + * getpwnam() doesn't tell you *where* the information it gives you + * came from, nor should it. That's our job. + */ + if (_unix_getpwnam(pamh, user, 1, on(UNIX_NIS, ctrl), &pwd) == 0) { + pam_syslog(pamh, LOG_DEBUG, + "user \"%s\" does not exist in /etc/passwd%s", + user, on(UNIX_NIS, ctrl) ? " or NIS" : ""); + return PAM_USER_UNKNOWN; + } else if (pwd == NULL) { + pam_syslog(pamh, LOG_DEBUG, + "user \"%s\" has corrupted passwd entry", + user); + return PAM_USER_UNKNOWN; + } + + /* + * This is not an AUTH module! + */ + if (on(UNIX__NONULL, ctrl)) + set(UNIX__NULLOK, ctrl); + + if (on(UNIX__PRELIM, ctrl)) { + /* + * obtain and verify the current password (OLDAUTHTOK) for + * the user. + */ + D(("prelim check")); + + if (_unix_blankpasswd(pamh, ctrl, user)) { + return PAM_SUCCESS; + } else if (off(UNIX__IAMROOT, ctrl) || + (on(UNIX_NIS, ctrl) && _unix_comesfromsource(pamh, user, 0, 1))) { + /* instruct user what is happening */ + if (off(UNIX__QUIET, ctrl)) { + retval = pam_info(pamh, _("Changing password for %s."), user); + if (retval != PAM_SUCCESS) + return retval; + } + retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass_old, NULL); + + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, + "password - (old) token not obtained"); + return retval; + } + /* verify that this is the password for this user */ + + retval = _unix_verify_password(pamh, user, pass_old, ctrl); + } else { + D(("process run by root so do nothing this time around")); + pass_old = NULL; + retval = PAM_SUCCESS; /* root doesn't have to */ + } + + if (retval != PAM_SUCCESS) { + D(("Authentication failed")); + pass_old = NULL; + return retval; + } + pass_old = NULL; + retval = _unix_verify_shadow(pamh,user, ctrl); + if (retval == PAM_AUTHTOK_ERR) { + if (off(UNIX__IAMROOT, ctrl)) + _make_remark(pamh, ctrl, PAM_ERROR_MSG, + _("You must wait longer to change your password.")); + else + retval = PAM_SUCCESS; + } + } else if (on(UNIX__UPDATE, ctrl)) { + /* + * tpass is used below to store the _pam_md() return; it + * should be _pam_delete()'d. + */ + + char *tpass = NULL; + int retry = 0; + + /* + * obtain the proposed password + */ + + D(("do update")); + + /* + * get the old token back. NULL was ok only if root [at this + * point we assume that this has already been enforced on a + * previous call to this function]. + */ + + retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &item); + + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "user not authenticated"); + return retval; + } + pass_old = item; + D(("pass_old [%s]", pass_old)); + + D(("get new password now")); + + lctrl = ctrl; + + if (on(UNIX_USE_AUTHTOK, lctrl)) { + set(UNIX_USE_FIRST_PASS, lctrl); + } + if (on(UNIX_USE_FIRST_PASS, lctrl)) { + retry = MAX_PASSWD_TRIES-1; + } + retval = PAM_AUTHTOK_ERR; + while ((retval != PAM_SUCCESS) && (retry++ < MAX_PASSWD_TRIES)) { + /* + * use_authtok is to force the use of a previously entered + * password -- needed for pluggable password strength checking + */ + + retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass_new, NULL); + + if (retval != PAM_SUCCESS) { + if (on(UNIX_DEBUG, ctrl)) { + pam_syslog(pamh, LOG_ERR, + "password - new password not obtained"); + } + pass_old = NULL; /* tidy up */ + return retval; + } + D(("returned to _unix_chauthtok")); + + /* + * At this point we know who the user is and what they + * propose as their new password. Verify that the new + * password is acceptable. + */ + + if (*pass_new == '\0') { /* "\0" password = NULL */ + pass_new = NULL; + } + retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, + pass_new, pass_min_len); + + if (retval != PAM_SUCCESS) { + pam_set_item(pamh, PAM_AUTHTOK, NULL); + } + } + + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, + "new password not acceptable"); + pass_new = pass_old = NULL; /* tidy up */ + return retval; + } + if (lock_pwdf() != PAM_SUCCESS) { + return PAM_AUTHTOK_LOCK_BUSY; + } + + if (pass_old) { + retval = _unix_verify_password(pamh, user, pass_old, ctrl); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "user password changed by another process"); + unlock_pwdf(); + return retval; + } + } + + retval = _unix_verify_shadow(pamh, user, ctrl); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "user shadow entry expired"); + unlock_pwdf(); + return retval; + } + + retval = _pam_unix_approve_pass(pamh, ctrl, pass_old, pass_new, + pass_min_len); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, + "new password not acceptable 2"); + pass_new = pass_old = NULL; /* tidy up */ + unlock_pwdf(); + return retval; + } + + /* + * By reaching here we have approved the passwords and must now + * rebuild the password database file. + */ + + /* + * First we encrypt the new password. + */ + + tpass = create_password_hash(pamh, pass_new, ctrl, rounds); + if (tpass == NULL) { + pam_syslog(pamh, LOG_CRIT, + "crypt() failure or out of memory for password"); + pass_new = pass_old = NULL; /* tidy up */ + unlock_pwdf(); + return PAM_BUF_ERR; + } + + D(("password processed")); + + /* update the password database(s) -- race conditions..? */ + + retval = _do_setpass(pamh, user, pass_old, tpass, ctrl, + remember); + /* _do_setpass has called unlock_pwdf for us */ + + _pam_delete(tpass); + pass_old = pass_new = NULL; + } else { /* something has broken with the module */ + pam_syslog(pamh, LOG_CRIT, + "password received unknown request"); + retval = PAM_ABORT; + } + + D(("retval was %d", retval)); + + return retval; +} diff --git a/modules/pam_unix/pam_unix_sess.c b/modules/pam_unix/pam_unix_sess.c new file mode 100644 index 0000000..3534948 --- /dev/null +++ b/modules/pam_unix/pam_unix_sess.c @@ -0,0 +1,134 @@ +/* + * pam_unix session management + * + * Copyright Alexander O. Yuriev, 1996. All rights reserved. + * Copyright Jan Rękorajski, 1999. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "support.h" + +/* + * PAM framework looks for these entry-points to pass control to the + * session module. + */ + +int +pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + const char *user_name, *service; + unsigned long long ctrl; + int retval; + const char *login_name; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + + retval = pam_get_item(pamh, PAM_USER, (const void **) &user_name); + if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, + "open_session - error recovering username"); + return PAM_SESSION_ERR; /* How did we get authenticated with + no username?! */ + } + retval = pam_get_item(pamh, PAM_SERVICE, (const void **) &service); + if (service == NULL || *service == '\0' || retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_CRIT, + "open_session - error recovering service"); + return PAM_SESSION_ERR; + } + login_name = pam_modutil_getlogin(pamh); + if (login_name == NULL) { + login_name = ""; + } + if (off (UNIX_QUIET, ctrl)) { + char uid[32]; + struct passwd *pwd = pam_modutil_getpwnam (pamh, user_name); + if (pwd == NULL) { + snprintf (uid, 32, "getpwnam error"); + } + else { + snprintf (uid, 32, "%u", pwd->pw_uid); + } + pam_syslog(pamh, LOG_INFO, "session opened for user %s(uid=%s) by %s(uid=%lu)", user_name, uid, login_name, (unsigned long)getuid()); + } + return PAM_SUCCESS; +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + const char *user_name, *service; + unsigned long long ctrl; + int retval; + + D(("called.")); + + ctrl = _set_ctrl(pamh, flags, NULL, NULL, NULL, argc, argv); + + retval = pam_get_item(pamh, PAM_USER, (const void **) &user_name); + if (user_name == NULL || *user_name == '\0' || retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, + "close_session - error recovering username"); + return PAM_SESSION_ERR; /* How did we get authenticated with + no username?! */ + } + retval = pam_get_item(pamh, PAM_SERVICE, (const void **) &service); + if (service == NULL || *service == '\0' || retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_CRIT, + "close_session - error recovering service"); + return PAM_SESSION_ERR; + } + if (off (UNIX_QUIET, ctrl)) + pam_syslog(pamh, LOG_INFO, "session closed for user %s", + user_name); + + return PAM_SUCCESS; +} diff --git a/modules/pam_unix/passverify.c b/modules/pam_unix/passverify.c new file mode 100644 index 0000000..e8d0b91 --- /dev/null +++ b/modules/pam_unix/passverify.c @@ -0,0 +1,1234 @@ +/* + * Copyright information at end of file. + */ +#include "config.h" +#include +#include +#include "support.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_CRYPT_H +#include +#endif + +#include "pam_cc_compat.h" +#include "pam_inline.h" +#include "md5.h" +#include "bigcrypt.h" +#include "passverify.h" + +#ifdef WITH_SELINUX +#include +#define SELINUX_ENABLED (is_selinux_enabled()>0) +#else +#define SELINUX_ENABLED 0 +#endif + +#ifdef HELPER_COMPILE +#define pam_modutil_getpwnam(h,n) getpwnam(n) +#define pam_modutil_getspnam(h,n) getspnam(n) +#define pam_syslog(h,a,b,c) helper_log_err(a,b,c) +#else +#include +#include +#endif + +#if defined(USE_LCKPWDF) && !defined(HAVE_LCKPWDF) +# include "./lckpwdf.-c" +#endif + +static void +strip_hpux_aging(char *hash) +{ + static const char *const valid = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789./"; + if ((*hash != '$') && (strlen(hash) > 13)) { + for (hash += 13; *hash != '\0'; hash++) { + if (strchr(valid, *hash) == NULL) { + *hash = '\0'; + break; + } + } + } +} + +PAMH_ARG_DECL(int verify_pwd_hash, + const char *p, char *hash, unsigned int nullok) +{ + size_t hash_len; + char *pp = NULL; + int retval; + D(("called")); + + strip_hpux_aging(hash); + hash_len = strlen(hash); + + if (p && p[0] == '\0' && !nullok) { + /* The passed password is empty */ + retval = PAM_AUTH_ERR; + } else if (!hash_len) { + /* the stored password is NULL */ + if (p && p[0] == '\0' && nullok) { /* this means we've succeeded */ + D(("user has empty password - access granted")); + retval = PAM_SUCCESS; + } else { + D(("user has empty password - access denied")); + retval = PAM_AUTH_ERR; + } + } else if (!p || *hash == '*' || *hash == '!') { + retval = PAM_AUTH_ERR; + } else { + if (pam_str_skip_prefix(hash, "$1$") != NULL) { + pp = Goodcrypt_md5(p, hash); + if (pp && !pam_consttime_streq(pp, hash)) { + _pam_delete(pp); + pp = Brokencrypt_md5(p, hash); + } + } else if (*hash != '$' && hash_len >= 13) { + pp = bigcrypt(p, hash); + if (pp && hash_len == 13 && strlen(pp) > hash_len) { + pam_overwrite_string(pp + hash_len); + } + } else { + /* + * Ok, we don't know the crypt algorithm, but maybe + * libcrypt knows about it? We should try it. + */ +#if defined(CRYPT_CHECKSALT_AVAILABLE) && CRYPT_CHECKSALT_AVAILABLE + /* Get the status of the hash from checksalt */ + int retval_checksalt = crypt_checksalt(hash); + + /* + * Check for hashing methods that are disabled by + * libcrypt configuration and/or system preset. + */ + if (retval_checksalt == CRYPT_SALT_METHOD_DISABLED) { + /* + * pam_syslog() needs a pam handle, + * but that's not available here. + */ + pam_syslog(pamh, LOG_ERR, + "The support for password hash \"%.6s\" " + "has been disabled in libcrypt " + "configuration.", + hash); + } + /* + * Check for malformed hashes, like descrypt hashes + * starting with "$2...", which might have been + * generated by unsafe base64 encoding functions + * as used in glibc <= 2.16. + * Such hashes are likely to be rejected by many + * recent implementations of libcrypt. + */ + if (retval_checksalt == CRYPT_SALT_INVALID) { + pam_syslog(pamh, LOG_ERR, + "The password hash \"%.6s\" is unknown to " + "libcrypt.", + hash); + } +#else +#ifndef HELPER_COMPILE + (void)pamh; +#endif +#endif +#ifdef HAVE_CRYPT_R + struct crypt_data *cdata; + cdata = calloc(1, sizeof(*cdata)); + if (cdata != NULL) { + pp = x_strdup(crypt_r(p, hash, cdata)); + pam_overwrite_object(cdata); + free(cdata); + } +#else + pp = x_strdup(crypt(p, hash)); +#endif + } + p = NULL; /* no longer needed here */ + + /* the moment of truth -- do we agree with the password? */ + D(("comparing state of pp[%s] and hash[%s]", pp ? pp : "(null)", hash)); + + if (pp && pam_consttime_streq(pp, hash)) { + retval = PAM_SUCCESS; + } else { + retval = PAM_AUTH_ERR; + } + } + + if (pp) + _pam_delete(pp); + D(("done [%d].", retval)); + + return retval; +} + +int +is_pwd_shadowed(const struct passwd *pwd) +{ + if (pwd != NULL) { + if (strcmp(pwd->pw_passwd, "x") == 0) { + return 1; + } + if ((pwd->pw_passwd[0] == '#') && + (pwd->pw_passwd[1] == '#') && + (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)) { + return 1; + } + } + return 0; +} + +PAMH_ARG_DECL(int get_account_info, + const char *name, struct passwd **pwd, struct spwd **spwdent) +{ + /* UNIX passwords area */ + *pwd = pam_modutil_getpwnam(pamh, name); /* Get password file entry... */ + *spwdent = NULL; + + if (*pwd != NULL) { + if (strcmp((*pwd)->pw_passwd, "*NP*") == 0) + { /* NIS+ */ +#ifdef HELPER_COMPILE + uid_t save_euid, save_uid; + + save_euid = geteuid(); + save_uid = getuid(); + if (save_uid == (*pwd)->pw_uid) { + if (setreuid(save_euid, save_uid)) + return PAM_CRED_INSUFFICIENT; + } else { + if (setreuid(0, -1)) + return PAM_CRED_INSUFFICIENT; + if (setreuid(-1, (*pwd)->pw_uid)) { + if (setreuid(-1, 0) + || setreuid(0, -1) + || setreuid(-1, (*pwd)->pw_uid)) { + return PAM_CRED_INSUFFICIENT; + } + } + } + + *spwdent = pam_modutil_getspnam(pamh, name); + if (save_uid == (*pwd)->pw_uid) { + if (setreuid(save_uid, save_euid)) + return PAM_CRED_INSUFFICIENT; + } else { + if (setreuid(-1, 0) + || setreuid(save_uid, -1) + || setreuid(-1, save_euid)) + return PAM_CRED_INSUFFICIENT; + } + + if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL) + return PAM_AUTHINFO_UNAVAIL; +#else + /* we must run helper for NIS+ passwords */ + return PAM_UNIX_RUN_HELPER; +#endif + } else if (is_pwd_shadowed(*pwd)) { +#ifdef HELPER_COMPILE + /* + * shadow password file entry for this user, + * if shadowing is enabled + */ + *spwdent = getspnam(name); + if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL) + return PAM_AUTHINFO_UNAVAIL; +#else + /* + * The helper has to be invoked to deal with + * the shadow password file entry. + */ + return PAM_UNIX_RUN_HELPER; +#endif + } + } else { + return PAM_USER_UNKNOWN; + } + return PAM_SUCCESS; +} + +PAMH_ARG_DECL(int get_pwd_hash, + const char *name, struct passwd **pwd, char **hash) +{ + int retval; + struct spwd *spwdent = NULL; + + retval = get_account_info(PAMH_ARG(name, pwd, &spwdent)); + if (retval != PAM_SUCCESS) { + return retval; + } + + if (spwdent) + *hash = x_strdup(spwdent->sp_pwdp); + else + *hash = x_strdup((*pwd)->pw_passwd); + if (*hash == NULL) + return PAM_BUF_ERR; + + return PAM_SUCCESS; +} + +/* + * invariant: 0 <= num1 + * invariant: 0 <= num2 + */ +static int +subtract(long num1, long num2) +{ + long value = num1 - num2; + if (value < INT_MIN) + return INT_MIN; + if (value > INT_MAX) + return INT_MAX; + return (int)value; +} + +PAMH_ARG_DECL(int check_shadow_expiry, + struct spwd *spent, int *daysleft) +{ + long int curdays, passed; + *daysleft = -1; + curdays = (long int)(time(NULL) / (60 * 60 * 24)); + D(("today is %ld, last change %ld", curdays, spent->sp_lstchg)); + if (spent->sp_expire >= 0 && curdays >= spent->sp_expire) { + D(("account expired")); + return PAM_ACCT_EXPIRED; + } + if (spent->sp_lstchg == 0) { + D(("need a new password")); + *daysleft = 0; + return PAM_NEW_AUTHTOK_REQD; + } + if (spent->sp_lstchg < 0) { + D(("password aging disabled")); + return PAM_SUCCESS; + } + if (curdays < spent->sp_lstchg) { + pam_syslog(pamh, LOG_DEBUG, + "account %s has password changed in future", + spent->sp_namp); + return PAM_SUCCESS; + } + passed = curdays - spent->sp_lstchg; + if (spent->sp_max >= 0) { + if (spent->sp_inact >= 0) { + long inact = spent->sp_max < LONG_MAX - spent->sp_inact ? + spent->sp_max + spent->sp_inact : LONG_MAX; + if (passed >= inact) { + *daysleft = subtract(inact, passed); + D(("authtok expired")); + return PAM_AUTHTOK_EXPIRED; + } + } + if (passed >= spent->sp_max) { + D(("need a new password 2")); + return PAM_NEW_AUTHTOK_REQD; + } + if (spent->sp_warn > 0) { + long warn = spent->sp_warn > spent->sp_max ? -1 : + spent->sp_max - spent->sp_warn; + if (passed >= warn) { + *daysleft = subtract(spent->sp_max, passed); + D(("warn before expiry")); + } + } + } + if (spent->sp_min > 0 && passed < spent->sp_min) { + /* + * The last password change was too recent. This error will be ignored + * if no password change is attempted. + */ + D(("password change too recent")); + return PAM_AUTHTOK_ERR; + } + return PAM_SUCCESS; +} + +/* passwd/salt conversion macros */ + +#define PW_TMPFILE "/etc/npasswd" +#define SH_TMPFILE "/etc/nshadow" +#define OPW_TMPFILE SCONFIG_DIR "/nopasswd" + +/* + * i64c - convert an integer to a radix 64 character + */ +static int +i64c(int i) +{ + if (i < 0) + return ('.'); + else if (i > 63) + return ('z'); + if (i == 0) + return ('.'); + if (i == 1) + return ('/'); + if (i >= 2 && i <= 11) + return ('0' - 2 + i); + if (i >= 12 && i <= 37) + return ('A' - 12 + i); + if (i >= 38 && i <= 63) + return ('a' - 38 + i); + return ('\0'); +} + +/* must point to a buffer of at least +1 length */ +static void +crypt_make_salt(char *where, int length) +{ + struct timeval tv; + MD5_CTX ctx; + unsigned char tmp[16]; + unsigned char *src = (unsigned char *)where; + int i; +#ifdef PAM_PATH_RANDOMDEV + int fd; + int rv; + + if ((rv = fd = open(PAM_PATH_RANDOMDEV, O_RDONLY | O_CLOEXEC)) != -1) { + while ((rv = read(fd, where, length)) != length && errno == EINTR); + close (fd); + } + if (rv != length) { +#endif + /* + * Code lifted from Marek Michalkiewicz's shadow suite. (CG) + * removed use of static variables (AGM) + * + * will work correctly only for length <= 16 */ + src = tmp; + GoodMD5Init(&ctx); + gettimeofday(&tv, (struct timezone *) 0); + GoodMD5Update(&ctx, (void *) &tv, sizeof tv); + i = getpid(); + GoodMD5Update(&ctx, (void *) &i, sizeof i); + i = clock(); + GoodMD5Update(&ctx, (void *) &i, sizeof i); + GoodMD5Update(&ctx, src, length); + GoodMD5Final(tmp, &ctx); +#ifdef PAM_PATH_RANDOMDEV + } +#endif + for (i = 0; i < length; i++) + *where++ = i64c(src[i] & 077); + *where = '\0'; +} + +char * +crypt_md5_wrapper(const char *pass_new) +{ + unsigned char result[16]; + char *cp = (char *) result; + + cp = stpcpy(cp, "$1$"); /* magic for the MD5 */ + crypt_make_salt(cp, 8); + + /* no longer need cleartext */ + cp = Goodcrypt_md5(pass_new, (const char *) result); + pass_new = NULL; + + return cp; +} + +PAMH_ARG_DECL(char * create_password_hash, + const char *password, unsigned long long ctrl, int rounds) +{ + const char *algoid; +#if defined(CRYPT_GENSALT_OUTPUT_SIZE) && CRYPT_GENSALT_OUTPUT_SIZE > 64 + /* Strings returned by crypt_gensalt_rn will be no longer than this. */ + char salt[CRYPT_GENSALT_OUTPUT_SIZE]; +#else + char salt[64]; /* contains rounds number + max 16 bytes of salt + algo id */ +#endif + char *sp, *ret; +#ifdef HAVE_CRYPT_R + struct crypt_data *cdata = NULL; +#endif + + if (on(UNIX_MD5_PASS, ctrl)) { + /* algoid = "$1" */ + return crypt_md5_wrapper(password); + } else if (on(UNIX_YESCRYPT_PASS, ctrl)) { + algoid = "$y$"; + } else if (on(UNIX_GOST_YESCRYPT_PASS, ctrl)) { + algoid = "$gy$"; + } else if (on(UNIX_BLOWFISH_PASS, ctrl)) { + algoid = "$2b$"; + } else if (on(UNIX_SHA256_PASS, ctrl)) { + algoid = "$5$"; + } else if (on(UNIX_SHA512_PASS, ctrl)) { + algoid = "$6$"; + } else { /* must be crypt/bigcrypt */ + char tmppass[9]; + char *hashed; + + crypt_make_salt(salt, 2); + if (off(UNIX_BIGCRYPT, ctrl) && strlen(password) > 8) { + strncpy(tmppass, password, sizeof(tmppass)-1); + tmppass[sizeof(tmppass)-1] = '\0'; + password = tmppass; + } + hashed = bigcrypt(password, salt); + pam_overwrite_array(tmppass); + password = NULL; + return hashed; + } + +#if defined(CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY) && CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY + /* + * Any version of libcrypt supporting auto entropy is + * guaranteed to have crypt_gensalt_rn(). + */ + sp = crypt_gensalt_rn(algoid, rounds, NULL, 0, salt, sizeof(salt)); +#else + sp = stpcpy(salt, algoid); + if (on(UNIX_ALGO_ROUNDS, ctrl)) { + sp += snprintf(sp, sizeof(salt) - (16 + 1 + (sp - salt)), "rounds=%u$", rounds); + } + crypt_make_salt(sp, 16); +#endif /* CRYPT_GENSALT_IMPLEMENTS_AUTO_ENTROPY */ +#ifdef HAVE_CRYPT_R + sp = NULL; + cdata = calloc(1, sizeof(*cdata)); + if (cdata != NULL) { + sp = crypt_r(password, salt, cdata); + } +#else + sp = crypt(password, salt); +#endif + if (!sp || strncmp(algoid, sp, strlen(algoid)) != 0) { + /* libxcrypt/libc doesn't know the algorithm, error out */ + pam_syslog(pamh, LOG_ERR, + "Algo %s not supported by the crypto backend.\n", + on(UNIX_YESCRYPT_PASS, ctrl) ? "yescrypt" : + on(UNIX_GOST_YESCRYPT_PASS, ctrl) ? "gost_yescrypt" : + on(UNIX_BLOWFISH_PASS, ctrl) ? "blowfish" : + on(UNIX_SHA256_PASS, ctrl) ? "sha256" : + on(UNIX_SHA512_PASS, ctrl) ? "sha512" : algoid); + if(sp) { + pam_overwrite_string(sp); + } +#ifdef HAVE_CRYPT_R + pam_overwrite_object(cdata); + free(cdata); +#endif + return NULL; + } + ret = strdup(sp); + pam_overwrite_string(sp); +#ifdef HAVE_CRYPT_R + pam_overwrite_object(cdata); + free(cdata); +#endif + return ret; +} + +#ifdef WITH_SELINUX +int +unix_selinux_confined(void) +{ + static int confined = -1; + int fd; + char tempfile[]="/etc/.pwdXXXXXX"; + + if (confined != -1) + return confined; + + /* cannot be confined without SELinux enabled */ + if (!SELINUX_ENABLED){ + confined = 0; + return confined; + } + + /* let's try opening shadow read only */ + if ((fd=open("/etc/shadow", O_RDONLY | O_CLOEXEC)) != -1) { + close(fd); + confined = 0; + return confined; + } + + if (errno == EACCES) { + confined = 1; + return confined; + } + + /* shadow opening failed because of other reasons let's try + creating a file in /etc */ + if ((fd=mkstemp(tempfile)) != -1) { + unlink(tempfile); + close(fd); + confined = 0; + return confined; + } + + confined = 1; + return confined; +} + +#else +int +unix_selinux_confined(void) +{ + return 0; +} +#endif + +#ifdef USE_LCKPWDF +int +lock_pwdf(void) +{ + int i; + int retval; + +#ifndef HELPER_COMPILE + if (unix_selinux_confined()) { + return PAM_SUCCESS; + } +#endif + /* These values for the number of attempts and the sleep time + are, of course, completely arbitrary. + My reading of the PAM docs is that, once pam_chauthtok() has been + called with PAM_UPDATE_AUTHTOK, we are obliged to take any + reasonable steps to make sure the token is updated; so retrying + for 1/10 sec. isn't overdoing it. */ + i=0; + while((retval = lckpwdf()) != 0 && i < 100) { + usleep(1000); + i++; + } + if(retval != 0) { + return PAM_AUTHTOK_LOCK_BUSY; + } + return PAM_SUCCESS; +} + +void +unlock_pwdf(void) +{ +#ifndef HELPER_COMPILE + if (unix_selinux_confined()) { + return; + } +#endif + ulckpwdf(); +} +#else +int +lock_pwdf(void) +{ + return PAM_SUCCESS; +} + +void +unlock_pwdf(void) +{ + return; +} +#endif + +#ifdef HELPER_COMPILE +int +save_old_password(const char *forwho, const char *oldpass, + int howmany) +#else +int +save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass, + int howmany) +#endif +{ + char *buf = NULL; + char *s_luser, *s_uid, *s_npas, *s_pas, *pass; + int npas; + FILE *pwfile, *opwfile; + int err = 0; + mode_t oldmask; + int found = 0; + struct passwd *pwd = NULL; + struct stat st; + size_t bufsize = 0; + size_t len = strlen(forwho); +#ifdef WITH_SELINUX + char *prev_context_raw = NULL; +#endif + + if (howmany < 0) { + return PAM_SUCCESS; + } + + if (oldpass == NULL) { + return PAM_SUCCESS; + } + + oldmask = umask(077); + +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + char *passwd_context_raw = NULL; + if (getfilecon_raw("/etc/passwd",&passwd_context_raw)<0) { + return PAM_AUTHTOK_ERR; + }; + if (getfscreatecon_raw(&prev_context_raw)<0) { + freecon(passwd_context_raw); + return PAM_AUTHTOK_ERR; + } + if (setfscreatecon_raw(passwd_context_raw)) { + freecon(passwd_context_raw); + freecon(prev_context_raw); + return PAM_AUTHTOK_ERR; + } + freecon(passwd_context_raw); + } +#endif + pwfile = fopen(OPW_TMPFILE, "we"); + umask(oldmask); + if (pwfile == NULL) { + err = 1; + goto done; + } + + opwfile = fopen(OLD_PASSWORDS_FILE, "re"); + if (opwfile == NULL) { + fclose(pwfile); + err = 1; + goto done; + } + + if (fstat(fileno(opwfile), &st) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + if (fchmod(fileno(pwfile), st.st_mode) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + for (; getline(&buf, &bufsize, opwfile) != -1; pam_overwrite_n(buf, bufsize)) { + if (!strncmp(buf, forwho, len) && strchr(":\n", buf[len]) != NULL) { + char *ep, *sptr = NULL; + long value; + found = 1; + if (howmany == 0) + continue; + buf[strlen(buf) - 1] = '\0'; + s_luser = strtok_r(buf, ":", &sptr); + if (s_luser == NULL) { + found = 0; + continue; + } + s_uid = strtok_r(NULL, ":", &sptr); + if (s_uid == NULL) { + found = 0; + continue; + } + s_npas = strtok_r(NULL, ":", &sptr); + if (s_npas == NULL) { + found = 0; + continue; + } + s_pas = strtok_r(NULL, "", &sptr); + value = strtol(s_npas, &ep, 10); + if (value < 0 || value >= INT_MAX || s_npas == ep || *ep != '\0') + npas = 0; + else + npas = (int)value + 1; + while (npas > howmany && s_pas != NULL) { + s_pas = strpbrk(s_pas, ","); + if (s_pas != NULL) + s_pas++; + npas--; + } + pass = crypt_md5_wrapper(oldpass); + if (s_pas == NULL) + err = fprintf(pwfile, "%s:%s:%d:%s\n", + s_luser, s_uid, npas, pass) < 0; + else + err = fprintf(pwfile, "%s:%s:%d:%s,%s\n", + s_luser, s_uid, npas, s_pas, pass) < 0; + _pam_delete(pass); + if (err) + break; + } else if (fputs(buf, pwfile) < 0) { + err = 1; + break; + } + } + pam_overwrite_n(buf, bufsize); + free(buf); + fclose(opwfile); + + if (!found) { + pwd = pam_modutil_getpwnam(pamh, forwho); + if (pwd == NULL) { + err = 1; + } else { + pass = crypt_md5_wrapper(oldpass); + err = fprintf(pwfile, "%s:%lu:1:%s\n", + forwho, (unsigned long)pwd->pw_uid, pass) < 0; + _pam_delete(pass); + } + } + + if (fflush(pwfile) || fsync(fileno(pwfile))) { + D(("fflush or fsync error writing entries to old passwords file: %m")); + err = 1; + } + + if (fclose(pwfile)) { + D(("fclose error writing entries to old passwords file: %m")); + err = 1; + } + +done: + if (!err) { + if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE)) + err = 1; + } +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon_raw(prev_context_raw)) { + err = 1; + } + if (prev_context_raw) + freecon(prev_context_raw); + prev_context_raw = NULL; + } +#endif + if (!err) { + return PAM_SUCCESS; + } else { + unlink(OPW_TMPFILE); + return PAM_AUTHTOK_ERR; + } +} + +PAMH_ARG_DECL(int unix_update_passwd, + const char *forwho, const char *towhat) +{ + struct passwd *tmpent = NULL; + struct stat st; + FILE *pwfile, *opwfile; + int err = 1; + mode_t oldmask; +#ifdef WITH_SELINUX + char *prev_context_raw = NULL; +#endif + + oldmask = umask(077); +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + char *passwd_context_raw = NULL; + if (getfilecon_raw("/etc/passwd",&passwd_context_raw)<0) { + return PAM_AUTHTOK_ERR; + }; + if (getfscreatecon_raw(&prev_context_raw)<0) { + freecon(passwd_context_raw); + return PAM_AUTHTOK_ERR; + } + if (setfscreatecon_raw(passwd_context_raw)) { + freecon(passwd_context_raw); + freecon(prev_context_raw); + return PAM_AUTHTOK_ERR; + } + freecon(passwd_context_raw); + } +#endif + pwfile = fopen(PW_TMPFILE, "we"); + umask(oldmask); + if (pwfile == NULL) { + err = 1; + goto done; + } + + opwfile = fopen("/etc/passwd", "re"); + if (opwfile == NULL) { + fclose(pwfile); + err = 1; + goto done; + } + + if (fstat(fileno(opwfile), &st) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + if (fchmod(fileno(pwfile), st.st_mode) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + tmpent = fgetpwent(opwfile); + while (tmpent) { + if (!strcmp(tmpent->pw_name, forwho)) { + /* To shut gcc up */ + union { + const char *const_charp; + char *charp; + } assigned_passwd; + assigned_passwd.const_charp = towhat; + + tmpent->pw_passwd = assigned_passwd.charp; + err = 0; + } + if (putpwent(tmpent, pwfile)) { + D(("error writing entry to password file: %m")); + err = 1; + break; + } + tmpent = fgetpwent(opwfile); + } + fclose(opwfile); + + if (fflush(pwfile) || fsync(fileno(pwfile))) { + D(("fflush or fsync error writing entries to password file: %m")); + err = 1; + } + + if (fclose(pwfile)) { + D(("fclose error writing entries to password file: %m")); + err = 1; + } + +done: + if (!err) { + if (!rename(PW_TMPFILE, "/etc/passwd")) + pam_syslog(pamh, + LOG_NOTICE, "password changed for %s", forwho); + else + err = 1; + } +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon_raw(prev_context_raw)) { + err = 1; + } + if (prev_context_raw) + freecon(prev_context_raw); + prev_context_raw = NULL; + } +#endif + if (!err) { + return PAM_SUCCESS; + } else { + unlink(PW_TMPFILE); + return PAM_AUTHTOK_ERR; + } +} + +PAMH_ARG_DECL(int unix_update_shadow, + const char *forwho, char *towhat) +{ + struct spwd spwdent, *stmpent = NULL; + struct stat st; + FILE *pwfile, *opwfile; + int err = 0; + mode_t oldmask; + int wroteentry = 0; +#ifdef WITH_SELINUX + char *prev_context_raw = NULL; +#endif + + oldmask = umask(077); + +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + char *shadow_context_raw = NULL; + if (getfilecon_raw("/etc/shadow",&shadow_context_raw)<0) { + return PAM_AUTHTOK_ERR; + }; + if (getfscreatecon_raw(&prev_context_raw)<0) { + freecon(shadow_context_raw); + return PAM_AUTHTOK_ERR; + } + if (setfscreatecon_raw(shadow_context_raw)) { + freecon(shadow_context_raw); + freecon(prev_context_raw); + return PAM_AUTHTOK_ERR; + } + freecon(shadow_context_raw); + } +#endif + pwfile = fopen(SH_TMPFILE, "we"); + umask(oldmask); + if (pwfile == NULL) { + err = 1; + goto done; + } + + opwfile = fopen("/etc/shadow", "re"); + if (opwfile == NULL) { + fclose(pwfile); + err = 1; + goto done; + } + + if (fstat(fileno(opwfile), &st) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + if (fchmod(fileno(pwfile), st.st_mode) == -1) { + fclose(opwfile); + fclose(pwfile); + err = 1; + goto done; + } + + stmpent = fgetspent(opwfile); + while (stmpent) { + + if (!strcmp(stmpent->sp_namp, forwho)) { + stmpent->sp_pwdp = towhat; + stmpent->sp_lstchg = time(NULL) / (60 * 60 * 24); + if (stmpent->sp_lstchg == 0) + stmpent->sp_lstchg = -1; /* Don't request passwort change + only because time isn't set yet. */ + wroteentry = 1; + D(("Set password %s for %s", stmpent->sp_pwdp, forwho)); + } + + if (putspent(stmpent, pwfile)) { + D(("error writing entry to shadow file: %m")); + err = 1; + break; + } + + stmpent = fgetspent(opwfile); + } + + fclose(opwfile); + + if (!wroteentry && !err) { + DIAG_PUSH_IGNORE_CAST_QUAL; + spwdent.sp_namp = (char *)forwho; + DIAG_POP_IGNORE_CAST_QUAL; + spwdent.sp_pwdp = towhat; + spwdent.sp_lstchg = time(NULL) / (60 * 60 * 24); + if (spwdent.sp_lstchg == 0) + spwdent.sp_lstchg = -1; /* Don't request passwort change + only because time isn't set yet. */ + spwdent.sp_min = spwdent.sp_max = spwdent.sp_warn = spwdent.sp_inact = + spwdent.sp_expire = -1; + spwdent.sp_flag = (unsigned long)-1l; + if (putspent(&spwdent, pwfile)) { + D(("error writing entry to shadow file: %m")); + err = 1; + } + } + + if (fflush(pwfile) || fsync(fileno(pwfile))) { + D(("fflush or fsync error writing entries to shadow file: %m")); + err = 1; + } + + if (fclose(pwfile)) { + D(("fclose error writing entries to shadow file: %m")); + err = 1; + } + + done: + if (!err) { + if (!rename(SH_TMPFILE, "/etc/shadow")) + pam_syslog(pamh, + LOG_NOTICE, "password changed for %s", forwho); + else + err = 1; + } + +#ifdef WITH_SELINUX + if (SELINUX_ENABLED) { + if (setfscreatecon_raw(prev_context_raw)) { + err = 1; + } + if (prev_context_raw) + freecon(prev_context_raw); + prev_context_raw = NULL; + } +#endif + + if (!err) { + return PAM_SUCCESS; + } else { + unlink(SH_TMPFILE); + return PAM_AUTHTOK_ERR; + } +} + +#ifdef HELPER_COMPILE + +int +helper_verify_password(const char *name, const char *p, int nullok) +{ + struct passwd *pwd = NULL; + char *hash = NULL; + int retval; + + retval = get_pwd_hash(name, &pwd, &hash); + + if (pwd == NULL || hash == NULL) { + helper_log_err(LOG_NOTICE, "check pass; user unknown"); + retval = PAM_USER_UNKNOWN; + } else { + retval = verify_pwd_hash(p, hash, nullok); + } + + if (hash) { + pam_overwrite_string(hash); + _pam_drop(hash); + } + + p = NULL; /* no longer needed here */ + + return retval; +} + +void +helper_log_err(int err, const char *format, ...) +{ + va_list args; + + va_start(args, format); + openlog(HELPER_COMPILE, LOG_CONS | LOG_PID, LOG_AUTHPRIV); + vsyslog(err, format, args); + va_end(args); + closelog(); +} + +static void +su_sighandler(int sig) +{ +#ifndef SA_RESETHAND + /* emulate the behaviour of the SA_RESETHAND flag */ + if ( sig == SIGILL || sig == SIGTRAP || sig == SIGBUS || sig = SIGSERV ) { + struct sigaction sa; + memset(&sa, '\0', sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigaction(sig, &sa, NULL); + } +#endif + if (sig > 0) { + _exit(128 + (sig & 127)); + } +} + +void +setup_signals(void) +{ + struct sigaction action; /* posix signal structure */ + + /* + * Setup signal handlers + */ + (void) memset((void *) &action, 0, sizeof(action)); + action.sa_handler = su_sighandler; +#ifdef SA_RESETHAND + action.sa_flags = SA_RESETHAND; +#endif + (void) sigaction(SIGILL, &action, NULL); + (void) sigaction(SIGTRAP, &action, NULL); + (void) sigaction(SIGBUS, &action, NULL); + (void) sigaction(SIGSEGV, &action, NULL); + action.sa_handler = SIG_IGN; + action.sa_flags = 0; + (void) sigaction(SIGTERM, &action, NULL); + (void) sigaction(SIGHUP, &action, NULL); + (void) sigaction(SIGINT, &action, NULL); + (void) sigaction(SIGQUIT, &action, NULL); +} + +char * +getuidname(uid_t uid) +{ + struct passwd *pw; + + pw = getpwuid(uid); + if (pw == NULL) + return NULL; + + return pw->pw_name; +} + +#endif +/* ****************************************************************** * + * Copyright (c) Jan Rękorajski 1999. + * Copyright (c) Andrew G. Morgan 1996-8. + * Copyright (c) Alex O. Yuriev, 1996. + * Copyright (c) Cristian Gafton 1996. + * Copyright (c) Red Hat, Inc. 1996, 2007, 2008. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_unix/passverify.h b/modules/pam_unix/passverify.h new file mode 100644 index 0000000..1636791 --- /dev/null +++ b/modules/pam_unix/passverify.h @@ -0,0 +1,117 @@ +/* + * Copyright information at end of file. + */ + +#include +#include +#include +#include + +#define PAM_UNIX_RUN_HELPER PAM_CRED_INSUFFICIENT + +#define OLD_PASSWORDS_FILE SCONFIG_DIR "/opasswd" + +int +is_pwd_shadowed(const struct passwd *pwd); + +char * +crypt_md5_wrapper(const char *pass_new); + +int +unix_selinux_confined(void); + +int +lock_pwdf(void); + +void +unlock_pwdf(void); + +#ifdef HELPER_COMPILE +int +save_old_password(const char *forwho, const char *oldpass, + int howmany); +#else +int +save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass, + int howmany); +#endif + +#ifdef HELPER_COMPILE +void +PAM_FORMAT((printf, 2, 3)) +helper_log_err(int err, const char *format,...); + +int +helper_verify_password(const char *name, const char *p, int nullok); + +void +setup_signals(void); + +char * +getuidname(uid_t uid); + +#endif + +#ifdef HELPER_COMPILE +#define PAMH_ARG_DECL(fname, ...) fname(__VA_ARGS__) +#define PAMH_ARG(...) __VA_ARGS__ +#else +#define PAMH_ARG_DECL(fname, ...) fname(pam_handle_t *pamh, __VA_ARGS__) +#define PAMH_ARG(...) pamh, __VA_ARGS__ +#endif + +PAMH_ARG_DECL(int verify_pwd_hash, + const char *p, char *hash, unsigned int nullok); + +PAMH_ARG_DECL(char * create_password_hash, + const char *password, unsigned long long ctrl, int rounds); + +PAMH_ARG_DECL(int get_account_info, + const char *name, struct passwd **pwd, struct spwd **spwdent); + +PAMH_ARG_DECL(int get_pwd_hash, + const char *name, struct passwd **pwd, char **hash); + +PAMH_ARG_DECL(int check_shadow_expiry, + struct spwd *spent, int *daysleft); + +PAMH_ARG_DECL(int unix_update_passwd, + const char *forwho, const char *towhat); + +PAMH_ARG_DECL(int unix_update_shadow, + const char *forwho, char *towhat); + +/* ****************************************************************** * + * Copyright (c) Red Hat, Inc. 2007. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_unix/support.c b/modules/pam_unix/support.c new file mode 100644 index 0000000..dfdc9fc --- /dev/null +++ b/modules/pam_unix/support.c @@ -0,0 +1,938 @@ +/* + * Copyright information at end of file. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_NIS +#include +#endif + +#include +#include +#include +#include + +#include "pam_cc_compat.h" +#include "pam_inline.h" +#include "support.h" +#include "passverify.h" + +/* this is a front-end for module-application conversations */ + +int _make_remark(pam_handle_t * pamh, unsigned long long ctrl, + int type, const char *text) +{ + int retval = PAM_SUCCESS; + + if (off(UNIX__QUIET, ctrl)) { + retval = pam_prompt(pamh, type, NULL, "%s", text); + } + return retval; +} + +static int _unix_strtoi(const char *str, int minval, int *result) +{ + char *ep; + long value = strtol(str, &ep, 10); + if (value < minval || value > INT_MAX || str == ep || *ep != '\0') { + *result = minval; + return -1; + } + *result = (int)value; + return 0; +} + +/* + * set the control flags for the UNIX module. + */ + +unsigned long long _set_ctrl(pam_handle_t *pamh, int flags, int *remember, + int *rounds, int *pass_min_len, int argc, + const char **argv) +{ + unsigned long long ctrl; + char *val; + int j; + + D(("called.")); + + ctrl = UNIX_DEFAULTS; /* the default selection of options */ + + /* set some flags manually */ + + if (getuid() == 0 && !(flags & PAM_CHANGE_EXPIRED_AUTHTOK)) { + D(("IAMROOT")); + set(UNIX__IAMROOT, ctrl); + } + if (flags & PAM_UPDATE_AUTHTOK) { + D(("UPDATE_AUTHTOK")); + set(UNIX__UPDATE, ctrl); + } + if (flags & PAM_PRELIM_CHECK) { + D(("PRELIM_CHECK")); + set(UNIX__PRELIM, ctrl); + } + if (flags & PAM_SILENT) { + D(("SILENT")); + set(UNIX__QUIET, ctrl); + } + + /* preset encryption method with value from /etc/login.defs */ + val = pam_modutil_search_key(pamh, LOGIN_DEFS, "ENCRYPT_METHOD"); + if (val) { + for (j = 0; j < UNIX_CTRLS_; ++j) { + if (unix_args[j].token && unix_args[j].is_hash_algo + && !strncasecmp(val, unix_args[j].token, strlen(unix_args[j].token))) { + break; + } + } + if (j >= UNIX_CTRLS_) { + pam_syslog(pamh, LOG_WARNING, "unrecognized ENCRYPT_METHOD value [%s]", val); + } else { + ctrl &= unix_args[j].mask; /* for turning things off */ + ctrl |= unix_args[j].flag; /* for turning things on */ + } + free (val); + } + + /* now parse the arguments to this module */ + + for (; argc-- > 0; ++argv) { + const char *str = NULL; + + D(("pam_unix arg: %s", *argv)); + + for (j = 0; j < UNIX_CTRLS_; ++j) { + if (unix_args[j].token + && (str = pam_str_skip_prefix_len(*argv, + unix_args[j].token, + strlen(unix_args[j].token))) != NULL) { + break; + } + } + + if (str == NULL) { + pam_syslog(pamh, LOG_ERR, + "unrecognized option [%s]", *argv); + } else { + /* special cases */ + if (j == UNIX_REMEMBER_PASSWD) { + if (remember == NULL) { + pam_syslog(pamh, LOG_ERR, + "option remember not allowed for this module type"); + continue; + } + if (_unix_strtoi(str, -1, remember)) { + pam_syslog(pamh, LOG_ERR, + "option remember invalid [%s]", str); + continue; + } + if (*remember > 400) + *remember = 400; + } else if (j == UNIX_MIN_PASS_LEN) { + if (pass_min_len == NULL) { + pam_syslog(pamh, LOG_ERR, + "option minlen not allowed for this module type"); + continue; + } + if (_unix_strtoi(str, 0, pass_min_len)) { + pam_syslog(pamh, LOG_ERR, + "option minlen invalid [%s]", str); + continue; + } + } else if (j == UNIX_ALGO_ROUNDS) { + if (rounds == NULL) { + pam_syslog(pamh, LOG_ERR, + "option rounds not allowed for this module type"); + continue; + } + if (_unix_strtoi(str, 0, rounds)) { + pam_syslog(pamh, LOG_ERR, + "option rounds invalid [%s]", str); + continue; + } + } + + ctrl &= unix_args[j].mask; /* for turning things off */ + ctrl |= unix_args[j].flag; /* for turning things on */ + } + } + + if (UNIX_DES_CRYPT(ctrl) + && pass_min_len && *pass_min_len > 8) + { + pam_syslog (pamh, LOG_NOTICE, "Password minlen reset to 8 characters"); + *pass_min_len = 8; + } + + if (flags & PAM_DISALLOW_NULL_AUTHTOK) { + D(("DISALLOW_NULL_AUTHTOK")); + set(UNIX__NONULL, ctrl); + } + + /* Read number of rounds for sha256, sha512 and yescrypt */ + if (off(UNIX_ALGO_ROUNDS, ctrl) && rounds != NULL) { + const char *key = NULL; + if (on(UNIX_YESCRYPT_PASS, ctrl)) + key = "YESCRYPT_COST_FACTOR"; + else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl)) + key = "SHA_CRYPT_MAX_ROUNDS"; + else + key = NULL; + + if (key != NULL) { + val = pam_modutil_search_key(pamh, LOGIN_DEFS, key); + if (val) { + if (_unix_strtoi(val, 0, rounds)) + pam_syslog(pamh, LOG_ERR, + "option %s invalid [%s]", key, val); + else + set(UNIX_ALGO_ROUNDS, ctrl); + free (val); + } + } + } + + /* Set default rounds for blowfish, gost-yescrypt and yescrypt */ + if (off(UNIX_ALGO_ROUNDS, ctrl) && rounds != NULL) { + if (on(UNIX_BLOWFISH_PASS, ctrl) || + on(UNIX_GOST_YESCRYPT_PASS, ctrl) || + on(UNIX_YESCRYPT_PASS, ctrl)) { + *rounds = 5; + set(UNIX_ALGO_ROUNDS, ctrl); + } + } + + /* Enforce sane "rounds" values */ + if (on(UNIX_ALGO_ROUNDS, ctrl)) { + if (on(UNIX_GOST_YESCRYPT_PASS, ctrl) || + on(UNIX_YESCRYPT_PASS, ctrl)) { + if (*rounds < 3) + *rounds = 3; + else if (*rounds > 11) + *rounds = 11; + } else if (on(UNIX_BLOWFISH_PASS, ctrl)) { + if (*rounds < 4) + *rounds = 4; + else if (*rounds > 31) + *rounds = 31; + } else if (on(UNIX_SHA256_PASS, ctrl) || on(UNIX_SHA512_PASS, ctrl)) { + if ((*rounds < 1000) || (*rounds == INT_MAX)) { + /* don't care about bogus values */ + *rounds = 0; + unset(UNIX_ALGO_ROUNDS, ctrl); + } else if (*rounds >= 10000000) { + *rounds = 9999999; + } + } + } + + /* auditing is a more sensitive version of debug */ + + if (on(UNIX_AUDIT, ctrl)) { + set(UNIX_DEBUG, ctrl); + } + /* return the set of flags */ + + D(("done.")); + return ctrl; +} + +/* ************************************************************** * + * Useful non-trivial functions * + * ************************************************************** */ + + /* + * the following is used to keep track of the number of times a user fails + * to authenticate themself. + */ + +#define FAIL_PREFIX "-UN*X-FAIL-" +#define UNIX_MAX_RETRIES 3 + +struct _pam_failed_auth { + char *user; /* user that's failed to be authenticated */ + char *name; /* attempt from user with name */ + int uid; /* uid of calling user */ + int euid; /* euid of calling process */ + int count; /* number of failures so far */ +}; + +#ifndef PAM_DATA_REPLACE +#error "Need to get an updated libpam 0.52 or better" +#endif + +static void _cleanup_failures(pam_handle_t * pamh, void *fl, int err) +{ + int quiet; + const void *service = NULL; + const void *ruser = NULL; + const void *rhost = NULL; + const void *tty = NULL; + struct _pam_failed_auth *failure; + + D(("called")); + + quiet = err & PAM_DATA_SILENT; /* should we log something? */ + err &= PAM_DATA_REPLACE; /* are we just replacing data? */ + failure = (struct _pam_failed_auth *) fl; + + if (failure != NULL) { + + if (!quiet && !err) { /* under advisement from Sun,may go away */ + + /* log the number of authentication failures */ + if (failure->count > 1) { + (void) pam_get_item(pamh, PAM_SERVICE, + &service); + (void) pam_get_item(pamh, PAM_RUSER, + &ruser); + (void) pam_get_item(pamh, PAM_RHOST, + &rhost); + (void) pam_get_item(pamh, PAM_TTY, + &tty); + pam_syslog(pamh, LOG_NOTICE, + "%d more authentication failure%s; " + "logname=%s uid=%d euid=%d " + "tty=%s ruser=%s rhost=%s " + "%s%s", + failure->count - 1, failure->count == 2 ? "" : "s", + failure->name, failure->uid, failure->euid, + tty ? (const char *)tty : "", ruser ? (const char *)ruser : "", + rhost ? (const char *)rhost : "", + (failure->user && failure->user[0] != '\0') + ? " user=" : "", + failure->user ? failure->user : "" + ); + + if (failure->count > UNIX_MAX_RETRIES) { + pam_syslog(pamh, LOG_NOTICE, + "service(%s) ignoring max retries; %d > %d", + service == NULL ? "**unknown**" : (const char *)service, + failure->count, + UNIX_MAX_RETRIES); + } + } + } + _pam_delete(failure->user); /* tidy up */ + _pam_delete(failure->name); /* tidy up */ + free(failure); + } +} + +/* + * _unix_getpwnam() searches only /etc/passwd and NIS to find user information + */ +static void _unix_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status UNUSED) +{ + free(data); +} + +int _unix_getpwnam(pam_handle_t *pamh, const char *name, + int files, int nis, struct passwd **ret) +{ + char *buf = NULL; + int matched = 0; + + if (!matched && files && strchr(name, ':') == NULL) { + FILE *passwd; + + passwd = fopen("/etc/passwd", "re"); + if (passwd != NULL) { + size_t n = 0, userlen; + ssize_t r; + + userlen = strlen(name); + + while ((r = getline(&buf, &n, passwd)) != -1) { + if ((size_t)r > userlen && (buf[userlen] == ':') && + (strncmp(name, buf, userlen) == 0)) { + char *p; + + p = buf + strlen(buf) - 1; + while (isspace((unsigned char)*p) && (p >= buf)) { + *p-- = '\0'; + } + matched = 1; + break; + } + } + if (!matched) { + _pam_drop(buf); + } + fclose(passwd); + } + } + +#if defined(HAVE_NIS) && defined(HAVE_YP_GET_DEFAULT_DOMAIN) && defined (HAVE_YP_BIND) && defined (HAVE_YP_MATCH) && defined (HAVE_YP_UNBIND) + if (!matched && nis) { + char *userinfo = NULL, *domain = NULL; + int len = 0, i; + len = yp_get_default_domain(&domain); + if (len == YPERR_SUCCESS) { + len = yp_bind(domain); + } + if (len == YPERR_SUCCESS) { + i = yp_match(domain, "passwd.byname", name, + strlen(name), &userinfo, &len); + yp_unbind(domain); + if (i == YPERR_SUCCESS && (buf = strdup(userinfo)) != NULL) { + matched = 1; + } + } + } +#else + /* we don't have NIS support, make compiler happy. */ + (void) nis; +#endif + + if (matched && (ret != NULL)) { + char *slogin, *spasswd, *suid, *sgid, *sgecos, *shome, *sshell, *p; + size_t retlen; + + *ret = NULL; + + slogin = buf; + + spasswd = strchr(slogin, ':'); + if (spasswd == NULL) { + goto fail; + } + *spasswd++ = '\0'; + + suid = strchr(spasswd, ':'); + if (suid == NULL) { + goto fail; + } + *suid++ = '\0'; + + sgid = strchr(suid, ':'); + if (sgid == NULL) { + goto fail; + } + *sgid++ = '\0'; + + sgecos = strchr(sgid, ':'); + if (sgecos == NULL) { + goto fail; + } + *sgecos++ = '\0'; + + shome = strchr(sgecos, ':'); + if (shome == NULL) { + goto fail; + } + *shome++ = '\0'; + + sshell = strchr(shome, ':'); + if (sshell == NULL) { + goto fail; + } + *sshell++ = '\0'; + + retlen = sizeof(struct passwd) + + strlen(slogin) + 1 + + strlen(spasswd) + 1 + + strlen(sgecos) + 1 + + strlen(shome) + 1 + + strlen(sshell) + 1; + *ret = calloc(retlen, sizeof(char)); + if (*ret == NULL) { + goto fail; + } + + (*ret)->pw_uid = strtol(suid, &p, 10); + if ((strlen(suid) == 0) || (*p != '\0')) { + goto fail; + } + + (*ret)->pw_gid = strtol(sgid, &p, 10); + if ((strlen(sgid) == 0) || (*p != '\0')) { + goto fail; + } + + p = ((char*)(*ret)) + sizeof(struct passwd); + (*ret)->pw_name = strcpy(p, slogin); + p += strlen(p) + 1; + (*ret)->pw_passwd = strcpy(p, spasswd); + p += strlen(p) + 1; + (*ret)->pw_gecos = strcpy(p, sgecos); + p += strlen(p) + 1; + (*ret)->pw_dir = strcpy(p, shome); + p += strlen(p) + 1; + (*ret)->pw_shell = strcpy(p, sshell); + + _pam_drop(buf); + if (asprintf(&buf, "_pam_unix_getpwnam_%s", name) < 0) { + buf = NULL; + goto fail; + } + + if (pam_set_data(pamh, buf, + *ret, _unix_cleanup) != PAM_SUCCESS) { + goto fail; + } + } + + _pam_drop(buf); + return matched; +fail: + _pam_drop(buf); + _pam_drop(*ret); + return matched; +} + +/* + * _unix_comsefromsource() is a quick check to see if information about a given + * user comes from a particular source (just files and nis for now) + * + */ +int _unix_comesfromsource(pam_handle_t *pamh, + const char *name, int files, int nis) +{ + return _unix_getpwnam(pamh, name, files, nis, NULL); +} + +/* + * verify the password of a user + */ + +#include +#include + +static int _unix_run_helper_binary(pam_handle_t *pamh, const char *passwd, + unsigned long long ctrl, const char *user) +{ + int retval, child, fds[2]; + struct sigaction newsa, oldsa; + + D(("called.")); + /* create a pipe for the password */ + if (pipe(fds) != 0) { + D(("could not make pipe")); + return PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + /* + * This code arranges that the demise of the child does not cause + * the application to receive a signal it is not expecting - which + * may kill the application or worse. + * + * The "noreap" module argument is provided so that the admin can + * override this behavior. + */ + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &newsa, &oldsa); + } + + /* fork */ + child = fork(); + if (child == 0) { + static char *envp[] = { NULL }; + const char *args[] = { NULL, NULL, NULL, NULL }; + + /* XXX - should really tidy up PAM here too */ + + /* reopen stdin as pipe */ + if (dup2(fds[0], STDIN_FILENO) != STDIN_FILENO) { + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin"); + _exit(PAM_AUTHINFO_UNAVAIL); + } + + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_PIPE_FD, + PAM_MODUTIL_PIPE_FD) < 0) { + _exit(PAM_AUTHINFO_UNAVAIL); + } + + /* must set the real uid to 0 so the helper will not error + out if pam is called from setuid binary (su, sudo...) */ + if (setuid(0) == -1) { + D(("setuid failed")); + if (geteuid() == 0) { + _exit(PAM_AUTHINFO_UNAVAIL); + } + } + + /* exec binary helper */ + args[0] = CHKPWD_HELPER; + args[1] = user; + if (off(UNIX__NONULL, ctrl)) { /* this means we've succeeded */ + args[2]="nullok"; + } else { + args[2]="nonull"; + } + + DIAG_PUSH_IGNORE_CAST_QUAL; + execve(CHKPWD_HELPER, (char *const *) args, envp); + DIAG_POP_IGNORE_CAST_QUAL; + + /* should not get here: exit with error */ + D(("helper binary is not available")); + _exit(PAM_AUTHINFO_UNAVAIL); + } else if (child > 0) { + /* wait for child */ + /* if the stored password is NULL */ + int rc=0; + if (passwd != NULL) { /* send the password to the child */ + size_t len = strlen(passwd); + + if (len > PAM_MAX_RESP_SIZE) + len = PAM_MAX_RESP_SIZE; + if (write(fds[1], passwd, len) == -1 || + write(fds[1], "", 1) == -1) { + pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m"); + retval = PAM_AUTH_ERR; + } + passwd = NULL; + } else { /* blank password */ + if (write(fds[1], "", 1) == -1) { + pam_syslog (pamh, LOG_ERR, "Cannot send password to helper: %m"); + retval = PAM_AUTH_ERR; + } + } + close(fds[0]); /* close here to avoid possible SIGPIPE above */ + close(fds[1]); + /* wait for helper to complete: */ + while ((rc=waitpid(child, &retval, 0)) < 0 && errno == EINTR); + if (rc<0) { + pam_syslog(pamh, LOG_ERR, "unix_chkpwd waitpid returned %d: %m", rc); + retval = PAM_AUTH_ERR; + } else if (!WIFEXITED(retval)) { + pam_syslog(pamh, LOG_ERR, "unix_chkpwd abnormal exit: %d", retval); + retval = PAM_AUTH_ERR; + } else { + retval = WEXITSTATUS(retval); + } + } else { + D(("fork failed")); + close(fds[0]); + close(fds[1]); + retval = PAM_AUTH_ERR; + } + + if (off(UNIX_NOREAP, ctrl)) { + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + } + + D(("returning %d", retval)); + return retval; +} + +/* + * _unix_blankpasswd() is a quick check for a blank password + * + * returns TRUE if user does not have a password + * - to avoid prompting for one in such cases (CG) + */ + +int +_unix_blankpasswd (pam_handle_t *pamh, unsigned long long ctrl, const char *name) +{ + struct passwd *pwd = NULL; + char *salt = NULL; + int daysleft; + int retval; + int blank = 0; + int execloop; + int nonexistent_check = 1; + + D(("called")); + + /* + * This function does not have to be too smart if something goes + * wrong, return FALSE and let this case be treated somewhere + * else (CG) + */ + + if (on(UNIX_NULLRESETOK, ctrl)) { + retval = _unix_verify_user(pamh, ctrl, name, &daysleft); + if (retval == PAM_NEW_AUTHTOK_REQD) { + /* password reset is enforced, allow authentication with empty password */ + pam_syslog(pamh, LOG_DEBUG, "user [%s] has expired blank password, enabling nullok", name); + set(UNIX__NULLOK, ctrl); + } + } + + if (on(UNIX__NONULL, ctrl)) + return 0; /* will fail but don't let on yet */ + + /* UNIX passwords area */ + + /* + * Execute this loop twice: one checking the password hash of an existing + * user and another one for a non-existing user. This way the runtimes + * are equal, making it more difficult to differentiate existing from + * non-existing users. + */ + for (execloop = 0; execloop < 2; ++execloop) { + retval = get_pwd_hash(pamh, name, &pwd, &salt); + + if (retval == PAM_UNIX_RUN_HELPER) { + if (_unix_run_helper_binary(pamh, NULL, ctrl, name) == PAM_SUCCESS) + blank = nonexistent_check; + } else if (retval == PAM_USER_UNKNOWN) { + name = "root"; + nonexistent_check = 0; + continue; + } else if (salt != NULL) { + if (strlen(salt) == 0) + blank = nonexistent_check; + } + name = "pam_unix_non_existent:"; + /* non-existent user check will not affect the blank value */ + } + + /* tidy up */ + if (salt) + _pam_delete(salt); + + return blank; +} + +int _unix_verify_password(pam_handle_t * pamh, const char *name + ,const char *p, unsigned long long ctrl) +{ + struct passwd *pwd = NULL; + char *salt = NULL; + char *data_name; + char pw[PAM_MAX_RESP_SIZE + 1]; + int retval; + + + D(("called")); + + if (off(UNIX_NODELAY, ctrl)) { + D(("setting delay")); + (void) pam_fail_delay(pamh, 2000000); /* 2 sec delay for on failure */ + } + + /* locate the entry for this user */ + + D(("locating user's record")); + + retval = get_pwd_hash(pamh, name, &pwd, &salt); + + if (asprintf(&data_name, "%s%s", FAIL_PREFIX, name) < 0) { + pam_syslog(pamh, LOG_CRIT, "no memory for data-name"); + data_name = NULL; + } + + if (p != NULL && strlen(p) > PAM_MAX_RESP_SIZE) { + memset(pw, 0, sizeof(pw)); + p = strncpy(pw, p, sizeof(pw) - 1); + } + + if (retval != PAM_SUCCESS) { + if (retval == PAM_UNIX_RUN_HELPER) { + D(("running helper binary")); + retval = _unix_run_helper_binary(pamh, p, ctrl, name); + } else { + D(("user's record unavailable")); + p = NULL; + if (on(UNIX_AUDIT, ctrl)) { + /* this might be a typo and the user has given a password + instead of a username. Careful with this. */ + pam_syslog(pamh, LOG_NOTICE, + "check pass; user (%s) unknown", name); + } else { + name = NULL; + if (on(UNIX_DEBUG, ctrl) || pwd == NULL) { + pam_syslog(pamh, LOG_NOTICE, + "check pass; user unknown"); + } else { + /* don't log failure as another pam module can succeed */ + goto cleanup; + } + } + } + } else { + retval = verify_pwd_hash(pamh, p, salt, off(UNIX__NONULL, ctrl)); + } + + if (retval == PAM_SUCCESS) { + if (data_name) /* reset failures */ + pam_set_data(pamh, data_name, NULL, _cleanup_failures); + } else { + if (data_name != NULL) { + struct _pam_failed_auth *new = NULL; + const struct _pam_failed_auth *old = NULL; + + /* get a failure recorder */ + + new = (struct _pam_failed_auth *) + malloc(sizeof(struct _pam_failed_auth)); + + if (new != NULL) { + + const char *login_name; + const void *void_old; + + + login_name = pam_modutil_getlogin(pamh); + if (login_name == NULL) { + login_name = ""; + } + + new->user = strdup(name ? name : ""); + new->uid = getuid(); + new->euid = geteuid(); + new->name = strdup(login_name); + + /* any previous failures for this user ? */ + if (pam_get_data(pamh, data_name, &void_old) + == PAM_SUCCESS) + old = void_old; + else + old = NULL; + + if (old != NULL) { + new->count = old->count + 1; + if (new->count >= UNIX_MAX_RETRIES) { + retval = PAM_MAXTRIES; + } + } else { + const void *service=NULL; + const void *ruser=NULL; + const void *rhost=NULL; + const void *tty=NULL; + + (void) pam_get_item(pamh, PAM_SERVICE, + &service); + (void) pam_get_item(pamh, PAM_RUSER, + &ruser); + (void) pam_get_item(pamh, PAM_RHOST, + &rhost); + (void) pam_get_item(pamh, PAM_TTY, + &tty); + + pam_syslog(pamh, LOG_NOTICE, + "authentication failure; " + "logname=%s uid=%d euid=%d " + "tty=%s ruser=%s rhost=%s " + "%s%s", + new->name, new->uid, new->euid, + tty ? (const char *)tty : "", + ruser ? (const char *)ruser : "", + rhost ? (const char *)rhost : "", + (new->user && new->user[0] != '\0') + ? " user=" : "", + new->user ? new->user : "" + ); + new->count = 1; + } + + pam_set_data(pamh, data_name, new, _cleanup_failures); + + } else { + pam_syslog(pamh, LOG_CRIT, + "no memory for failure recorder"); + } + } + } + +cleanup: + pam_overwrite_array(pw); /* clear memory of the password */ + if (data_name) + _pam_delete(data_name); + if (salt) + _pam_delete(salt); + + D(("done [%d].", retval)); + + return retval; +} + +int +_unix_verify_user(pam_handle_t *pamh, + unsigned long long ctrl, + const char *name, + int *daysleft) +{ + int retval; + struct spwd *spent; + struct passwd *pwent; + + retval = get_account_info(pamh, name, &pwent, &spent); + if (retval == PAM_USER_UNKNOWN) { + pam_syslog(pamh, LOG_ERR, + "could not identify user (from getpwnam(%s))", + name); + return retval; + } + + if (retval == PAM_SUCCESS && spent == NULL) + return PAM_SUCCESS; + + if (retval == PAM_UNIX_RUN_HELPER) { + retval = _unix_run_verify_binary(pamh, ctrl, name, daysleft); + if (retval == PAM_AUTHINFO_UNAVAIL && + on(UNIX_BROKEN_SHADOW, ctrl)) + return PAM_SUCCESS; + } else if (retval != PAM_SUCCESS) { + if (on(UNIX_BROKEN_SHADOW,ctrl)) + return PAM_SUCCESS; + else + return retval; + } else + retval = check_shadow_expiry(pamh, spent, daysleft); + + return retval; +} + +/* ****************************************************************** * + * Copyright (c) Jan Rękorajski 1999. + * Copyright (c) Andrew G. Morgan 1996-8. + * Copyright (c) Alex O. Yuriev, 1996. + * Copyright (c) Cristian Gafton 1996. + * Copyright (c) Red Hat, Inc. 2007. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_unix/support.h b/modules/pam_unix/support.h new file mode 100644 index 0000000..e8f629d --- /dev/null +++ b/modules/pam_unix/support.h @@ -0,0 +1,182 @@ +/* + * $Id$ + */ + +#ifndef _PAM_UNIX_SUPPORT_H +#define _PAM_UNIX_SUPPORT_H + +#include + +/* + * File to read value of ENCRYPT_METHOD from. + */ +#define LOGIN_DEFS "/etc/login.defs" + + +/* + * here is the string to inform the user that the new passwords they + * typed were not the same. + */ + +/* type definition for the control options */ + +typedef struct { + const char *token; + unsigned long long mask; /* shall assume 64 bits of flags */ + unsigned long long flag; + unsigned int is_hash_algo; +} UNIX_Ctrls; + +/* + * macro to determine if a given flag is on + */ + +#define on(x,ctrl) (unix_args[x].flag & (ctrl)) + +/* + * macro to determine that a given flag is NOT on + */ + +#define off(x,ctrl) (!on(x,ctrl)) + +/* + * macro to turn on/off a ctrl flag manually + */ + +#define set(x,ctrl) ((ctrl) = ((ctrl)&unix_args[x].mask)|unix_args[x].flag) +#define unset(x,ctrl) ((ctrl) &= ~(unix_args[x].flag)) + +/* the generic mask */ + +#define _ALL_ON_ (~0ULL) + +/* end of macro definitions definitions for the control flags */ + +/* ****************************************************************** * + * ctrl flags proper.. + */ + +/* + * here are the various options recognized by the unix module. They + * are enumerated here and then defined below. Internal arguments are + * given NULL tokens. + */ + +#define UNIX__OLD_PASSWD 0 /* internal */ +#define UNIX__VERIFY_PASSWD 1 /* internal */ +#define UNIX__IAMROOT 2 /* internal */ + +#define UNIX_AUDIT 3 /* print more things than debug.. + some information may be sensitive */ +#define UNIX_USE_FIRST_PASS 4 +#define UNIX_TRY_FIRST_PASS 5 +#define UNIX_AUTHTOK_TYPE 6 /* TYPE for pam_get_authtok() */ + +#define UNIX__PRELIM 7 /* internal */ +#define UNIX__UPDATE 8 /* internal */ +#define UNIX__NONULL 9 /* internal */ +#define UNIX__QUIET 10 /* internal */ +#define UNIX_USE_AUTHTOK 11 /* insist on reading PAM_AUTHTOK */ +#define UNIX_SHADOW 12 /* signal shadow on */ +#define UNIX_MD5_PASS 13 /* force the use of MD5 passwords */ +#define UNIX__NULLOK 14 /* Null token ok */ +#define UNIX_DEBUG 15 /* send more info to syslog(3) */ +#define UNIX_NODELAY 16 /* admin does not want a fail-delay */ +#define UNIX_NIS 17 /* wish to use NIS for pwd */ +#define UNIX_BIGCRYPT 18 /* use DEC-C2 crypt()^x function */ +#define UNIX_LIKE_AUTH 19 /* need to auth for setcred to work */ +#define UNIX_REMEMBER_PASSWD 20 /* Remember N previous passwords */ +#define UNIX_NOREAP 21 /* don't reap child process */ +#define UNIX_BROKEN_SHADOW 22 /* ignore errors reading password aging + * information during acct management */ +#define UNIX_SHA256_PASS 23 /* new password hashes will use SHA256 */ +#define UNIX_SHA512_PASS 24 /* new password hashes will use SHA512 */ +#define UNIX_ALGO_ROUNDS 25 /* optional number of rounds for new + password hash algorithms */ +#define UNIX_BLOWFISH_PASS 26 /* new password hashes will use blowfish */ +#define UNIX_MIN_PASS_LEN 27 /* min length for password */ +#define UNIX_QUIET 28 /* Don't print informational messages */ +#define UNIX_NO_PASS_EXPIRY 29 /* Don't check for password expiration if not used for authentication */ +#define UNIX_DES 30 /* DES, default */ +#define UNIX_GOST_YESCRYPT_PASS 31 /* new password hashes will use gost-yescrypt */ +#define UNIX_YESCRYPT_PASS 32 /* new password hashes will use yescrypt */ +#define UNIX_NULLRESETOK 33 /* allow empty password if password reset is enforced */ +/* -------------- */ +#define UNIX_CTRLS_ 34 /* number of ctrl arguments defined */ + +#define UNIX_DES_CRYPT(ctrl) (off(UNIX_MD5_PASS,ctrl)&&off(UNIX_BIGCRYPT,ctrl)&&off(UNIX_SHA256_PASS,ctrl)&&off(UNIX_SHA512_PASS,ctrl)&&off(UNIX_BLOWFISH_PASS,ctrl)&&off(UNIX_GOST_YESCRYPT_PASS,ctrl)&&off(UNIX_YESCRYPT_PASS,ctrl)) + +static const UNIX_Ctrls unix_args[UNIX_CTRLS_] = +{ +/* symbol token name ctrl mask ctrl * + * --------------------------- -------------------- ------------------------- ---------------- */ + +/* UNIX__OLD_PASSWD */ {NULL, _ALL_ON_, 01, 0}, +/* UNIX__VERIFY_PASSWD */ {NULL, _ALL_ON_, 02, 0}, +/* UNIX__IAMROOT */ {NULL, _ALL_ON_, 04, 0}, +/* UNIX_AUDIT */ {"audit", _ALL_ON_, 010, 0}, +/* UNIX_USE_FIRST_PASS */ {"use_first_pass", _ALL_ON_^(060ULL), 020, 0}, +/* UNIX_TRY_FIRST_PASS */ {"try_first_pass", _ALL_ON_^(060ULL), 040, 0}, +/* UNIX_AUTHTOK_TYPE */ {"authtok_type=", _ALL_ON_, 0100, 0}, +/* UNIX__PRELIM */ {NULL, _ALL_ON_^(0600ULL), 0200, 0}, +/* UNIX__UPDATE */ {NULL, _ALL_ON_^(0600ULL), 0400, 0}, +/* UNIX__NONULL */ {NULL, _ALL_ON_, 01000, 0}, +/* UNIX__QUIET */ {NULL, _ALL_ON_, 02000, 0}, +/* UNIX_USE_AUTHTOK */ {"use_authtok", _ALL_ON_, 04000, 0}, +/* UNIX_SHADOW */ {"shadow", _ALL_ON_, 010000, 0}, +/* UNIX_MD5_PASS */ {"md5", _ALL_ON_^(015660420000ULL), 020000, 1}, +/* UNIX__NULLOK */ {"nullok", _ALL_ON_^(01000ULL), 0, 0}, +/* UNIX_DEBUG */ {"debug", _ALL_ON_, 040000, 0}, +/* UNIX_NODELAY */ {"nodelay", _ALL_ON_, 0100000, 0}, +/* UNIX_NIS */ {"nis", _ALL_ON_, 0200000, 0}, +/* UNIX_BIGCRYPT */ {"bigcrypt", _ALL_ON_^(015660420000ULL), 0400000, 1}, +/* UNIX_LIKE_AUTH */ {"likeauth", _ALL_ON_, 01000000, 0}, +/* UNIX_REMEMBER_PASSWD */ {"remember=", _ALL_ON_, 02000000, 0}, +/* UNIX_NOREAP */ {"noreap", _ALL_ON_, 04000000, 0}, +/* UNIX_BROKEN_SHADOW */ {"broken_shadow", _ALL_ON_, 010000000, 0}, +/* UNIX_SHA256_PASS */ {"sha256", _ALL_ON_^(015660420000ULL), 020000000, 1}, +/* UNIX_SHA512_PASS */ {"sha512", _ALL_ON_^(015660420000ULL), 040000000, 1}, +/* UNIX_ALGO_ROUNDS */ {"rounds=", _ALL_ON_, 0100000000, 0}, +/* UNIX_BLOWFISH_PASS */ {"blowfish", _ALL_ON_^(015660420000ULL), 0200000000, 1}, +/* UNIX_MIN_PASS_LEN */ {"minlen=", _ALL_ON_, 0400000000, 0}, +/* UNIX_QUIET */ {"quiet", _ALL_ON_, 01000000000, 0}, +/* UNIX_NO_PASS_EXPIRY */ {"no_pass_expiry", _ALL_ON_, 02000000000, 0}, +/* UNIX_DES */ {"des", _ALL_ON_^(015660420000ULL), 0, 1}, +/* UNIX_GOST_YESCRYPT_PASS */ {"gost_yescrypt", _ALL_ON_^(015660420000ULL), 04000000000, 1}, +/* UNIX_YESCRYPT_PASS */ {"yescrypt", _ALL_ON_^(015660420000ULL), 010000000000, 1}, +/* UNIX_NULLRESETOK */ {"nullresetok", _ALL_ON_, 020000000000, 0}, +}; + +#define UNIX_DEFAULTS (unix_args[UNIX__NONULL].flag) + +/* use this to free strings. ESPECIALLY password strings */ + +#define _pam_delete(xx) \ +{ \ + pam_overwrite_string(xx); \ + _pam_drop(xx); \ +} + +extern int _make_remark(pam_handle_t * pamh, unsigned long long ctrl, + int type, const char *text); +extern unsigned long long _set_ctrl(pam_handle_t * pamh, int flags, + int *remember, int *rounds, + int *pass_min_len, + int argc, const char **argv); +extern int _unix_getpwnam (pam_handle_t *pamh, + const char *name, int files, int nis, + struct passwd **ret); +extern int _unix_comesfromsource (pam_handle_t *pamh, + const char *name, int files, int nis); +extern int _unix_blankpasswd(pam_handle_t *pamh, unsigned long long ctrl, + const char *name); +extern int _unix_verify_password(pam_handle_t * pamh, const char *name, + const char *p, unsigned long long ctrl); + +extern int _unix_verify_user(pam_handle_t *pamh, unsigned long long ctrl, + const char *name, int *daysleft); + +extern int _unix_run_verify_binary(pam_handle_t *pamh, + unsigned long long ctrl, + const char *user, int *daysleft); +#endif /* _PAM_UNIX_SUPPORT_H */ diff --git a/modules/pam_unix/tst-pam_unix b/modules/pam_unix/tst-pam_unix new file mode 100755 index 0000000..2292280 --- /dev/null +++ b/modules/pam_unix/tst-pam_unix @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_unix.so diff --git a/modules/pam_unix/unix_chkpwd.8.xml b/modules/pam_unix/unix_chkpwd.8.xml new file mode 100644 index 0000000..ca0fa10 --- /dev/null +++ b/modules/pam_unix/unix_chkpwd.8.xml @@ -0,0 +1,64 @@ + + + + unix_chkpwd + 8 + Linux-PAM + Linux-PAM Manual + + + + unix_chkpwd + Helper binary that verifies the password of the current user + + + + + unix_chkpwd + + ... + + + + + + + DESCRIPTION + + + unix_chkpwd is a helper program for the + pam_unix module that verifies the + password of the current user. It also checks password and account + expiration dates in shadow. It is not intended to + be run directly from the command line and logs a security violation if + done so. + + + + It is typically installed setuid root or setgid shadow. + + + + The interface of the helper - command line options, and input/output + data format are internal to the pam_unix + module and it should not be called directly from applications. + + + + + SEE ALSO + + + pam_unix8 + + + + + + AUTHOR + + Written by Andrew Morgan and other various people. + + + + \ No newline at end of file diff --git a/modules/pam_unix/unix_chkpwd.c b/modules/pam_unix/unix_chkpwd.c new file mode 100644 index 0000000..820136d --- /dev/null +++ b/modules/pam_unix/unix_chkpwd.c @@ -0,0 +1,223 @@ +/* + * This program is designed to run setuid(root) or with sufficient + * privilege to read all of the unix password databases. It is designed + * to provide a mechanism for the current user (defined by this + * process's uid) to verify their own password. + * + * The password is read from the standard input. The exit status of + * this program indicates whether the user is authenticated or not. + * + * Copyright information is located at the end of the file. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBAUDIT +#include +#include "audit.h" +#endif + +#include +#include + +#include "passverify.h" +#include "pam_inline.h" + +static int _check_expiry(const char *uname) +{ + struct spwd *spent; + struct passwd *pwent; + int retval; + int daysleft; + + retval = get_account_info(uname, &pwent, &spent); + if (retval != PAM_SUCCESS) { + helper_log_err(LOG_ERR, "could not obtain user info (%s)", uname); + printf("-1\n"); + return retval; + } + + if (spent == NULL) { + printf("-1\n"); + return retval; + } + + retval = check_shadow_expiry(spent, &daysleft); + printf("%d\n", daysleft); + return retval; +} + +int main(int argc, char *argv[]) +{ + char pass[PAM_MAX_RESP_SIZE + 1]; + char *option; + int npass, nullok; + int blankpass = 0; + int retval = PAM_AUTH_ERR; + char *user; + char *passwords[] = { pass }; + + /* + * Catch or ignore as many signal as possible. + */ + setup_signals(); + + /* + * we establish that this program is running with non-tty stdin. + * this is to discourage casual use. It does *NOT* prevent an + * intruder from repeatedly running this program to determine the + * password of the current user (brute force attack, but one for + * which the attacker must already have gained access to the user's + * account). + */ + + if (isatty(STDIN_FILENO) || argc != 3 ) { + helper_log_err(LOG_NOTICE + ,"inappropriate use of Unix helper binary [UID=%d]" + ,getuid()); +#ifdef HAVE_LIBAUDIT + audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR); +#endif + fprintf(stderr + ,"This binary is not designed for running in this way\n" + "-- the system administrator has been informed\n"); + sleep(10); /* this should discourage/annoy the user */ + return PAM_SYSTEM_ERR; + } + + /* + * Determine what the current user's name is. + * We must thus skip the check if the real uid is 0. + */ + if (getuid() == 0) { + user=argv[1]; + } + else { + user = getuidname(getuid()); + /* if the caller specifies the username, verify that user + matches it */ + if (user == NULL || strcmp(user, argv[1])) { + uid_t ruid = getuid(); + gid_t rgid = getgid(); + + /* no match -> permanently change to the real user and group, + * check for no-return, and proceed */ + if (setgid(rgid) != 0 || setuid(ruid) != 0 || + (rgid != 0 && setgid(0) != -1) || (ruid != 0 && setuid(0) != -1)) + return PAM_AUTH_ERR; + } + user = argv[1]; + } + + option=argv[2]; + + if (strcmp(option, "chkexpiry") == 0) + /* Check account information from the shadow file */ + return _check_expiry(argv[1]); + /* read the nullok/nonull option */ + else if (strcmp(option, "nullok") == 0) + nullok = 1; + else if (strcmp(option, "nonull") == 0) + nullok = 0; + else { +#ifdef HAVE_LIBAUDIT + audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR); +#endif + return PAM_SYSTEM_ERR; + } + /* read the password from stdin (a pipe from the pam_unix module) */ + + npass = pam_read_passwords(STDIN_FILENO, 1, passwords); + + if (npass != 1) { /* is it a valid password? */ + helper_log_err(LOG_DEBUG, "no password supplied"); + *pass = '\0'; + } + + if (*pass == '\0') { + blankpass = 1; + } + + retval = helper_verify_password(user, pass, nullok); + + pam_overwrite_array(pass); /* clear memory of the password */ + + /* return pass or fail */ + + if (retval != PAM_SUCCESS) { + if (!nullok || !blankpass) { + /* no need to log blank pass test */ +#ifdef HAVE_LIBAUDIT + if (getuid() != 0) + audit_log(AUDIT_USER_AUTH, user, PAM_AUTH_ERR); +#endif + helper_log_err(LOG_NOTICE, "password check failed for user (%s)", user); + } + /* if helper_verify_password() returned PAM_USER_UNKNOWN, the + most appropriate error to propagate to + _unix_verify_password() is PAM_AUTHINFO_UNAVAIL; otherwise + return general failure */ + if (retval == PAM_USER_UNKNOWN) + return PAM_AUTHINFO_UNAVAIL; + else + return PAM_AUTH_ERR; + } else { + if (getuid() != 0) { +#ifdef HAVE_LIBAUDIT + return audit_log(AUDIT_USER_AUTH, user, PAM_SUCCESS); +#else + return PAM_SUCCESS; +#endif + } + return PAM_SUCCESS; + } +} + +/* + * Copyright (c) Andrew G. Morgan, 1996. All rights reserved + * Copyright (c) Red Hat, Inc., 2007,2008. All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_unix/unix_update.8.xml b/modules/pam_unix/unix_update.8.xml new file mode 100644 index 0000000..1a96865 --- /dev/null +++ b/modules/pam_unix/unix_update.8.xml @@ -0,0 +1,64 @@ + + + + unix_update + 8 + Linux-PAM + Linux-PAM Manual + + + + unix_update + Helper binary that updates the password of a given user + + + + + unix_update + + ... + + + + + + + DESCRIPTION + + + unix_update is a helper program for the + pam_unix module that updates the + password of a given user. It is not intended to be run directly + from the command line and logs a security violation if done so. + + + + The purpose of the helper is to enable tighter confinement of + login and password changing services. The helper is thus called only + when SELinux is enabled on the system. + + + + The interface of the helper - command line options, and input/output + data format are internal to the pam_unix + module and it should not be called directly from applications. + + + + + SEE ALSO + + + pam_unix8 + + + + + + AUTHOR + + Written by Tomas Mraz and other various people. + + + + \ No newline at end of file diff --git a/modules/pam_unix/unix_update.c b/modules/pam_unix/unix_update.c new file mode 100644 index 0000000..e17d6f8 --- /dev/null +++ b/modules/pam_unix/unix_update.c @@ -0,0 +1,222 @@ +/* + * This program is designed to run with sufficient privilege + * to read and write all of the unix password databases. + * Its purpose is to allow updating the databases when + * SELinux confinement of the caller domain prevents them to + * do that themselves. + * + * The password is read from the standard input. The exit status of + * this program indicates whether the password was updated or not. + * + * Copyright information is located at the end of the file. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LIBAUDIT +#include +#include "audit.h" +#endif + +#include +#include + +#include "passverify.h" +#include "pam_inline.h" + +static int +set_password(const char *forwho, const char *shadow, const char *remember) +{ + struct passwd *pwd = NULL; + uid_t ruid; + int retval; + char pass[PAM_MAX_RESP_SIZE + 1]; + char towhat[PAM_MAX_RESP_SIZE + 1]; + int npass = 0; + /* we don't care about number format errors because the helper + should be called internally only */ + int doshadow = atoi(shadow); + int nremember = atoi(remember); + char *passwords[] = { pass, towhat }; + + /* read the password from stdin (a pipe from the pam_unix module) */ + + npass = pam_read_passwords(STDIN_FILENO, 2, passwords); + + if (npass != 2) { /* is it a valid password? */ + if (npass == 1) { + helper_log_err(LOG_DEBUG, "no new password supplied"); + pam_overwrite_array(pass); + } else { + helper_log_err(LOG_DEBUG, "no valid passwords supplied"); + } + return PAM_AUTHTOK_ERR; + } + + if (lock_pwdf() != PAM_SUCCESS) { + pam_overwrite_array(pass); + pam_overwrite_array(towhat); + return PAM_AUTHTOK_LOCK_BUSY; + } + + pwd = getpwnam(forwho); + + if (pwd == NULL) { + retval = PAM_USER_UNKNOWN; + goto done; + } + + /* If real caller uid is not root we must verify that + * the target user is the caller and the + * received old pass agrees with the current one. + * We always allow change from null pass. */ + ruid = getuid(); + if (ruid != 0) { + if (pwd->pw_uid != ruid) { + helper_log_err(LOG_NOTICE, "user mismatch detected: source=%d target=%d", + ruid, pwd->pw_uid); + retval = PAM_AUTHTOK_ERR; + goto done; + } + + retval = helper_verify_password(forwho, pass, 1); +#ifdef HAVE_LIBAUDIT + audit_log(AUDIT_USER_AUTH, getuidname(getuid()), retval); +#endif + if (retval != PAM_SUCCESS) { + helper_log_err(LOG_NOTICE, "password check failed for user (%s)", + getuidname(getuid())); + goto done; + } + } + + /* first, save old password */ + if (save_old_password(forwho, pass, nremember)) { + retval = PAM_AUTHTOK_ERR; + goto done; + } + + if (doshadow || is_pwd_shadowed(pwd)) { + retval = unix_update_shadow(forwho, towhat); + if (retval == PAM_SUCCESS) + if (!is_pwd_shadowed(pwd)) + retval = unix_update_passwd(forwho, "x"); + } else { + retval = unix_update_passwd(forwho, towhat); + } + +#ifdef HAVE_LIBAUDIT + audit_log(AUDIT_USER_CHAUTHTOK, getuidname(getuid()), retval); +#endif + + +done: + pam_overwrite_array(pass); + pam_overwrite_array(towhat); + + unlock_pwdf(); + + if (retval == PAM_SUCCESS) { + return PAM_SUCCESS; + } else { + return PAM_AUTHTOK_ERR; + } +} + +int main(int argc, char *argv[]) +{ + char *option; + + /* + * Catch or ignore as many signal as possible. + */ + setup_signals(); + + /* + * we establish that this program is running with non-tty stdin. + * this is to discourage casual use. It does *NOT* prevent an + * intruder from repeatedly running this program to determine the + * password of the current user (brute force attack, but one for + * which the attacker must already have gained access to the user's + * account). + */ + + if (isatty(STDIN_FILENO) || argc != 5 ) { + helper_log_err(LOG_NOTICE + ,"inappropriate use of Unix helper binary [UID=%d]" + ,getuid()); +#ifdef HAVE_LIBAUDIT + audit_log(AUDIT_ANOM_EXEC, getuidname(getuid()), PAM_SYSTEM_ERR); +#endif + fprintf(stderr + ,"This binary is not designed for running in this way\n" + "-- the system administrator has been informed\n"); + sleep(10); /* this should discourage/annoy the user */ + return PAM_SYSTEM_ERR; + } + + /* We must be root to read/update shadow. + */ + if (geteuid() != 0) { + return PAM_CRED_INSUFFICIENT; + } + + option = argv[2]; + + if (strcmp(option, "update") == 0) { + /* Attempting to change the password */ + return set_password(argv[1], argv[3], argv[4]); + } + + return PAM_SYSTEM_ERR; +} + +/* + * Copyright (c) Andrew G. Morgan, 1996. All rights reserved + * Copyright (c) Red Hat, Inc., 2007, 2008. All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_unix/yppasswd.h b/modules/pam_unix/yppasswd.h new file mode 100644 index 0000000..3a40c3e --- /dev/null +++ b/modules/pam_unix/yppasswd.h @@ -0,0 +1,68 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _YPPASSWD_H_RPCGEN +#define _YPPASSWD_H_RPCGEN + +#include "config.h" + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +struct xpasswd { + char *pw_name; + char *pw_passwd; + int pw_uid; + int pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; +}; +typedef struct xpasswd xpasswd; + +struct yppasswd { + char *oldpass; + xpasswd newpw; +}; +typedef struct yppasswd yppasswd; + +#define YPPASSWDPROG 100009 +#define YPPASSWDVERS 1 + +#if defined(__STDC__) || defined(__cplusplus) +#define YPPASSWDPROC_UPDATE 1 +extern int * yppasswdproc_update_1(yppasswd *, CLIENT *); +extern int * yppasswdproc_update_1_svc(yppasswd *, struct svc_req *); +extern int yppasswdprog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t); + +#else /* K&R C */ +#define YPPASSWDPROC_UPDATE 1 +extern int * yppasswdproc_update_1(); +extern int * yppasswdproc_update_1_svc(); +extern int yppasswdprog_1_freeresult (); +#endif /* K&R C */ + +/* the xdr functions */ + +#if defined(__STDC__) || defined(__cplusplus) +extern bool_t xdr_passwd (XDR *, xpasswd*); +extern bool_t xdr_yppasswd (XDR *, yppasswd*); + +#else /* K&R C */ +extern bool_t xdr_passwd (); +extern bool_t xdr_yppasswd (); + +#endif /* K&R C */ + +#ifdef __cplusplus +} +#endif + +#endif /* !_YPPASSWD_H_RPCGEN */ diff --git a/modules/pam_unix/yppasswd_xdr.c b/modules/pam_unix/yppasswd_xdr.c new file mode 100644 index 0000000..0523d52 --- /dev/null +++ b/modules/pam_unix/yppasswd_xdr.c @@ -0,0 +1,36 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include "yppasswd.h" + +bool_t +xdr_passwd (XDR *xdrs, xpasswd *objp) +{ + if (!xdr_string (xdrs, &objp->pw_name, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->pw_passwd, ~0)) + return FALSE; + if (!xdr_int (xdrs, &objp->pw_uid)) + return FALSE; + if (!xdr_int (xdrs, &objp->pw_gid)) + return FALSE; + if (!xdr_string (xdrs, &objp->pw_gecos, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->pw_dir, ~0)) + return FALSE; + if (!xdr_string (xdrs, &objp->pw_shell, ~0)) + return FALSE; + return TRUE; +} + +bool_t +xdr_yppasswd (XDR *xdrs, yppasswd *objp) +{ + if (!xdr_string (xdrs, &objp->oldpass, ~0)) + return FALSE; + if (!xdr_passwd (xdrs, &objp->newpw)) + return FALSE; + return TRUE; +} diff --git a/modules/pam_userdb/README.xml b/modules/pam_userdb/README.xml new file mode 100644 index 0000000..4e8f8ee --- /dev/null +++ b/modules/pam_userdb/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_userdb.8.xml" xpointer='xpointer(id("pam_userdb-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_userdb/create.pl b/modules/pam_userdb/create.pl new file mode 100644 index 0000000..06915c9 --- /dev/null +++ b/modules/pam_userdb/create.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl +# this program creates a database in ARGV[1] from pairs given on +# stdandard input +# +# $Id$ + +use DB_File; + +my $database = $ARGV[0]; +die "Use: create.pl \n" unless ($database); +print "Using database: $database\n"; + +my %lusers = (); + +tie %lusers, 'DB_File', $database, O_RDWR|O_CREAT, 0644, $DB_HASH ; +while () { + my ($user, $pass) = split; + + $lusers{$user} = $pass; +} +untie %lusers; diff --git a/modules/pam_userdb/meson.build b/modules/pam_userdb/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_userdb/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_userdb/pam_userdb.8.xml b/modules/pam_userdb/pam_userdb.8.xml new file mode 100644 index 0000000..0f96410 --- /dev/null +++ b/modules/pam_userdb/pam_userdb.8.xml @@ -0,0 +1,291 @@ + + + + pam_userdb + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_userdb + PAM module to authenticate against a db database + + + + + pam_userdb.so + + db=/path/database + + + debug + + + crypt=[crypt|none] + + + icase + + + dump + + + try_first_pass + + + use_first_pass + + + unknown_ok + + + key_only + + + + + + + DESCRIPTION + + + The pam_userdb module is used to verify a username/password pair + against values stored in a Berkeley DB database. The database is + indexed by the username, and the data fields corresponding to the + username keys are the passwords. + + + + + + OPTIONS + + + + crypt=[crypt|none] + + + + Indicates whether encrypted or plaintext passwords are stored + in the database. If it is , passwords + should be stored in the database in + + crypt3 + form. If is selected, + passwords should be stored in the database as plaintext. + + + + + + db=/path/database + + + + Use the /path/database database for + performing lookup. There is no default; the module will + return PAM_IGNORE if no + database is provided. Note that the path to the database file + should be specified without the .db suffix. + + + + + + debug + + + + Print debug information. Note that password hashes, both from db + and computed, will be printed to syslog. + + + + + + dump + + + + Dump all the entries in the database to the log. + Don't do this by default! + + + + + + icase + + + + Make the password verification to be case insensitive + (ie when working with registration numbers and such). + Only works with plaintext password storage. + + + + + + + try_first_pass + + + + Use the authentication token previously obtained by + another module that did the conversation with the + application. If this token can not be obtained then + the module will try to converse. This option can + be used for stacking different modules that need to + deal with the authentication tokens. + + + + + + use_first_pass + + + + Use the authentication token previously obtained by + another module that did the conversation with the + application. If this token can not be obtained then + the module will fail. This option can be used for + stacking different modules that need to deal with + the authentication tokens. + + + + + + unknown_ok + + + + Do not return error when checking for a user that is + not in the database. This can be used to stack more + than one pam_userdb module that will check a + username/password pair in more than a database. + + + + + + key_only + + + + The username and password are concatenated together + in the database hash as 'username-password' with a + random value. if the concatenation of the username and + password with a dash in the middle returns any result, + the user is valid. this is useful in cases where + the username may not be unique but the username and + password pair are. + + + + + + + + MODULE TYPES PROVIDED + + The and module + types are provided. + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + Authentication failure. + + + + PAM_AUTHTOK_RECOVERY_ERR + + + Authentication information cannot be recovered. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_CONV_ERR + + + Conversation failure. + + + + + PAM_SERVICE_ERR + + + Error in service module. + + + + + PAM_SUCCESS + + + Success. + + + + + PAM_USER_UNKNOWN + + + User not known to the underlying authentication module. + + + + + + + + EXAMPLES + +auth sufficient pam_userdb.so icase db=/etc/dbtest + + + + + SEE ALSO + + + crypt3 + , + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_userdb was written by Cristian Gafton >gafton@redhat.com<. + + + + \ No newline at end of file diff --git a/modules/pam_userdb/pam_userdb.c b/modules/pam_userdb/pam_userdb.c new file mode 100644 index 0000000..71012e5 --- /dev/null +++ b/modules/pam_userdb/pam_userdb.c @@ -0,0 +1,588 @@ +/* + * pam_userdb module + * + * Written by Cristian Gafton 1996/09/10 + * See the end of the file for Copyright Information + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_CRYPT_H +#include +#endif + +#include "pam_userdb.h" + +#ifdef HAVE_NDBM_H +# include +#elif defined(HAVE_GDBM_H) +# include +#else +# ifdef HAVE_DB_H +# define DB_DBM_HSEARCH 1 /* use the dbm interface */ +# define HAVE_DBM /* for BerkDB 5.0 and later */ +# include +# else +# error "failed to find a libdb or equivalent" +# endif +#endif + +#include +#include +#include +#include "pam_inline.h" +#include "pam_i18n.h" + +#ifndef HAVE_GDBM_H +# define COND_UNUSED UNUSED +#else +# define COND_UNUSED +#endif /* HAVE_GDBM_H */ + +/* + * Conversation function to obtain the user's password + */ +static int +obtain_authtok(pam_handle_t *pamh) +{ + char *resp; + const void *item; + int retval; + + retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, &resp, _("Password: ")); + + if (retval != PAM_SUCCESS) + return retval; + + if (resp == NULL) + return PAM_CONV_ERR; + + /* set the auth token */ + retval = pam_set_item(pamh, PAM_AUTHTOK, resp); + + /* clean it up */ + pam_overwrite_string(resp); + _pam_drop(resp); + + if ( (retval != PAM_SUCCESS) || + (retval = pam_get_item(pamh, PAM_AUTHTOK, &item)) + != PAM_SUCCESS ) { + return retval; + } + + return retval; +} + +static int +_pam_parse (pam_handle_t *pamh, int argc, const char **argv, + const char **database, const char **cryptmode) +{ + int ctrl; + + *database = NULL; + *cryptmode = NULL; + + /* step through arguments */ + for (ctrl = 0; argc-- > 0; ++argv) + { + const char *str; + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_DEBUG_ARG; + else if (!strcasecmp(*argv, "icase")) + ctrl |= PAM_ICASE_ARG; + else if (!strcasecmp(*argv, "dump")) + ctrl |= PAM_DUMP_ARG; + else if (!strcasecmp(*argv, "unknown_ok")) + ctrl |= PAM_UNKNOWN_OK_ARG; + else if (!strcasecmp(*argv, "key_only")) + ctrl |= PAM_KEY_ONLY_ARG; + else if (!strcasecmp(*argv, "use_first_pass")) + ctrl |= PAM_USE_FPASS_ARG; + else if (!strcasecmp(*argv, "try_first_pass")) + ctrl |= PAM_TRY_FPASS_ARG; + else if ((str = pam_str_skip_icase_prefix(*argv, "db=")) != NULL) + { + *database = str; + if (**database == '\0') { + *database = NULL; + pam_syslog(pamh, LOG_ERR, + "db= specification missing argument - ignored"); + } + } + else if ((str = pam_str_skip_icase_prefix(*argv, "crypt=")) != NULL) + { + *cryptmode = str; + if (**cryptmode == '\0') + pam_syslog(pamh, LOG_ERR, + "crypt= specification missing argument - ignored"); + } + else + { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + return ctrl; +} + +/* + * Database abstraction functions + */ +static void * +db_open(const char *database, mode_t file_mode) +{ +#ifdef HAVE_GDBM_H + return gdbm_open(database, 4096, GDBM_READER, file_mode, NULL); +#else + return dbm_open(database, O_RDONLY, file_mode); +#endif /* HAVE_GDBM_H */ +} + +static datum +db_firstkey(void *dbm) +{ +#ifdef HAVE_GDBM_H + return gdbm_firstkey(dbm); +#else + return dbm_firstkey(dbm); +#endif /* HAVE_GDBM_H */ +} + +static datum +db_nextkey(void *dbm, datum key COND_UNUSED) +{ +#ifdef HAVE_GDBM_H + return gdbm_nextkey(dbm, key); +#else + return dbm_nextkey(dbm); +#endif /* HAVE_GDBM_H */ +} + +static datum +db_fetch(void *dbm, datum key) +{ +#ifdef HAVE_GDBM_H + return gdbm_fetch(dbm, key); +#else + return dbm_fetch(dbm, key); +#endif /* HAVE_GDBM_H */ +} + +static int +db_close(void *dbm) +{ +#ifdef HAVE_GDBM_H +# ifdef GDBM_CLOSE_RETURNS_INT + return gdbm_close(dbm); +# else + gdbm_close(dbm); + return 0; +# endif +#else + dbm_close(dbm); + return 0; +#endif /* HAVE_GDBM_H */ +} + + +/* + * Looks up a user name in a database and checks the password + * + * return values: + * 1 = User not found + * 0 = OK + * -1 = Password incorrect + * -2 = System error + */ +static int +user_lookup (pam_handle_t *pamh, const char *database, const char *cryptmode, + const char *user, const char *pass, int ctrl) +{ +#ifdef HAVE_GDBM_H + GDBM_FILE *dbm; +#else + DBM *dbm; +#endif + datum key, data; + + /* Open the DB file. */ + dbm = db_open(database, 0644); + if (dbm == NULL) { + pam_syslog(pamh, LOG_ERR, + "user_lookup: could not open database `%s': %m", database); + return -2; + } + + /* dump out the database contents for debugging */ + if (ctrl & PAM_DUMP_ARG) { + pam_syslog(pamh, LOG_INFO, "Database dump:"); + for (key = db_firstkey(dbm); key.dptr != NULL; + key = db_nextkey(dbm, key)) { + data = db_fetch(dbm, key); + pam_syslog(pamh, LOG_INFO, + "key[len=%d] = `%s', data[len=%d] = `%s'", + key.dsize, key.dptr, data.dsize, data.dptr); + } + } + + /* do some more init work */ + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + if (ctrl & PAM_KEY_ONLY_ARG) { + if (asprintf(&key.dptr, "%s-%s", user, pass) < 0) + key.dptr = NULL; + else + key.dsize = strlen(key.dptr); + } else { + key.dptr = strdup(user); + key.dsize = strlen(user); + } + + if (key.dptr) { + data = db_fetch(dbm, key); + pam_overwrite_n(key.dptr, key.dsize); + free(key.dptr); + } + + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_INFO, + "password in database is [%p]`%.*s', len is %d", + data.dptr, data.dsize, (char *) data.dptr, data.dsize); + } + + if (data.dptr != NULL) { + int compare = -2; + + if (ctrl & PAM_KEY_ONLY_ARG) + { + db_close (dbm); + return 0; /* found it, data contents don't matter */ + } + + if (cryptmode && pam_str_skip_icase_prefix(cryptmode, "crypt") != NULL) { + if (data.dsize < 13) { + /* hash is too short */ + pam_syslog(pamh, LOG_INFO, "password hash in database is too short"); + } else if (ctrl & PAM_ICASE_ARG) { + pam_syslog(pamh, LOG_INFO, + "case-insensitive comparison only works with plaintext passwords"); + } else { + /* libdb is not guaranteed to produce null terminated strings */ + char *pwhash = strndup(data.dptr, data.dsize); + + if (pwhash == NULL) { + pam_syslog(pamh, LOG_CRIT, "strndup failed: data.dptr"); + } else { + char *cryptpw = NULL; +#ifdef HAVE_CRYPT_R + struct crypt_data *cdata = NULL; + cdata = calloc(1, sizeof(*cdata)); + if (cdata == NULL) { + pam_syslog(pamh, LOG_CRIT, "malloc failed: struct crypt_data"); + } else { + cryptpw = crypt_r(pass, pwhash, cdata); + } +#else + cryptpw = crypt (pass, pwhash); +#endif + if (cryptpw && strlen(cryptpw) == (size_t)data.dsize) { + compare = memcmp(data.dptr, cryptpw, data.dsize); + } else { + if (ctrl & PAM_DEBUG_ARG) { + if (cryptpw) { + pam_syslog(pamh, LOG_INFO, "lengths of computed and stored hashes differ"); + pam_syslog(pamh, LOG_INFO, "computed hash: %s", cryptpw); + } else { + pam_syslog(pamh, LOG_ERR, "crypt() returned NULL"); + } + } + } +#ifdef HAVE_CRYPT_R + pam_overwrite_object(cdata); + free(cdata); +#else + pam_overwrite_string(cryptpw); +#endif + } + pam_overwrite_string(pwhash); + free(pwhash); + } + } else { + + /* Unknown password encryption method - + * default to plaintext password storage + */ + + if (strlen(pass) != (size_t)data.dsize) { + compare = 1; /* wrong password len -> wrong password */ + } else if (ctrl & PAM_ICASE_ARG) { + compare = strncasecmp(data.dptr, pass, data.dsize); + } else { + compare = strncmp(data.dptr, pass, data.dsize); + } + + if (cryptmode && pam_str_skip_icase_prefix(cryptmode, "none") == NULL + && (ctrl & PAM_DEBUG_ARG)) { + pam_syslog(pamh, LOG_INFO, "invalid value for crypt parameter: %s", + cryptmode); + pam_syslog(pamh, LOG_INFO, "defaulting to plaintext password mode"); + } + + } + + db_close(dbm); + if (compare == 0) + return 0; /* match */ + else + return -1; /* wrong */ + } else { + int saw_user = 0; + + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_INFO, "error returned by dbm_fetch: %m"); + } + + /* probably we should check dbm_error() here */ + + if ((ctrl & PAM_KEY_ONLY_ARG) == 0) { + db_close(dbm); + return 1; /* not key_only, so no entry => no entry for the user */ + } + + /* now handle the key_only case */ + for (key = db_firstkey(dbm); + key.dptr != NULL; + key = db_nextkey(dbm, key)) { + int compare; + /* first compare the user portion (case sensitive) */ + compare = strncmp(key.dptr, user, strlen(user)); + if (compare == 0) { + /* assume failure */ + compare = -1; + /* if we have the divider where we expect it to be... */ + if (key.dptr[strlen(user)] == '-') { + saw_user = 1; + if ((size_t)key.dsize == strlen(user) + 1 + strlen(pass)) { + if (ctrl & PAM_ICASE_ARG) { + /* compare the password portion (case insensitive)*/ + compare = strncasecmp(key.dptr + strlen(user) + 1, + pass, + strlen(pass)); + } else { + /* compare the password portion (case sensitive) */ + compare = strncmp(key.dptr + strlen(user) + 1, + pass, + strlen(pass)); + } + } + } + if (compare == 0) { + db_close(dbm); + return 0; /* match */ + } + } + } + db_close(dbm); + if (saw_user) + return -1; /* saw the user, but password mismatch */ + else + return 1; /* not found */ + } + + /* NOT REACHED */ + return -2; +} + +/* --- authentication management functions (only) --- */ + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + const char *username; + const void *password; + const char *database = NULL; + const char *cryptmode = NULL; + int retval = PAM_AUTH_ERR, ctrl; + + /* parse arguments */ + ctrl = _pam_parse(pamh, argc, argv, &database, &cryptmode); + if (database == NULL) { + pam_syslog(pamh, LOG_ERR, "can not get the database name"); + return PAM_SERVICE_ERR; + } + + /* Get the username */ + retval = pam_get_user(pamh, &username, NULL); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", + pam_strerror(pamh, retval)); + return PAM_SERVICE_ERR; + } + + if ((ctrl & PAM_USE_FPASS_ARG) == 0 && (ctrl & PAM_TRY_FPASS_ARG) == 0) { + /* Converse to obtain a password */ + retval = obtain_authtok(pamh); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "can not obtain password from user"); + return retval; + } + } + + /* Check if we got a password */ + retval = pam_get_item(pamh, PAM_AUTHTOK, &password); + if (retval != PAM_SUCCESS || password == NULL) { + if ((ctrl & PAM_TRY_FPASS_ARG) != 0) { + /* Converse to obtain a password */ + retval = obtain_authtok(pamh); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, "can not obtain password from user"); + return retval; + } + retval = pam_get_item(pamh, PAM_AUTHTOK, &password); + } + if (retval != PAM_SUCCESS || password == NULL) { + pam_syslog(pamh, LOG_ERR, "can not recover user password"); + return PAM_AUTHTOK_RECOVERY_ERR; + } + } + + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_INFO, "Verify user `%s' with a password", + username); + + /* Now use the username to look up password in the database file */ + retval = user_lookup(pamh, database, cryptmode, username, password, ctrl); + switch (retval) { + case -2: + /* some sort of system error. The log was already printed */ + return PAM_SERVICE_ERR; + case -1: + /* incorrect password */ + pam_syslog(pamh, LOG_NOTICE, + "user `%s' denied access (incorrect password)", + username); + return PAM_AUTH_ERR; + case 1: + /* the user does not exist in the database */ + if (ctrl & PAM_DEBUG_ARG) + pam_syslog(pamh, LOG_NOTICE, + "user `%s' not found in the database", username); + return PAM_USER_UNKNOWN; + case 0: + /* Otherwise, the authentication looked good */ + pam_syslog(pamh, LOG_NOTICE, "user '%s' granted access", username); + return PAM_SUCCESS; + default: + /* we don't know anything about this return value */ + pam_syslog(pamh, LOG_ERR, + "internal module error (retval = %d, user = `%s'", + retval, username); + return PAM_SERVICE_ERR; + } + + /* should not be reached */ + return PAM_IGNORE; +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + const char *username; + const char *database = NULL; + const char *cryptmode = NULL; + int retval = PAM_AUTH_ERR, ctrl; + + /* parse arguments */ + ctrl = _pam_parse(pamh, argc, argv, &database, &cryptmode); + + /* Get the username */ + retval = pam_get_user(pamh, &username, NULL); + if (retval != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", + pam_strerror(pamh, retval)); + return PAM_SERVICE_ERR; + } + + /* Now use the username to look up password in the database file */ + retval = user_lookup(pamh, database, cryptmode, username, "", ctrl); + switch (retval) { + case -2: + /* some sort of system error. The log was already printed */ + return PAM_SERVICE_ERR; + case -1: + /* incorrect password, but we don't care */ + /* FALL THROUGH */ + case 0: + /* authentication succeeded. dumbest password ever. */ + return PAM_SUCCESS; + case 1: + /* the user does not exist in the database */ + return PAM_USER_UNKNOWN; + default: + /* we don't know anything about this return value */ + pam_syslog(pamh, LOG_ERR, + "internal module error (retval = %d, user = `%s'", + retval, username); + return PAM_SERVICE_ERR; + } + + return PAM_SUCCESS; +} + +/* + * Copyright (c) Cristian Gafton , 1999 + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_userdb/pam_userdb.h b/modules/pam_userdb/pam_userdb.h new file mode 100644 index 0000000..86e9b47 --- /dev/null +++ b/modules/pam_userdb/pam_userdb.h @@ -0,0 +1,59 @@ + +#ifndef _PAM_USERSDB_H +#define _PAM_USERSDB_H +/* $Id$ */ + +/* Header files */ +#include + +/* argument parsing */ +#define PAM_DEBUG_ARG 0x0001 +#define PAM_ICASE_ARG 0x0002 +#define PAM_DUMP_ARG 0x0004 +#define PAM_UNKNOWN_OK_ARG 0x0010 +#define PAM_KEY_ONLY_ARG 0x0020 +#define PAM_USE_FPASS_ARG 0x0040 +#define PAM_TRY_FPASS_ARG 0x0080 + +/* The name of the module we are compiling */ +#ifndef MODULE_NAME +#define MODULE_NAME "pam_userdb" +#endif /* MODULE_NAME */ + +#endif /* _PAM_USERSDB_H */ + +/* + * Copyright (c) Cristian Gafton , 1999 + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_userdb/tst-pam_userdb b/modules/pam_userdb/tst-pam_userdb new file mode 100755 index 0000000..5d5eb19 --- /dev/null +++ b/modules/pam_userdb/tst-pam_userdb @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_userdb.so diff --git a/modules/pam_usertype/README.xml b/modules/pam_usertype/README.xml new file mode 100644 index 0000000..7faf549 --- /dev/null +++ b/modules/pam_usertype/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_usertype.8.xml" xpointer='xpointer(id("pam_usertype-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_usertype/meson.build b/modules/pam_usertype/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_usertype/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_usertype/pam_usertype.8.xml b/modules/pam_usertype/pam_usertype.8.xml new file mode 100644 index 0000000..87ad079 --- /dev/null +++ b/modules/pam_usertype/pam_usertype.8.xml @@ -0,0 +1,195 @@ + + + pam_usertype + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_usertype + check if the authenticated user is a system or regular account + + + + + + pam_usertype.so + flag + condition + + + + + + DESCRIPTION + + pam_usertype.so is designed to succeed or fail authentication + based on type of the account of the authenticated user. + The type of the account is decided with help of + SYS_UID_MAX + settings in /etc/login.defs. One use is to select + whether to load other modules based on this test. + + + + The module should be given only one condition as module argument. + Authentication will succeed only if the condition is met. + + + + + OPTIONS + + The following flags are supported: + + + + + use_uid + + + Evaluate conditions using the account of the user whose UID + the application is running under instead of the user being + authenticated. + + + + + audit + + + Log unknown users to the system log. + + + + + + + Available conditions are: + + + + + issystem + + Succeed if the user is a system user. + + + + isregular + + Succeed if the user is a regular user. + + + + + + + MODULE TYPES PROVIDED + + All module types (, , + and ) are provided. + + + + + RETURN VALUES + + + + PAM_SUCCESS + + + The condition was true. + + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_CONV_ERR + + + The conversation method supplied by the application + failed to obtain the username. + + + + + + PAM_INCOMPLETE + + + The conversation method supplied by the application + returned PAM_CONV_AGAIN. + + + + + + PAM_AUTH_ERR + + + The condition was false. + + + + + + PAM_SERVICE_ERR + + + A service error occurred or the arguments can't be + parsed correctly. + + + + + + PAM_USER_UNKNOWN + + + User was not found. + + + + + + + + + EXAMPLES + + Skip remaining modules if the user is a system user: + + +account sufficient pam_usertype.so issystem + + + + + SEE ALSO + + + login.defs5 + , + + pam8 + + + + + + AUTHOR + Pavel Březina <pbrezina@redhat.com> + + \ No newline at end of file diff --git a/modules/pam_usertype/pam_usertype.c b/modules/pam_usertype/pam_usertype.c new file mode 100644 index 0000000..2738038 --- /dev/null +++ b/modules/pam_usertype/pam_usertype.c @@ -0,0 +1,318 @@ +/****************************************************************************** + * Check user type based on login.defs. + * + * Copyright (c) 2020 Red Hat, Inc. + * Written by Pavel Březina + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LOGIN_DEFS "/etc/login.defs" + +enum pam_usertype_op { + OP_IS_SYSTEM, + OP_IS_REGULAR, + + OP_SENTINEL +}; + +struct pam_usertype_opts { + enum pam_usertype_op op; + int use_uid; + int audit; +}; + +static int +pam_usertype_parse_args(struct pam_usertype_opts *opts, + pam_handle_t *pamh, + int argc, + const char **argv) +{ + int i; + + memset(opts, 0, sizeof(struct pam_usertype_opts)); + opts->op = OP_SENTINEL; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "use_uid") == 0) { + opts->use_uid = 1; + } else if (strcmp(argv[i], "audit") == 0) { + opts->audit = 1; + } else if (strcmp(argv[i], "issystem") == 0) { + opts->op = OP_IS_SYSTEM; + } else if (strcmp(argv[i], "isregular") == 0) { + opts->op = OP_IS_REGULAR; + } else { + pam_syslog(pamh, LOG_WARNING, "Unknown argument: %s", argv[i]); + /* Just continue. */ + } + } + + if (opts->op == OP_SENTINEL) { + pam_syslog(pamh, LOG_ERR, "Operation not specified"); + return PAM_SERVICE_ERR; + } + + return PAM_SUCCESS; +} + +static int +pam_usertype_get_uid(struct pam_usertype_opts *opts, + pam_handle_t *pamh, + uid_t *_uid) +{ + struct passwd *pwd; + const char *username; + int ret; + + /* Get uid of user that runs the application. */ + if (opts->use_uid) { + pwd = pam_modutil_getpwuid(pamh, getuid()); + if (pwd == NULL) { + pam_syslog(pamh, LOG_ERR, + "error retrieving information about user %lu", + (unsigned long)getuid()); + return PAM_USER_UNKNOWN; + } + + *_uid = pwd->pw_uid; + return PAM_SUCCESS; + } + + /* Get uid of user that is being authenticated. */ + ret = pam_get_user(pamh, &username, NULL); + if (ret != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s", + pam_strerror(pamh, ret)); + return ret == PAM_CONV_AGAIN ? PAM_INCOMPLETE : ret; + } + + pwd = pam_modutil_getpwnam(pamh, username); + if (pwd == NULL) { + if (opts->audit) { + pam_syslog(pamh, LOG_NOTICE, + "error retrieving information about user %s", username); + } + + pam_modutil_getpwnam(pamh, "root"); + + return PAM_USER_UNKNOWN; + } + pam_modutil_getpwnam(pamh, "pam_usertype_non_existent:"); + + *_uid = pwd->pw_uid; + + return PAM_SUCCESS; +} + +#define MAX_UID_VALUE 0xFFFFFFFFUL + +static uid_t +pam_usertype_get_id(pam_handle_t *pamh, + const char *key, + uid_t default_value) +{ + unsigned long ul; + char *value; + char *ep; + uid_t uid; + + value = pam_modutil_search_key(pamh, LOGIN_DEFS, key); + if (value == NULL) { + return default_value; + } + + /* taken from get_lastlog_uid_max() */ + ep = value + strlen(value); + while (ep > value && isspace((unsigned char)*(--ep))) { + *ep = '\0'; + } + + errno = 0; + ul = strtoul(value, &ep, 10); + if (!(ul >= MAX_UID_VALUE + || (uid_t)ul >= MAX_UID_VALUE + || (errno != 0 && ul == 0) + || value == ep + || *ep != '\0')) { + uid = (uid_t)ul; + } else { + uid = default_value; + } + + free(value); + + return uid; +} + +static int +pam_usertype_is_system(pam_handle_t *pamh, uid_t uid) +{ + uid_t uid_min; + uid_t sys_max; + + if (uid == (uid_t)-1) { + pam_syslog(pamh, LOG_WARNING, "invalid uid"); + return PAM_USER_UNKNOWN; + } + + if (uid == PAM_USERTYPE_OVERFLOW_UID) { + /* nobody */ + return PAM_SUCCESS; + } + + uid_min = pam_usertype_get_id(pamh, "UID_MIN", PAM_USERTYPE_UIDMIN); + sys_max = pam_usertype_get_id(pamh, "SYS_UID_MAX", uid_min - 1); + + if (uid <= sys_max && uid < uid_min) { + return PAM_SUCCESS; + } + + return PAM_AUTH_ERR; +} + +static int +pam_usertype_is_regular(pam_handle_t *pamh, uid_t uid) +{ + int ret; + + ret = pam_usertype_is_system(pamh, uid); + switch (ret) { + case PAM_SUCCESS: + return PAM_AUTH_ERR; + case PAM_USER_UNKNOWN: + return PAM_USER_UNKNOWN; + default: + return PAM_SUCCESS; + } +} + +static int +pam_usertype_evaluate(struct pam_usertype_opts *opts, + pam_handle_t *pamh, + uid_t uid) +{ + switch (opts->op) { + case OP_IS_SYSTEM: + return pam_usertype_is_system(pamh, uid); + case OP_IS_REGULAR: + return pam_usertype_is_regular(pamh, uid); + default: + pam_syslog(pamh, LOG_ERR, "Unknown operation: %d", opts->op); + return PAM_SERVICE_ERR; + } +} + +/** + * Arguments: + * - issystem: uid less than SYS_UID_MAX + * - isregular: not issystem + * - use_uid: use user that runs application not that is being authenticate (same as in pam_succeed_if) + * - audit: log unknown users to syslog + */ +static int +pam_usertype(pam_handle_t *pamh, int argc, const char **argv) +{ + struct pam_usertype_opts opts; + uid_t uid = -1; + int ret; + + ret = pam_usertype_parse_args(&opts, pamh, argc, argv); + if (ret != PAM_SUCCESS) { + return ret; + } + + ret = pam_usertype_get_uid(&opts, pamh, &uid); + if (ret != PAM_SUCCESS) { + return ret; + } + + return pam_usertype_evaluate(&opts, pamh, uid); +} + +int +pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_IGNORE; +} + +int +pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_usertype(pamh, argc, argv); +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_usertype(pamh, argc, argv); +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_usertype(pamh, argc, argv); +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_usertype(pamh, argc, argv); +} + +int +pam_sm_chauthtok(pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + return pam_usertype(pamh, argc, argv); +} diff --git a/modules/pam_usertype/tst-pam_usertype b/modules/pam_usertype/tst-pam_usertype new file mode 100755 index 0000000..a21f8fe --- /dev/null +++ b/modules/pam_usertype/tst-pam_usertype @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_usertype.so diff --git a/modules/pam_warn/README.xml b/modules/pam_warn/README.xml new file mode 100644 index 0000000..56093f8 --- /dev/null +++ b/modules/pam_warn/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_warn.8.xml" xpointer='xpointer(id("pam_warn-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_warn/meson.build b/modules/pam_warn/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_warn/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_warn/pam_warn.8.xml b/modules/pam_warn/pam_warn.8.xml new file mode 100644 index 0000000..a20c5f7 --- /dev/null +++ b/modules/pam_warn/pam_warn.8.xml @@ -0,0 +1,102 @@ + + + + pam_warn + 8 + Linux-PAM + Linux-PAM Manual + + + pam_warn + PAM module which logs all PAM items if called + + + + pam_warn.so + + + + + DESCRIPTION + + pam_warn is a PAM module that logs the service, terminal, user, + remote user and remote host to + + syslog3 + . The items are not probed for, but instead obtained + from the standard PAM items. The module always returns + PAM_IGNORE, indicating that it + does not want to affect the authentication process. + + + + + OPTIONS + This module does not recognise any options. + + + + MODULE TYPES PROVIDED + + The , , + and module + types are provided. + + + + + RETURN VALUES + + + PAM_IGNORE + + + This module always returns PAM_IGNORE. + + + + + + + + EXAMPLES + +#%PAM-1.0 +# +# If we don't have config entries for a service, the +# OTHER entries are used. To be secure, warn and deny +# access to everything. +other auth required pam_warn.so +other auth required pam_deny.so +other account required pam_warn.so +other account required pam_deny.so +other password required pam_warn.so +other password required pam_deny.so +other session required pam_warn.so +other session required pam_deny.so + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_warn was written by Andrew G. Morgan <morgan@kernel.org>. + + + + \ No newline at end of file diff --git a/modules/pam_warn/pam_warn.c b/modules/pam_warn/pam_warn.c new file mode 100644 index 0000000..e2258fe --- /dev/null +++ b/modules/pam_warn/pam_warn.c @@ -0,0 +1,92 @@ +/* + * pam_warn module + * + * Written by Andrew Morgan 1996/3/11 + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include + +/* some syslogging */ + +#define OBTAIN(item, value, default_value) do { \ + (void) pam_get_item(pamh, item, &(value)); \ + (value) = (value) ? (value) : (default_value) ; \ +} while (0) + +static void log_items(pam_handle_t *pamh, const char *function, int flags) +{ + const void *service=NULL, *user=NULL, *terminal=NULL, + *rhost=NULL, *ruser=NULL; + + OBTAIN(PAM_SERVICE, service, ""); + OBTAIN(PAM_TTY, terminal, ""); + OBTAIN(PAM_USER, user, ""); + OBTAIN(PAM_RUSER, ruser, ""); + OBTAIN(PAM_RHOST, rhost, ""); + + pam_syslog(pamh, LOG_NOTICE, + "function=[%s] flags=%#x service=[%s] terminal=[%s] user=[%s]" + " ruser=[%s] rhost=[%s]\n", function, flags, + (const char *) service, (const char *) terminal, + (const char *) user, (const char *) ruser, + (const char *) rhost); +} + +/* --- authentication management functions (only) --- */ + +int pam_sm_authenticate(pam_handle_t *pamh, int flags, + int argc UNUSED, const char **argv UNUSED) +{ + log_items(pamh, __FUNCTION__, flags); + return PAM_IGNORE; +} + +int pam_sm_setcred(pam_handle_t *pamh, int flags, + int argc UNUSED, const char **argv UNUSED) +{ + log_items(pamh, __FUNCTION__, flags); + return PAM_IGNORE; +} + +/* password updating functions */ + +int pam_sm_chauthtok(pam_handle_t *pamh, int flags, + int argc UNUSED, const char **argv UNUSED) +{ + log_items(pamh, __FUNCTION__, flags); + return PAM_IGNORE; +} + +int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, + int argc UNUSED, const char **argv UNUSED) +{ + log_items(pamh, __FUNCTION__, flags); + return PAM_IGNORE; +} + +int +pam_sm_open_session(pam_handle_t *pamh, int flags, + int argc UNUSED, const char **argv UNUSED) +{ + log_items(pamh, __FUNCTION__, flags); + return PAM_IGNORE; +} + +int +pam_sm_close_session(pam_handle_t *pamh, int flags, + int argc UNUSED, const char **argv UNUSED) +{ + log_items(pamh, __FUNCTION__, flags); + return PAM_IGNORE; +} + +/* end of module definition */ diff --git a/modules/pam_warn/tst-pam_warn b/modules/pam_warn/tst-pam_warn new file mode 100755 index 0000000..0b48365 --- /dev/null +++ b/modules/pam_warn/tst-pam_warn @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_warn.so diff --git a/modules/pam_warn/tst-pam_warn-retval.c b/modules/pam_warn/tst-pam_warn-retval.c new file mode 100644 index 0000000..48b1f31 --- /dev/null +++ b/modules/pam_warn/tst-pam_warn-retval.c @@ -0,0 +1,88 @@ +/* + * Check pam_warn return values. + * + * Copyright (c) 2020 Dmitry V. Levin + */ + +#include "test_assert.h" + +#include +#include +#include +#include +#include + +#define MODULE_NAME "pam_warn" +#define TEST_NAME "tst-" MODULE_NAME "-retval" + +static const char service_file[] = TEST_NAME ".service"; +static const char user_name[] = ""; +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + FILE *fp; + char cwd[PATH_MAX]; + + ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd))); + + /* PAM_IGNORE -> PAM_PERM_DENIED */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "account required %s/" LTDIR "%s.so\n" + "password required %s/" LTDIR "%s.so\n" + "session required %s/" LTDIR "%s.so\n", + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME, + cwd, MODULE_NAME)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_PERM_DENIED, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + /* PAM_IGNORE -> PAM_SUCCESS */ + ASSERT_NE(NULL, fp = fopen(service_file, "w")); + ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n" + "auth required %s/" LTDIR "%s.so\n" + "auth required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "account required %s/" LTDIR "%s.so\n" + "account required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "password required %s/" LTDIR "%s.so\n" + "password required %s/../pam_permit/" LTDIR "pam_permit.so\n" + "session required %s/" LTDIR "%s.so\n" + "session required %s/../pam_permit/" LTDIR "pam_permit.so\n", + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd, + cwd, MODULE_NAME, cwd)); + ASSERT_EQ(0, fclose(fp)); + + ASSERT_EQ(PAM_SUCCESS, + pam_start_confdir(service_file, user_name, &conv, ".", &pamh)); + ASSERT_NE(NULL, pamh); + ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_acct_mgmt(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_chauthtok(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0)); + pamh = NULL; + + ASSERT_EQ(0, unlink(service_file)); + + return 0; +} diff --git a/modules/pam_wheel/README.xml b/modules/pam_wheel/README.xml new file mode 100644 index 0000000..e40c46e --- /dev/null +++ b/modules/pam_wheel/README.xml @@ -0,0 +1,27 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_wheel.8.xml" xpointer='xpointer(id("pam_wheel-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_wheel/meson.build b/modules/pam_wheel/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_wheel/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_wheel/pam_wheel.8.xml b/modules/pam_wheel/pam_wheel.8.xml new file mode 100644 index 0000000..af0fd61 --- /dev/null +++ b/modules/pam_wheel/pam_wheel.8.xml @@ -0,0 +1,240 @@ + + + + pam_wheel + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_wheel + Only permit root access to members of group wheel + + + + + pam_wheel.so + + debug + + + deny + + + group=name + + + root_only + + + trust + + + use_uid + + + + + + DESCRIPTION + + The pam_wheel PAM module is used to enforce the so-called + wheel group. By default it permits + access to the target user if the applicant user is a member of the + wheel group. If no group with this name exist, + the module is using the group with the group-ID + 0. + + + + + OPTIONS + + + + debug + + + + Print debug information. + + + + + + deny + + + + Reverse the sense of the auth operation: if the user + is trying to get UID 0 access and is a member of the + wheel group (or the group of the option), + deny access. Conversely, if the user is not in the group, return + PAM_IGNORE (unless was also specified, + in which case we return PAM_SUCCESS). + + + + + + group=name + + + + Instead of checking the wheel or GID 0 groups, use + the group + to perform the authentication. + + + + + + root_only + + + + The check for wheel membership is done only when the target user + UID is 0. + + + + + + trust + + + + The pam_wheel module will return PAM_SUCCESS instead + of PAM_IGNORE if the user is a member of the wheel group + (thus with a little play stacking the modules the wheel + members may be able to su to root without being prompted + for a passwd). + + + + + + use_uid + + + + The check will be done against the real uid of the calling process, + instead of trying to obtain the user from the login session + associated with the terminal in use. + + + + + + + + MODULE TYPES PROVIDED + + The auth and + account module types are provided. + + + + + RETURN VALUES + + + PAM_AUTH_ERR + + + Authentication failure. + + + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + PAM_IGNORE + + + The return value should be ignored by PAM dispatch. + + + + + PAM_PERM_DENY + + + Permission denied. + + + + + PAM_SERVICE_ERR + + + Cannot determine the user name. + + + + + + PAM_SUCCESS + + + Success. + + + + + + PAM_USER_UNKNOWN + + + User not known. + + + + + + + + + EXAMPLES + + The root account gains access by default (rootok), only wheel + members can become root (wheel) but Unix authenticate non-root + applicants. + +su auth sufficient pam_rootok.so +su auth required pam_wheel.so +su auth required pam_unix.so + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_wheel was written by Cristian Gafton <gafton@redhat.com>. + + + + \ No newline at end of file diff --git a/modules/pam_wheel/pam_wheel.c b/modules/pam_wheel/pam_wheel.c new file mode 100644 index 0000000..dd047af --- /dev/null +++ b/modules/pam_wheel/pam_wheel.c @@ -0,0 +1,301 @@ +/* + * pam_wheel module + * + * Written by Cristian Gafton 1996/09/10 + * See the end of the file for Copyright Information + * + * + * 1.2 - added 'deny' and 'group=' options + * 1.1 - added 'trust' option + * 1.0 - the code is working for at least another person, so... :-) + * 0.1 - use vsyslog instead of vfprintf/syslog in _pam_log + * - return PAM_IGNORE on success (take care of sloppy sysadmins..) + * - use pam_get_user instead of pam_get_item(...,PAM_USER,...) + * - a new arg use_uid to auth the current uid instead of the + * initial (logged in) one. + * 0.0 - first release + * + * TODO: + * - try to use make_remark from pam_unix/support.c + * - consider returning on failure PAM_FAIL_NOW if the user is not + * a wheel member. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * here, we make a definition for the externally accessible function + * in this file (this definition is required for static a module + * but strongly encouraged generally) it is used to instruct the + * modules include file to define the function prototypes. + */ + +#include +#include +#include +#include "pam_inline.h" + +/* argument parsing */ + +#define PAM_DEBUG_ARG 0x0001 +#define PAM_USE_UID_ARG 0x0002 +#define PAM_TRUST_ARG 0x0004 +#define PAM_DENY_ARG 0x0010 +#define PAM_ROOT_ONLY_ARG 0x0020 + +static int +_pam_parse (const pam_handle_t *pamh, int argc, const char **argv, + const char **use_group) +{ + int ctrl=0; + + *use_group = ""; + + /* step through arguments */ + for (ctrl=0; argc-- > 0; ++argv) { + const char *str; + + /* generic options */ + + if (!strcmp(*argv,"debug")) + ctrl |= PAM_DEBUG_ARG; + else if (!strcmp(*argv,"use_uid")) + ctrl |= PAM_USE_UID_ARG; + else if (!strcmp(*argv,"trust")) + ctrl |= PAM_TRUST_ARG; + else if (!strcmp(*argv,"deny")) + ctrl |= PAM_DENY_ARG; + else if (!strcmp(*argv,"root_only")) + ctrl |= PAM_ROOT_ONLY_ARG; + else if ((str = pam_str_skip_prefix(*argv, "group=")) != NULL) + *use_group = str; + else { + pam_syslog(pamh, LOG_ERR, "unknown option: %s", *argv); + } + } + + return ctrl; +} + +static int +perform_check (pam_handle_t *pamh, int ctrl, const char *use_group) +{ + const char *username = NULL; + const char *fromsu; + struct passwd *pwd, *tpwd = NULL; + struct group *grp; + int retval = PAM_AUTH_ERR; + + retval = pam_get_user(pamh, &username, NULL); + if (retval != PAM_SUCCESS) { + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_DEBUG, "cannot determine user name: %s", + pam_strerror(pamh, retval)); + } + return PAM_SERVICE_ERR; + } + + pwd = pam_modutil_getpwnam (pamh, username); + if (!pwd) { + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_NOTICE, "unknown user %s", username); + } + return PAM_USER_UNKNOWN; + } + if (ctrl & PAM_ROOT_ONLY_ARG) { + /* su to a non uid 0 account ? */ + if (pwd->pw_uid != 0) { + return PAM_IGNORE; + } + } + + if (ctrl & PAM_USE_UID_ARG) { + tpwd = pam_modutil_getpwuid (pamh, getuid()); + if (tpwd == NULL) { + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_NOTICE, "who is running me ?!"); + } + return PAM_SERVICE_ERR; + } + fromsu = tpwd->pw_name; + } else { + fromsu = pam_modutil_getlogin(pamh); + + /* if getlogin fails try a fallback to PAM_RUSER */ + if (fromsu == NULL) { + const char *rhostname; + + retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhostname); + if (retval != PAM_SUCCESS || rhostname == NULL) { + retval = pam_get_item(pamh, PAM_RUSER, (const void **)&fromsu); + } + } + + if (fromsu != NULL) { + tpwd = pam_modutil_getpwnam (pamh, fromsu); + } + + if (fromsu == NULL || tpwd == NULL) { + if (ctrl & PAM_DEBUG_ARG) { + pam_syslog(pamh, LOG_NOTICE, "who is running me ?!"); + } + return PAM_SERVICE_ERR; + } + } + + /* + * At this point fromsu = username-of-invoker; tpwd = pwd ptr for fromsu + */ + + if (!use_group[0]) { + if ((grp = pam_modutil_getgrnam (pamh, "wheel")) == NULL) { + grp = pam_modutil_getgrgid (pamh, 0); + } + } else { + grp = pam_modutil_getgrnam (pamh, use_group); + } + + if (grp == NULL) { + if (ctrl & PAM_DEBUG_ARG) { + if (!use_group[0]) { + pam_syslog(pamh, LOG_NOTICE, "no members in a GID 0 group"); + } else { + pam_syslog(pamh, LOG_NOTICE, + "no members in '%s' group", use_group); + } + } + if (ctrl & PAM_DENY_ARG) { + /* if this was meant to deny access to the members + * of this group and the group does not exist, allow + * access + */ + return PAM_IGNORE; + } else { + return PAM_AUTH_ERR; + } + } + + /* + * test if the user is a member of the group, or if the + * user has the "wheel" (sic) group as its primary group. + */ + + if (pam_modutil_user_in_group_uid_gid(pamh, tpwd->pw_uid, grp->gr_gid)) { + + if (ctrl & PAM_DENY_ARG) { + retval = PAM_PERM_DENIED; + + } else if (ctrl & PAM_TRUST_ARG) { + retval = PAM_SUCCESS; /* this can be a sufficient check */ + + } else { + retval = PAM_IGNORE; + } + + } else { + + if (ctrl & PAM_DENY_ARG) { + + if (ctrl & PAM_TRUST_ARG) { + retval = PAM_SUCCESS; /* this can be a sufficient check */ + } else { + retval = PAM_IGNORE; + } + + } else { + retval = PAM_PERM_DENIED; + } + } + + if (ctrl & PAM_DEBUG_ARG) { + if (retval == PAM_IGNORE) { + pam_syslog(pamh, LOG_NOTICE, + "Ignoring access request '%s' for '%s'", + fromsu, username); + } else { + pam_syslog(pamh, LOG_NOTICE, "Access %s to '%s' for '%s'", + (retval != PAM_SUCCESS) ? "denied":"granted", + fromsu, username); + } + } + + return retval; +} + +/* --- authentication management functions --- */ + +int +pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + const char *use_group; + int ctrl; + + ctrl = _pam_parse(pamh, argc, argv, &use_group); + + return perform_check(pamh, ctrl, use_group); +} + +int +pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED, + int argc UNUSED, const char **argv UNUSED) +{ + return PAM_SUCCESS; +} + +int +pam_sm_acct_mgmt (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + const char *use_group; + int ctrl; + + ctrl = _pam_parse(pamh, argc, argv, &use_group); + + return perform_check(pamh, ctrl, use_group); +} + +/* + * Copyright (c) Cristian Gafton , 1996, 1997 + * All rights reserved + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ diff --git a/modules/pam_wheel/tst-pam_wheel b/modules/pam_wheel/tst-pam_wheel new file mode 100755 index 0000000..4bf5d6a --- /dev/null +++ b/modules/pam_wheel/tst-pam_wheel @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_wheel.so diff --git a/modules/pam_xauth/README.xml b/modules/pam_xauth/README.xml new file mode 100644 index 0000000..04fc246 --- /dev/null +++ b/modules/pam_xauth/README.xml @@ -0,0 +1,31 @@ +
+ + + + + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="pam_xauth.8.xml" xpointer='xpointer(id("pam_xauth-name")/*)'/> + + + + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git a/modules/pam_xauth/meson.build b/modules/pam_xauth/meson.build new file mode 120000 index 0000000..ba2e9b5 --- /dev/null +++ b/modules/pam_xauth/meson.build @@ -0,0 +1 @@ +../module-meson.build \ No newline at end of file diff --git a/modules/pam_xauth/pam_xauth.8.xml b/modules/pam_xauth/pam_xauth.8.xml new file mode 100644 index 0000000..f5fc5a3 --- /dev/null +++ b/modules/pam_xauth/pam_xauth.8.xml @@ -0,0 +1,290 @@ + + + + pam_xauth + 8 + Linux-PAM + Linux-PAM Manual + + + + pam_xauth + PAM module to forward xauth keys between users + + + + + pam_xauth.so + + debug + + + xauthpath=/path/to/xauth + + + systemuser=UID + + + targetuser=UID + + + + + + DESCRIPTION + + The pam_xauth PAM module is designed to forward xauth keys + (sometimes referred to as "cookies") between users. + + + Without pam_xauth, when xauth is enabled and a user uses the + + su1 + command to assume another user's privileges, + that user is no longer able to access the original user's X display + because the new user does not have the key needed to access the + display. pam_xauth solves the problem by forwarding the key from + the user running su (the source user) to the user whose identity the + source user is assuming (the target user) when the session is created, + and destroying the key when the session is torn down. + + + This means, for example, that when you run + + su1 + from an xterm session, you will be able to run + X programs without explicitly dealing with the + + xauth1 + xauth command or ~/.Xauthority files. + + + pam_xauth will only forward keys if xauth can list a key connected + to the $DISPLAY environment variable. + + + Primitive access control is provided by + ~/.xauth/export in the invoking user's home + directory and ~/.xauth/import in the target + user's home directory. + + + If a user has a ~/.xauth/import file, the user + will only receive cookies from users listed in the file. If there is + no ~/.xauth/import file, the user will accept + cookies from any other user. + + + If a user has a .xauth/export file, the user will + only forward cookies to users listed in the file. If there is no + ~/.xauth/export file, and the invoking user is + not root, the user will forward cookies + to any other user. If there is no ~/.xauth/export + file, and the invoking user is root, + the user will not forward cookies to + other users. + + + Both the import and export files support wildcards (such as + *). Both the import and export files + can be empty, signifying that no users are allowed. + + + + + OPTIONS + + + + debug + + + + Print debug information. + + + + + + xauthpath=/path/to/xauth + + + + Specify the path the xauth program (it is expected in + /usr/X11R6/bin/xauth, + /usr/bin/xauth, or + /usr/bin/X11/xauth by default). + + + + + + systemuser=UID + + + + Specify the highest UID which will be assumed to belong to a + "system" user. pam_xauth will refuse to forward credentials to + users with UID less than or equal to this number, except for + root and the "targetuser", if specified. + + + + + + targetuser=UID + + + + Specify a single target UID which is exempt from the + systemuser check. + + + + + + + + MODULE TYPES PROVIDED + + Only the session type is provided. + + + + + RETURN VALUES + + + PAM_BUF_ERR + + + Memory buffer error. + + + + + + PAM_PERM_DENIED + + + Permission denied by import/export file. + + + + + + PAM_SESSION_ERR + + + Cannot determine user name, UID or access users home directory. + + + + + + PAM_SUCCESS + + + Success. + + + + + + PAM_USER_UNKNOWN + + + User not known. + + + + + + + + + EXAMPLES + + Add the following line to /etc/pam.d/su to + forward xauth keys between users when calling su: + +session optional pam_xauth.so + + + + + + IMPLEMENTATION DETAILS + + pam_xauth will work only if it is + used from a setuid application in which the + getuid() call returns the id of the user + running the application, and for which PAM can supply the name + of the account that the user is attempting to assume. The typical + application of this type is + + su1 + . + The application must call both pam_open_session() + and pam_close_session() with the ruid set to the + uid of the calling user and the euid set to root, and must have + provided as the PAM_USER item the name of the target user. + + + pam_xauth calls + + xauth1 + as the source user to extract the key for $DISPLAY, + then calls xauth as the target user to merge the key into the a + temporary database and later remove the database. + + + pam_xauth cannot be told to not remove the keys when the session + is closed. + + + + + FILES + + + ~/.xauth/import + + XXX + + + + ~/.xauth/export + + XXX + + + + + + + + SEE ALSO + + + pam.conf5 + , + + pam.d5 + , + + pam8 + + + + + + AUTHOR + + pam_xauth was written by Nalin Dahyabhai <nalin@redhat.com>, + based on original version by + Michael K. Johnson <johnsonm@redhat.com>. + + + + \ No newline at end of file diff --git a/modules/pam_xauth/pam_xauth.c b/modules/pam_xauth/pam_xauth.c new file mode 100644 index 0000000..5e80b31 --- /dev/null +++ b/modules/pam_xauth/pam_xauth.c @@ -0,0 +1,797 @@ +/* + * pam_xauth module + * + * Copyright 2001-2003 Red Hat, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef WITH_SELINUX +#include +#include +#endif + +#include "pam_cc_compat.h" +#include "pam_inline.h" + +#define DATANAME "pam_xauth_cookie_file" +#define XAUTHENV "XAUTHORITY" +#define HOMEENV "HOME" +#define XAUTHDEF ".Xauthority" +#define XAUTHTMP ".xauthXXXXXX" + +/* Possible paths to xauth executable */ +static const char * const xauthpaths[] = { +#ifdef PAM_PATH_XAUTH + PAM_PATH_XAUTH, +#endif + "/usr/X11R6/bin/xauth", + "/usr/bin/xauth", + "/usr/bin/X11/xauth" +}; + +/* Run a given command (with a NULL-terminated argument list), feeding it the + * given input on stdin, and storing any output it generates. */ +static int +run_coprocess(pam_handle_t *pamh, const char *input, char **output, + uid_t uid, gid_t gid, const char *command, const char *argv[]) +{ + int ipipe[2], opipe[2], i; + char buf[LINE_MAX]; + pid_t child; + char *buffer = NULL; + size_t buffer_size = 0; + struct sigaction newsa, oldsa; + + *output = NULL; + + /* Create stdio pipery. */ + if (pipe(ipipe) == -1) { + pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m"); + return -1; + } + if (pipe(opipe) == -1) { + pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m"); + close(ipipe[0]); + close(ipipe[1]); + return -1; + } + + memset(&newsa, '\0', sizeof(newsa)); + newsa.sa_handler = SIG_DFL; + if (sigaction(SIGCHLD, &newsa, &oldsa) == -1) { + pam_syslog(pamh, LOG_ERR, "failed to reset SIGCHLD handler: %m"); + close(ipipe[0]); + close(ipipe[1]); + close(opipe[0]); + close(opipe[1]); + return -1; + } + + /* Fork off a child. */ + child = fork(); + if (child == -1) { + pam_syslog(pamh, LOG_ERR, "Could not fork: %m"); + close(ipipe[0]); + close(ipipe[1]); + close(opipe[0]); + close(opipe[1]); + return -1; + } + + if (child == 0) { + /* Drop privileges. */ + if (setgid(gid) == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "setgid(%lu) failed: %m", + (unsigned long) getegid ()); + _exit (err); + } + if (setgroups(0, NULL) == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "setgroups() failed: %m"); + _exit (err); + } + if (setuid(uid) == -1) + { + int err = errno; + pam_syslog (pamh, LOG_ERR, "setuid(%lu) failed: %m", + (unsigned long) geteuid ()); + _exit (err); + } + /* Set the pipe descriptors up as stdin and stdout, and close + * everything else, including the original values for the + * descriptors. */ + if (dup2(ipipe[0], STDIN_FILENO) != STDIN_FILENO) { + int err = errno; + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin"); + _exit(err); + } + if (dup2(opipe[1], STDOUT_FILENO) != STDOUT_FILENO) { + int err = errno; + pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdout"); + _exit(err); + } + if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_IGNORE_FD, + PAM_MODUTIL_NULL_FD) < 0) { + _exit(1); + } + /* Run the command. */ + DIAG_PUSH_IGNORE_CAST_QUAL; + execv(command, (char *const *) argv); + DIAG_POP_IGNORE_CAST_QUAL; + /* Never reached. */ + _exit(1); + } + + /* We're the parent, so close the other ends of the pipes. */ + close(opipe[1]); + /* Send input to the process (if we have any), then send an EOF. */ + if (input) { + (void)pam_modutil_write(ipipe[1], input, strlen(input)); + } + close(ipipe[0]); /* close here to avoid possible SIGPIPE above */ + close(ipipe[1]); + + /* Read data output until we run out of stuff to read. */ + i = pam_modutil_read(opipe[0], buf, sizeof(buf)); + while ((i != 0) && (i != -1)) { + char *tmp; + /* Resize the buffer to hold the data. */ + tmp = realloc(buffer, buffer_size + i + 1); + if (tmp == NULL) { + /* Uh-oh, bail. */ + free(buffer); + close(opipe[0]); + waitpid(child, NULL, 0); + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + return -1; + } + /* Save the new buffer location, copy the newly-read data into + * the buffer, and make sure the result will be + * nul-terminated. */ + buffer = tmp; + memcpy(buffer + buffer_size, buf, i); + buffer[buffer_size + i] = '\0'; + buffer_size += i; + /* Try to read again. */ + i = pam_modutil_read(opipe[0], buf, sizeof(buf)); + } + /* No more data. Clean up and return data. */ + close(opipe[0]); + *output = buffer; + waitpid(child, NULL, 0); + sigaction(SIGCHLD, &oldsa, NULL); /* restore old signal handler */ + return 0; +} + +/* Free a data item. */ +static void +cleanup (pam_handle_t *pamh UNUSED, void *data, int err UNUSED) +{ + free (data); +} + +/* Check if we want to allow export to the other user, or import from the + * other user. */ +static int +check_acl(pam_handle_t *pamh, + const char *sense, const char *this_user, const char *other_user, + int noent_code, int debug) +{ + char *path = NULL; + struct passwd *pwd; + FILE *fp = NULL; + int i, fd = -1, save_errno; + struct stat st; + PAM_MODUTIL_DEF_PRIVS(privs); + + /* Check this user's file. */ + pwd = pam_modutil_getpwnam(pamh, this_user); + if (pwd == NULL) { + pam_syslog(pamh, LOG_ERR, + "error determining home directory for '%s'", + this_user); + return PAM_SESSION_ERR; + } + /* Figure out what that file is really named. */ + i = asprintf(&path, "%s/.xauth/%s", pwd->pw_dir, sense); + if (i < 0) { + pam_syslog(pamh, LOG_ERR, + "cannot allocate path buffer for ~/.xauth/%s", + sense); + return PAM_SESSION_ERR; + } + if (pam_modutil_drop_priv(pamh, &privs, pwd)) { + free(path); + return PAM_SESSION_ERR; + } + if (!stat(path, &st)) { + if (!S_ISREG(st.st_mode)) + errno = EINVAL; + else + fd = open(path, O_RDONLY | O_NOCTTY); + } + save_errno = errno; + if (pam_modutil_regain_priv(pamh, &privs)) { + if (fd >= 0) + close(fd); + free(path); + return PAM_SESSION_ERR; + } + if (fd >= 0) { + if (!fstat(fd, &st)) { + if (!S_ISREG(st.st_mode)) + errno = EINVAL; + else + fp = fdopen(fd, "r"); + } + if (!fp) { + save_errno = errno; + close(fd); + } + } + if (fp) { + char *buf = NULL; + size_t n = 0; + /* Scan the file for a list of specs of users to "trust". */ + while (getline(&buf, &n, fp) != -1) { + buf[strcspn(buf, "\r\n")] = '\0'; + if (fnmatch(buf, other_user, 0) == 0) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "%s %s allowed by %s", + other_user, sense, path); + } + free(buf); + fclose(fp); + free(path); + return PAM_SUCCESS; + } + } + /* If there's no match in the file, we fail. */ + if (debug) { + pam_syslog(pamh, LOG_DEBUG, "%s not listed in %s", + other_user, path); + } + free(buf); + fclose(fp); + free(path); + return PAM_PERM_DENIED; + } else { + /* Default to okay if the file doesn't exist. */ + errno = save_errno; + switch (errno) { + case ENOENT: + if (noent_code == PAM_SUCCESS) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "%s does not exist, ignoring", + path); + } + } else { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "%s does not exist, failing", + path); + } + } + free(path); + return noent_code; + case ENAMETOOLONG: + pam_syslog(pamh, LOG_ERR, + "error opening %s: %m", path); + free(path); + return PAM_SESSION_ERR; + default: + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "error opening %s: %m", path); + } + free(path); + return PAM_PERM_DENIED; + } + } +} + +int +pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + char *cookiefile = NULL, *xauthority = NULL, + *cookie = NULL, *display = NULL, *tmp = NULL, + *xauthlocalhostname = NULL; + const char *user, *xauth = NULL; + struct passwd *tpwd, *rpwd; + int fd, i, debug = 0; + int retval = PAM_SUCCESS; + uid_t systemuser = 499, targetuser = 0; + + /* Parse arguments. We don't understand many, so no sense in breaking + * this into a separate function. */ + for (i = 0; i < argc; i++) { + const char *str; + + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + continue; + } + if ((str = pam_str_skip_prefix(argv[i], "xauthpath=")) != NULL) { + xauth = str; + continue; + } + if ((str = pam_str_skip_prefix(argv[i], "targetuser=")) != NULL) { + long l = strtol(str, &tmp, 10); + if ((*str != '\0') && (*tmp == '\0')) { + targetuser = l; + } else { + pam_syslog(pamh, LOG_WARNING, + "invalid value for targetuser (`%s')", + argv[i] + 11); + } + continue; + } + if ((str = pam_str_skip_prefix(argv[i], "systemuser=")) != NULL) { + long l = strtol(str, &tmp, 10); + if ((*str != '\0') && (*tmp == '\0')) { + systemuser = l; + } else { + pam_syslog(pamh, LOG_WARNING, + "invalid value for systemuser (`%s')", + argv[i] + 11); + } + continue; + } + pam_syslog(pamh, LOG_WARNING, "unrecognized option `%s'", + argv[i]); + } + + if (xauth == NULL) { + size_t j; + for (j = 0; j < PAM_ARRAY_SIZE(xauthpaths); j++) { + if (access(xauthpaths[j], X_OK) == 0) { + xauth = xauthpaths[j]; + break; + } + } + if (xauth == NULL) { + /* xauth executable not found - nothing to do */ + return PAM_SUCCESS; + } + } + + /* If DISPLAY isn't set, we don't really care, now do we? */ + if ((display = getenv("DISPLAY")) == NULL) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "user has no DISPLAY, doing nothing"); + } + return PAM_SUCCESS; + } + + /* Read the target user's name. */ + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name"); + retval = PAM_SESSION_ERR; + goto cleanup; + } + rpwd = pam_modutil_getpwuid(pamh, getuid()); + if (rpwd == NULL) { + pam_syslog(pamh, LOG_ERR, + "error determining invoking user's name"); + retval = PAM_SESSION_ERR; + goto cleanup; + } + + /* Get the target user's UID and primary GID, which we'll need to set + * on the xauthority file we create later on. */ + tpwd = pam_modutil_getpwnam(pamh, user); + if (tpwd == NULL) { + pam_syslog(pamh, LOG_NOTICE, + "error determining target user's UID"); + retval = PAM_SESSION_ERR; + goto cleanup; + } + + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "requesting user %lu/%lu, target user %lu/%lu", + (unsigned long) rpwd->pw_uid, + (unsigned long) rpwd->pw_gid, + (unsigned long) tpwd->pw_uid, + (unsigned long) tpwd->pw_gid); + } + + /* If the UID is a system account (and not the superuser), forget + * about forwarding keys. */ + if ((tpwd->pw_uid != 0) && + (tpwd->pw_uid != targetuser) && + (tpwd->pw_uid <= systemuser)) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "not forwarding cookies to user ID %lu", + (unsigned long) tpwd->pw_uid); + } + retval = PAM_SESSION_ERR; + goto cleanup; + } + + + /* If current user and the target user are the same, don't + check the ACL list, but forward X11 */ + if (strcmp (rpwd->pw_name, tpwd->pw_name) != 0) { + + /* Check that both users are amenable to this. By default, this + * boils down to this policy: + * export(ruser=root): only if is listed in .xauth/export + * export(ruser=*) if is listed in .xauth/export, or + * if .xauth/export does not exist + * import(user=*): if is listed in .xauth/import, or + * if .xauth/import does not exist */ + i = (getuid() != 0 || tpwd->pw_uid == 0) ? PAM_SUCCESS : PAM_PERM_DENIED; + i = check_acl(pamh, "export", rpwd->pw_name, user, i, debug); + if (i != PAM_SUCCESS) { + retval = PAM_SESSION_ERR; + goto cleanup; + } + i = PAM_SUCCESS; + i = check_acl(pamh, "import", user, rpwd->pw_name, i, debug); + if (i != PAM_SUCCESS) { + retval = PAM_SESSION_ERR; + goto cleanup; + } + } else { + if (debug) + pam_syslog (pamh, LOG_DEBUG, "current and target user are the same, forward X11"); + } + + /* Figure out where the source user's .Xauthority file is. */ + if (getenv(XAUTHENV) != NULL) { + cookiefile = strdup(getenv(XAUTHENV)); + if (cookiefile == NULL) { + retval = PAM_SESSION_ERR; + goto cleanup; + } + } else if (asprintf(&cookiefile, "%s/%s", rpwd->pw_dir, XAUTHDEF) < 0) { + cookiefile = NULL; + retval = PAM_SESSION_ERR; + goto cleanup; + } + if (debug) { + pam_syslog(pamh, LOG_DEBUG, "reading keys from `%s'", + cookiefile); + } + + /* Read the user's .Xauthority file. Because the current UID is + * the original user's UID, this will only fail if something has + * gone wrong, or we have no cookies. */ + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "running \"%s %s %s %s %s\" as %lu/%lu", + xauth, "-f", cookiefile, "nlist", display, + (unsigned long) getuid(), (unsigned long) getgid()); + } + if (run_coprocess(pamh, NULL, &cookie, + getuid(), getgid(), + xauth, (const char *[]) { + xauth, "-f", cookiefile, "nlist", display, + NULL}) == 0) { + char *cookiedata; +#ifdef WITH_SELINUX + char *context_raw = NULL; +#endif + PAM_MODUTIL_DEF_PRIVS(privs); + + /* Check that we got a cookie. If not, we get creative. */ + if (((cookie == NULL) || (strlen(cookie) == 0)) && + (pam_str_skip_prefix(display, "localhost:") != NULL || + pam_str_skip_prefix(display, "localhost/unix:") != NULL)) { + char hostname[HOST_NAME_MAX + 1]; + /* Free the useless cookie string. */ + free(cookie); + cookie = NULL; + if (gethostname(hostname, sizeof(hostname)) != -1) { + const char *screen; + char *t; + + /* Append protocol and screen number to host. */ + screen = display + strcspn(display, ":"); + if (asprintf(&t, "%s/unix%s", + hostname, screen) >= 0) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "no key for `%s', " + "trying `%s'", + display, t); + } + /* Read the cookie for this display. */ + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "running " + "\"%s %s %s %s %s\" as " + "%lu/%lu", + xauth, + "-f", + cookiefile, + "nlist", + t, + (unsigned long) getuid(), + (unsigned long) getgid()); + } + run_coprocess(pamh, NULL, &cookie, + getuid(), getgid(), + xauth, (const char *[]) { + xauth, "-f", cookiefile, + "nlist", t, NULL}); + free(t); + } + } + } + + /* Check that we got a cookie, this time for real. */ + if ((cookie == NULL) || (strlen(cookie) == 0)) { + if (debug) { + pam_syslog(pamh, LOG_DEBUG, "no key"); + } + retval = PAM_SESSION_ERR; + goto cleanup; + } + + /* Generate the environment variable + * "XAUTHORITY=/filename". */ + if (asprintf(&xauthority, "%s=%s/%s", + XAUTHENV, tpwd->pw_dir, XAUTHTMP) < 0) { + xauthority = NULL; + if (debug) { + pam_syslog(pamh, LOG_DEBUG, "out of memory"); + } + retval = PAM_SESSION_ERR; + goto cleanup; + } + + /* Generate a new file to hold the data. */ + if (pam_modutil_drop_priv(pamh, &privs, tpwd)) { + retval = PAM_SESSION_ERR; + goto cleanup; + } +#ifdef WITH_SELINUX + if (is_selinux_enabled() > 0) { + struct selabel_handle *ctx = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (!ctx) { + pam_syslog(pamh, LOG_WARNING, + "could not initialize SELinux labeling handle: %m"); + } else { + if (selabel_lookup_raw(ctx, &context_raw, + xauthority + sizeof(XAUTHENV), S_IFREG) != 0) { + pam_syslog(pamh, LOG_WARNING, + "could not get SELinux label for '%s': %m", + xauthority + sizeof(XAUTHENV)); + } + selabel_close(ctx); + if (setfscreatecon_raw(context_raw)) { + pam_syslog(pamh, LOG_WARNING, + "setfscreatecon_raw(%s) failed: %m", context_raw); + } + } + } +#endif /* WITH_SELINUX */ + fd = mkstemp(xauthority + sizeof(XAUTHENV)); + if (fd < 0) + pam_syslog(pamh, LOG_ERR, + "error creating temporary file `%s': %m", + xauthority + sizeof(XAUTHENV)); +#ifdef WITH_SELINUX + if (context_raw != NULL) { + free(context_raw); + setfscreatecon_raw(NULL); + } +#endif /* WITH_SELINUX */ + if (fd >= 0) + close(fd); + if (pam_modutil_regain_priv(pamh, &privs) || fd < 0) { + retval = PAM_SESSION_ERR; + goto cleanup; + } + + /* Get a copy of the filename to save as a data item for + * removal at session-close time. */ + cookiedata = strdup(xauthority + sizeof(XAUTHENV)); + if (!cookiedata) { + retval = PAM_SESSION_ERR; + goto cleanup; + } + + /* Save the filename. */ + if (pam_set_data(pamh, DATANAME, cookiedata, cleanup) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_ERR, + "error saving name of temporary file `%s'", + cookiedata); + unlink(cookiedata); + free(cookiedata); + retval = PAM_SESSION_ERR; + goto cleanup; + } + + /* Set the new variable in the environment. */ + if (pam_putenv (pamh, xauthority) != PAM_SUCCESS) + pam_syslog(pamh, LOG_ERR, + "can't set environment variable '%s'", + xauthority); + putenv (xauthority); /* The environment owns this string now. */ + xauthority = NULL; /* Don't free environment variables. */ + + /* set $DISPLAY in pam handle to make su - work */ + { + char *d; + + if (asprintf(&d, "DISPLAY=%s", display) < 0) + { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + retval = PAM_SESSION_ERR; + goto cleanup; + } + + if (pam_putenv (pamh, d) != PAM_SUCCESS) + pam_syslog (pamh, LOG_ERR, + "can't set environment variable '%s'", d); + free (d); + } + + /* set XAUTHLOCALHOSTNAME to make sure that su - work under gnome */ + if ((xauthlocalhostname = getenv("XAUTHLOCALHOSTNAME")) != NULL) { + char *d; + + if (asprintf(&d, "XAUTHLOCALHOSTNAME=%s", xauthlocalhostname) < 0) { + pam_syslog(pamh, LOG_CRIT, "out of memory"); + retval = PAM_SESSION_ERR; + goto cleanup; + } + + if (pam_putenv (pamh, d) != PAM_SUCCESS) + pam_syslog (pamh, LOG_ERR, + "can't set environment variable '%s'", d); + free (d); + } + + /* Merge the cookie we read before into the new file. */ + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "writing key `%s' to temporary file `%s'", + cookie, cookiedata); + } + if (debug) { + pam_syslog(pamh, LOG_DEBUG, + "running \"%s %s %s %s %s\" as %lu/%lu", + xauth, "-f", cookiedata, "nmerge", "-", + (unsigned long) tpwd->pw_uid, + (unsigned long) tpwd->pw_gid); + } + run_coprocess(pamh, cookie, &tmp, + tpwd->pw_uid, tpwd->pw_gid, + xauth, (const char *[]) { + xauth, "-f", cookiedata, "nmerge", "-", NULL}); + + /* We don't need to keep a copy of these around any more. */ + free(tmp); + } +cleanup: + /* Unset any old XAUTHORITY variable in the environment. */ + if (retval != PAM_SUCCESS && getenv (XAUTHENV)) + unsetenv (XAUTHENV); + free(cookiefile); + free(cookie); + free(xauthority); + return retval; +} + +int +pam_sm_close_session (pam_handle_t *pamh, int flags UNUSED, + int argc, const char **argv) +{ + int i, debug = 0; + const char *user; + const void *data; + const char *cookiefile; + struct passwd *tpwd; + PAM_MODUTIL_DEF_PRIVS(privs); + + /* Try to retrieve the name of a file we created when + * the session was opened. */ + if (pam_get_data(pamh, DATANAME, &data) != PAM_SUCCESS) + return PAM_SUCCESS; + cookiefile = data; + + /* Parse arguments. We don't understand many, so + * no sense in breaking this into a separate function. */ + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "debug") == 0) { + debug = 1; + continue; + } + if (pam_str_skip_prefix(argv[i], "xauthpath=") != NULL) + continue; + if (pam_str_skip_prefix(argv[i], "systemuser=") != NULL) + continue; + if (pam_str_skip_prefix(argv[i], "targetuser=") != NULL) + continue; + pam_syslog(pamh, LOG_WARNING, "unrecognized option `%s'", + argv[i]); + } + + if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) { + pam_syslog(pamh, LOG_NOTICE, "cannot determine user name"); + return PAM_SESSION_ERR; + } + if (!(tpwd = pam_modutil_getpwnam(pamh, user))) { + pam_syslog(pamh, LOG_NOTICE, + "error determining target user's UID"); + return PAM_SESSION_ERR; + } + + if (debug) + pam_syslog(pamh, LOG_DEBUG, "removing `%s'", cookiefile); + if (pam_modutil_drop_priv(pamh, &privs, tpwd)) + return PAM_SESSION_ERR; + if (unlink(cookiefile) == -1 && errno != ENOENT) + pam_syslog(pamh, LOG_WARNING, "Couldn't remove `%s': %m", cookiefile); + if (pam_modutil_regain_priv(pamh, &privs)) + return PAM_SESSION_ERR; + + return PAM_SUCCESS; +} diff --git a/modules/pam_xauth/tst-pam_xauth b/modules/pam_xauth/tst-pam_xauth new file mode 100755 index 0000000..3294896 --- /dev/null +++ b/modules/pam_xauth/tst-pam_xauth @@ -0,0 +1,2 @@ +#!/bin/sh +../../tests/tst-dlopen .libs/pam_xauth.so diff --git a/pgp.keys.asc b/pgp.keys.asc new file mode 100644 index 0000000..b56d6ab --- /dev/null +++ b/pgp.keys.asc @@ -0,0 +1,138 @@ +pub 4096R/A340AEB7 2015-10-21 + Key fingerprint = 296D 6F29 A020 808E 8717 A884 2DB5 BD89 A340 AEB7 +uid Dmitry V. Levin +sub 4096R/39E16E36 2015-10-21 [expires: 2025-10-18] + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFYnbLEBEACsADGvgMHHpUYlc+g0KnDHeolGcP0rNmIHKsRTt+/liKwaVDMk +MlSsv/kyUwZPu0rVOnQy0lvVOjEXBViMbqcN6UTGMJbxLZKLgqY4a0B25CWKNuMT +qb3/1ZgeIXHpP1hMTk60+yca3eRBSwYUAv4PxUBV15ipnYc0pDxkMy3v8Ty3FSxT +aHnm0qxnRTgC/ZFSj66+iixlo4B2km2cHwuxm1NE4FQQ5y+liWB7ycb2snX4a00Q +IhJEre480viprcyXfw6GiixlRUWexRd+wBYdwLYy8fxvHw/fV5NhjF7Fy5GkXfC0 +MVCPE+PMbuhgRIhgbMN9UlRy1V2xOfBsJaYlQfJVfxGSC2n4S4mB3SOGlbn5G7Wb +vDqQAPyrfZ089siKsurLtJI2ksZBtPpoJ0Meh8tfqqOfzcSULrJPwPHleyZQNkgQ +ScuChJPVcJkZMv2aE3tcK//NiSXYhtTwWzc9TOIFN2PfaXh5oWFUcnAK+2FxQWW3 +D0jwx3njy2UcxqAVNjIUhNtSHtBXZKEMZgfWfpgzNNbxJIJMZqVA5L4/7LuINdIZ +aEl8VYb/89nMjVs+FMp55Zd/Va53Hugc7VxaS6JuFetC84ax4x2aKSGtiKj5CLhJ +TBuy1Z9t4RimWkj0x0l3D0tdtmwYvWYCVaF0A7/i21J8RwBTiBEfT41HowARAQAB +tCJEbWl0cnkgVi4gTGV2aW4gPGxkdkBhbHRsaW51eC5vcmc+iQI3BBMBCAAhBQJW +J3OlAhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheAAAoJEC21vYmjQK633lQQAJeb +7Rxnl7t9iMYqQXlS1T4GMHVhk1flrN7k7roOg19n8u+Y3WzszCqSvRG9r3kn4ZaN +ZYiLtGf3jwVoZIvxEdiMveL89orGObSbnDEA3bwe20MFMouYbPYX9LamGR08CZ9u +CQ5HjeKlyos0IcqdXyM2AZ6yfPG5C1FTTStRr94vlr+gYm0mCTPAXWIIwdf4wwlL +Noei1GpVKGdU47zVmRfoENUjl47ykXme7PnRKJr1mjDzdEsdzDgW7olrRzCG7mbK +tDoGby5DRrIquArjODhWdqX4iipzpaZesMsTkP0OHNkUYS9pDbMf6uJDmNXeNN4h +EphJQzkq2yGBGF8riIqiVmum3hPGA1pbomeUobVl+2fdsP3G+m2Py9jB75v0OheE +jey2+vhuSpVKVL8IuwFv5TYlU66RHY0tDFXO6S52KacIVAywQ2JdrSmfKtIVWOO2 +eDRbtaSKTiYA3dPRCyECUn24GgLFOCBfp6GgVF40uROepIZTU+wC9Txe9QnSuAcm +CWoQbq4CaJFROGoLFuFqaiuxhQkbv+yAqWg/+ozImsWSemRxLaT2bm6IHZT0VdnP +LuhSmC5EAKN3rx6oK2EyMSvwx3X8gKdkUzrg75d/tcS9eAmi7gMwpDPdKhkWaN4q +MCHZDLA+fIuk/CPMJnrvtR4BMq4Y62u+uVOtoqUAiQI6BBMBCAAkAhsDBQsJCAcD +BRUKCQgLBRYCAwEAAh4BAheABQJWJ3O0AhkBAAoJEC21vYmjQK630H4P/i79LBSd +/lqrPloeaehvYvSputXrmMjwc/vXKpEY1F8fM2yftIy8ED1RdZw2A8o9l7E6ZFRb +MKhMlrcTOiGL0TZg2ctOi1YpaRlFyzKE7bjyS44fC4OF5JJWsM9Xl2Am4/8ppgft +AthxWvuC4iBf7iHGuQYuKPlv99zw5kzmae7oH5QHcKj/OCLh49/06XEiXyoRcogR +XPx5WheeJigM4vX9gvqf2KsO3KwKTK7H2QjmAVtoTbmqBGvBvyqd43IDwuJSCAUu +hKpCRSK92W50IJ6YV8sOBSDOEOPskQypQyGqctj014pjkBfHM49lealcldMaVyPk +UqVWb5ldSBMvpAqrl0GPZR9tJ+MqMTYw0wTKvMr2jjgVfICTq1VJq2WlYL+oRn5K +M80q+vqP7cmYZG2hjnYMTwyqDRWy9ecnKqOTPSeGPhbxQvnVnuxeUP2izfuY8pET +exkbgXRNvDQXvMft/963k/OXETilaFCOe1rdzjSldgATnsLJVZxIBBzdYoMrmMZe +fb9DnYUbWIKc6NvEDeWBbaTF8pqZg34vj9HfNwaMqZUWcmQP+ehUh3w1lwSdjeIh +J7Z9qc9j+A+jl47QcJ0zpbS4U5K5D/e0qHlu5n2fOs1By/FslQoV6DDpIjnDgvrE +o/jv3yd0O2IK/fvTmJuDiVVlR+oqqncTQLF0iEYEEBECAAYFAlYnefIACgkQ2XqG +i/fduzrVdgCeIrs08mJ91aqZLEiDP4WjSWSd5HkAoJiCXZSkPRAN3VLsRsxefmkQ +ZaQjiEYEEBECAAYFAlYnehEACgkQfKvmrJ41Nh6TMQCfZ6mUun/PTqNnRKOBHXBW +9LSJqekAnRDhtkSwY5D4C4JMHiBBUv9SbzxwiQIcBBABAgAGBQJWPlP4AAoJEHvM +cvpUpXXN4uQP/jn+xSBiITRgCsdqC8P7E3cO4D7tVBwZrFDNx+8ARefK8XvSiIop +T0xR4OU2j65oGs9r5/F80eOdMFiazXkntNTv2xF4JTMBgme8QaG7EsRoG/pJqIV8 +s+G8xO8tdf7aQzRmYVvvwnd7jPnCioUQA20KwQ6EyvlLPE94OR63PGrm9SI+yHmF +xOn1k+uB2g0HjbZabnFTJuOxvsJNrh0I1XPYb227HsQxHdLJhoAN9ZoE3+nJ7xDl +9arCeI6sAcgwDb5B7tH//afD2eLm2DScPeoyBoU6yDI+V3thcVKAZgm8ow9z/wc9 +LyWtq83KsRp7qVy41m88DriHVHMmXLoXCQNQ8QSJl5BXhgs9DYK+AxvZEZL9v40C +H+ZTf1AvH7OPYb+wyYpL2zD1ngyMyr6xqhBXAWuxboL2KDsJfzrz+rwprlDDDxek +Ad075jGZDDVqgbM2HVD72pLRxnxAX4fi0tUSksF0x/zBBB1W9TPLMtVh2j1rlovB +HdMitiNlC/squJzscYM8+ovke5OTdyKxBHlMzACP2tlUCoW4T4ORtzbtXl+aAAmf +36o3pvs1wqkhgoZTmQic/o7ammVp4wHU1q+WzuRaELIbxPzca24hGz6A2VXBZif3 +jYUnLIb+ZYYB4XREZP9UH+7duHNj9CufIy96mN2aqjIqo1l0E6eKzhBmiQIcBBMB +CAAGBQJWPocJAAoJEP78ZdGBr2RKzJcP/0tmmfl+XKFaTXd/V9z3nPs+yE5yMFBy +QSiwFInEkNn7Cb/IsK2OeCtcOim7yrGKD5UyB8uNKGwJ52nf5mo+jtX/rtKd1Vxi +QkCU+g/x2lUGFJU5y3jTtxXK99i+0+sa5gT8vDgFOZb3lUgsEKIHeGFOS656ffiQ +tS8tp6Zs4AmPQxJcO28r/fKjP8yg2wUjZDvair/CWxM4FosHCXLa9dJDyYa3akMM +ETgYD+BmrgcSjiIjvv6ONesBJlLf/7PxwjZOLjbmnTocyJZqRRqZMaLc3FjOH4Md +EuzzM82OmpH36h7pjD7fXahEIs1LY3zsa6k7kZawX84oqBcVql70iZQ7Le+EwgMd +S649wywWxKrdDPSzMnKlCod1ado7DDjbkRFfB/0sJNcyWuuKnanZ+jtTdbb9oGfI +S/JlCu3JybmEdwuxpEa9xw9l+MUVRJIpcOVbEMCmNTKyabvFRciYnWav/xtYmjoA +5ivKT2NNOkNs9ycijG6h0xUTDwLcXv6cHzE2XHzh+HmAzoM9sV7SgEZl9Mt/pR+l +IrlmP/oZkDReAbToF6JfwQpPlKusyganJjL0JI8Yv+zwPOAa7F2KNE4jtmTdarxN +RxYqqvPj/NQlcbHOKB6MAKkVbmSlOl9tUIyU17hFZAatWkhOMeyXyLbb2efeDvaq +coMRLMswa6ukiQIcBBABCAAGBQJWPuOZAAoJENgdDwyO0+iCyHMP/jC7IXOp75XX +Q71yCqPLB5Fnb//i641Y5DtjfGXqILgNQKn+MBgeN8oW6IFZQI6YfUR1HKS6gwUn +PEGXOc/vSTv1ifjQ1LQE85wm+L+9RLVJqymdOBIGDhObWa0l8DzpKCb4gnIcLNFD +Jf3NkKswalby7GEVwe1e86dTQzGjiik/3Ii3jVxigOKtgLtQjBeEwKcPImJkewCY +a6lJx/Aylf8GhQ15a9KOHOSr+lbc+B5l57tWKrDMJCWXyYsJNV7LkJ0GSv6rmHrh +uWSr+Zqil0YqCTEawz6FmlhYSJjTz1NG7UGjyCFzhn7piU7w5JYqJOVnpkGcRlvu +6n1IbzGfHBiafUqAAcqxEoWRQyYHKyfoZBVBPVpgwdrx5M3XKe0HEx5DBq5aBdUK +8rU9JJ/fHrLgjb3XxxXHTARNKdLlzcpufAP4XnrYQL7Co7h/YSc1/D4T8DTne9M4 +4z3wcUzxl9pNGX06uhs9wcqFTu0Wwg0m3GUHizfO6NZ/RejBaVfcx/2rGeBxlSqf +SbIYfscrqmuL1HStjjUqmZIM70Rvobrp5nR610IRIRN918fAIE/ocwx0H/B/33bk +XeEuR7qhtwvaD2o9yosT1KIZE1r+A9FxnzmCs3v1uly/5X1PMNQQwERDItS9jDm5 +TzxHnUf1XdHc9E4P43jFrj8yNOT7iIrTiQIcBBMBCgAGBQJWP2aFAAoJEN0/YqbE +cdMw9ewP/iu6otC2BZE3id/1o7tyyvcdEkTx1sLOq+qRTSLuKoC/gB9KBjCeqh+L +mrr1yxDcN/VFXxSf5Y8zHrBD/twQ0kMbRu3r7+a+bAsxobfBcpGC2HCyHPTHyIL9 +9QNugMAm1XKWXJoNwc0GepEKpWyn0OhSXyX3TOCijZ6fxllBtU+1XjVOvwIyqV0f +v4GepiiOqI4B5RJRK1Hj2645nV8IAXExsJhaQGG57tAOVyNw80OmcFDISH1kaINL +ZkC+DNAHLV3jU8WmqhuT68FWjsTZ95DlsBfgUYoulX+vGlvlisyba6pwXxiI/GQH +mxIHVtZp3if88i+YDfyBXje8NVprgMRH4YStRBWjm7ki5zh99YR/VqGKt8NTYdIi +8yuaQTDHoJeWhvjCOguPb19i42RzbEVKY7zdKVziwYVELpGtLJ5Jhj7HMGkj/Q+3 +9n47IqpiQ2gmRtx3lncOtyKBcbG9CcBE4g5Jk0zIXzO1ksjBKy830PgPWng8CoY5 +Vh6gsFvq57wWOEQaWRr7LtKufainZIb5BiIX4LOQR6yo7j7RP0qdId8V79BNrbrB +5LLdFS0tWFmVQv6Rssq6Tmq6VW0NBv53D7ELj7DYHcRCpw+7b1PqFOkGfr4XLmWj +vzFRB+RB+xZdYgyQaWbXV9wSSgbhuUm8TP8/PsYKXNqDUExN1ixjiQIcBBABCAAG +BQJWQRe2AAoJEDVC+iPpPR9CRjMP/jHa+sAN91J+xrUMoZ/FSR+yzJXoz6c/gTs+ +XyKY8eGz9Q7x3O3bNNYaXsGBHttwTM82l8hxxlYBaDe/UJ/qZtGKoJ0qW6PI1UPb +yCzjS/uL8TSKWZHVcCUBklqUAsd+Za3VwRLCmQ6CuyvHsooOWpvcTuFKZwm2aA7i +IqisJBWtbiC5jVgJVo90blWkG9NOK/td5tP4iqw8fhiW6HPUlyOriVaYXaM352dO +RG2wWrF66rOfNFwG/HM3TxJU3cHbeSvNPbftWGaN9ob6w9boCnn3G6KSF1jOTfe5 +doVVmWrEhWDhtSBsn62MzsspdKxbKNUqS6CmUbZT79IrFtKUVOxdqdBaZpwrnESx +1ndGB/jYI+x92UqnRmNKIylqrx09wU/tUZoFGgQnohENc+LK676OxRbIcyhpfWDJ +27K54GHfDXRmAi6tPMp87GAG1FxEkZXJULHsiI9TfLZnCoTLlDQGv+UadxdM+dDe +A9hesK+UmohxVKUgyefaEMl9Yc5JfLiFlLA7aTI2E+V4/7nKjVeG+hUg/22y4ZYB +KhS/mLmWphXx3qo60j8Hu1oArp3ckl5nne5s9yYyxYj8+2UGSPri8t6PSJG1bBGy +0TM7F8THondVVA1wSsjEYB6Z8rPhV7cnN2G76nb9731kLkjT3b/A5i10UiW7NBrd +YWDm+eMZuQINBFYnbj4BEAC1LKikhtm6BC9J/sIpeOND/gUn4YYfxYCue2Be3Gh2 +Dig8gX2wNK8mB76AC9mScx5vwzj94PLfB7NJTQOav17icVoBDa7FZ17EgfiOCzlk +2fKdfjwhrwWpJ/tZzPPmFQyBoMzSLKAYRW5lQv3mz+u+BVZi4vcBE7L+rrEYGwki +rCjGox+JVg2NI7UWualSrNgHvy7u6thTJBYwu+EE78h8QvLqbjFa8kqKSTYZ4oFO +9FwQwLm61ANSriMpQ4dLdkE9t2ua+cHgrfojAKXyvY6M32A2a8xsFMZ2RPKvUgln ++vmr41mEgmS/bnHr8jHmATo4M3VrdxkxlxcW2Hxbh699aNx8tcJzJ24TGzyGfrGe +ncqB6u+lOZ+ngjrDZbayrPAGdc+XJ6hxCJ+krgmkj+dlRskf630QjEYahmxKdwW9 +L6KNudYOhjjr7VUbFFt5fd29E3QCjL8daYh92K87kzX6dVc0e+ENwO32B6mYNDRA +EC1cRS/3eiQvlGaDyfpWj4j3j9fTiM3CeJ8xTKW8YpMwg2FIaVT0v7CLg31mv4T6 +W6yFYykkiVvLvZSkf4LXxLJ+b5r6sykP1ALXxuSYMDkRiSkmKAUNdGdWrPCU7mU5 +ncQUH/XkH6KtxbxUL3EpaIkMJC/tJ8uq/eid11Lic+7eKdN7fnOrxSbVkIiBzrGB +uQARAQABiQREBBgBCAAPBQJWJ24+AhsCBQkSzAMAAikJEC21vYmjQK63wV0gBBkB +CAAGBQJWJ24+AAoJEKgEH6g54W424awP/16ClxGMNYxGIrjtOGk1Jkpb8wkPX8n4 +P4lJGSmBk1TLjH5Z4Py5HB43Z3aWuHivF8tl0QkLSuJb05eg2NrCEK/fI3XzCPVL +Twn4mkH7RGYTGdt2S6n551CpbsqXGvCQCmM7kf7mRwlkjpjpX7SAZ7PTLQNNFX0O +kltM8xCedorZ2Yr3jZlrkvT8VhqPYzvqkGIJ2nT8CRdLQ7Gag8imkFgkNrEEwEIz +ZE/A7rVJ86BPoloBkwUhJPFRNwLPX1pQ9VDc1Mr5/E25Msyl4/AAfhmp9FtkXLDk +UYHzHS8YMIRkSqxJjEN6x6JCcjXI6YJ5TsqiXUBEyUch7R1omVXodeK1ctvTxXkj +0dGB0+YTtsuQO9ATJIJRfHBFX5y/adl6sQwF1fI9x8iCXgcwIGVwh+sq/54x2HSZ +jT1aWjzJU1fhZU1WCp2rWU4oFwhzHRvMZDGh5oFGB8Vwhzz+dGshV6RD5fq6gUW2 +M2YRrNXh2fpjTj/slXR+QVAmqK99+kW/lAcByF5Y48aPxUha8OE0PlS4pBvDwl9N +RnjQJdZbDDinG354VkVAbXj6U+CCPkJBIHDvia/7q/Cn9U/fSeCtr7wl0hL0VSql +nJtOI9lUVmUIxZFH6PNf+RLSa4HUizcH6S/+QoAhHkBpOh1JzLuaMkXN/S3GqAg+ +MqxnwP8l2k5yQtEQAIDs+gmHB2iyzz61ka49spB8XajmFZxsmICjAe0zNxthECYl +7bwVsb7+JicJ2fNXWKAxIQ2FMy381HFV9fj4xw7I9jiMTg5arWOCeaRZ6u5gp2Fr +s1SvHJxonnt7phUjyvlOE2ifkoGI8W3JVGulMDg7C/RxKJZX1sa8Iy9Z51Uoj4LE +aFRwlkTTlIAKYgsdh3iMtwQgJZPe4z0g14IFc859A3fmMdwLH8DMkTuMVP101jdj +kAc8+nRXqnv/BftH+tKarL0/FTEjuBiAG7dINg1jBSWHDnT82gpQfANETwyG3Nho +Qe3dZnVepa731Pwhrbak+nCTC6CNbKpR240DKH+mGI9G1V5EDDtJmcH3EpszaLO/ ++4PYhNQln9yJflWymyx5jOGb7OK3/bPYQyx2WsMgt8+ZxHlpuSTcPIN61F2vi2xr +9Sb94KWnoNl5vqoQg4K9/UVNTrIrL4wj/A3PKB7jYfcSoN5ivHW3SIz4YL6QpDFc +P1pDJjK/EwNXQSABGRHACo9AKgPdtB2RPbwTWCWMHa+XsNpIaXK/2pllMCZelR+G +E9Cc3Op7kN6orLJx+uFIAYwQOwJvZKftmzGh6ALzuFjRIPtwlC65BhO2WbUTLwBy +fHBFeOnRy4QXWyZvwWtqFbsRpDzxcHZ615EzWZhv5yprGE7rMejp3NOxo7Mc +=gcmT +-----END PGP PUBLIC KEY BLOCK----- diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..469753d --- /dev/null +++ b/po/LINGUAS @@ -0,0 +1,82 @@ +af +am +ar +as +be +bg +bn_IN +bn +bs +ca +cs +cy +da +de_CH +de +el +eo +es +et +eu +fa +fi +fr +ga +gl +gu +he +hi +hr +hu +ia +id +is +it +ja +ka +kk +km +kn +ko +kw_GB +ky +lt +lv +mk +ml +mn +mr +ms +my +nb +ne +nl +nn +or +pa +pl +pt_BR +pt +ro +ru +si +sk +sl +sq +sr@latin +sr +sv +ta +te +tg +th +tr +uk +ur +vi +yo +zh_CN +zh_HK +zh_TW +zu +az diff --git a/po/Linux-PAM.pot b/po/Linux-PAM.pot new file mode 100644 index 0000000..72959df --- /dev/null +++ b/po/Linux-PAM.pot @@ -0,0 +1,495 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the Linux-PAM package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM 1.7.0\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "" diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..cd057f1 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,95 @@ +libpam/pam_account.c +libpam/pam_audit.c +libpam/pam_auth.c +libpam/pam_data.c +libpam/pam_delay.c +libpam/pam_dispatch.c +libpam/pam_dynamic.c +libpam/pam_end.c +libpam/pam_env.c +libpam/pam_get_authtok.c +libpam/pam_handlers.c +libpam/pam_item.c +libpam/pam_misc.c +libpam/pam_modutil_cleanup.c +libpam/pam_modutil_getgrgid.c +libpam/pam_modutil_getgrnam.c +libpam/pam_modutil_getlogin.c +libpam/pam_modutil_getpwnam.c +libpam/pam_modutil_getpwuid.c +libpam/pam_modutil_getspnam.c +libpam/pam_modutil_ingroup.c +libpam/pam_modutil_ioloop.c +libpam/pam_password.c +libpam/pam_prelude.c +libpam/pam_session.c +libpam/pam_start.c +libpam/pam_strerror.c +libpam/pam_syslog.c +libpam/pam_vprompt.c +libpam_misc/help_env.c +libpam_misc/misc_conv.c +libpamc/pamc_client.c +libpamc/pamc_converse.c +libpamc/pamc_load.c +modules/pam_access/pam_access.c +modules/pam_debug/pam_debug.c +modules/pam_deny/pam_deny.c +modules/pam_echo/pam_echo.c +modules/pam_env/pam_env.c +modules/pam_exec/pam_exec.c +modules/pam_faildelay/pam_faildelay.c +modules/pam_faillock/main.c +modules/pam_faillock/pam_faillock.c +modules/pam_filter/pam_filter.c +modules/pam_filter/upperLOWER/upperLOWER.c +modules/pam_ftp/pam_ftp.c +modules/pam_group/pam_group.c +modules/pam_issue/pam_issue.c +modules/pam_keyinit/pam_keyinit.c +modules/pam_lastlog/pam_lastlog.c +modules/pam_limits/pam_limits.c +modules/pam_listfile/pam_listfile.c +modules/pam_localuser/pam_localuser.c +modules/pam_loginuid/pam_loginuid.c +modules/pam_mail/pam_mail.c +modules/pam_mkhomedir/pam_mkhomedir.c +modules/pam_motd/pam_motd.c +modules/pam_namespace/md5.c +modules/pam_namespace/pam_namespace.c +modules/pam_nologin/pam_nologin.c +modules/pam_permit/pam_permit.c +modules/pam_pwhistory/opasswd.c +modules/pam_pwhistory/pam_pwhistory.c +modules/pam_rhosts/pam_rhosts.c +modules/pam_rootok/pam_rootok.c +modules/pam_securetty/pam_securetty.c +modules/pam_selinux/pam_selinux.c +modules/pam_selinux/pam_selinux_check.c +modules/pam_sepermit/pam_sepermit.c +modules/pam_shells/pam_shells.c +modules/pam_stress/pam_stress.c +modules/pam_succeed_if/pam_succeed_if.c +modules/pam_time/pam_time.c +modules/pam_timestamp/pam_timestamp.c +modules/pam_timestamp/pam_timestamp_check.c +modules/pam_tty_audit/pam_tty_audit.c +modules/pam_umask/pam_umask.c +modules/pam_unix/bigcrypt.c +modules/pam_unix/bigcrypt_main.c +modules/pam_unix/md5.c +modules/pam_unix/md5_broken.c +modules/pam_unix/md5_crypt.c +modules/pam_unix/md5_good.c +modules/pam_unix/pam_unix_acct.c +modules/pam_unix/pam_unix_auth.c +modules/pam_unix/pam_unix_passwd.c +modules/pam_unix/pam_unix_sess.c +modules/pam_unix/passverify.c +modules/pam_unix/support.c +modules/pam_unix/unix_chkpwd.c +modules/pam_unix/yppasswd_xdr.c +modules/pam_userdb/pam_userdb.c +modules/pam_warn/pam_warn.c +modules/pam_wheel/pam_wheel.c +modules/pam_xauth/pam_xauth.c diff --git a/po/af.po b/po/af.po new file mode 100644 index 0000000..80625e8 --- /dev/null +++ b/po/af.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Afrikaans (http://www.transifex.com/projects/p/fedora/" +"language/af/)\n" +"Language: af\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Jy moet 'n korter wagwoord kies." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Jy moet 'n korter wagwoord kies." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Jy moet 'n korter wagwoord kies." diff --git a/po/am.po b/po/am.po new file mode 100644 index 0000000..d2c6409 --- /dev/null +++ b/po/am.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Amharic (http://www.transifex.com/projects/p/fedora/language/" +"am/)\n" +"Language: am\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "አጠር ያለ የይለፍ ቃል መምረጥ አለብህ." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "አጠር ያለ የይለፍ ቃል መምረጥ አለብህ." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "አጠር ያለ የይለፍ ቃል መምረጥ አለብህ." diff --git a/po/ar.po b/po/ar.po new file mode 100644 index 0000000..bb3d86d --- /dev/null +++ b/po/ar.po @@ -0,0 +1,575 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +# Yaron Shahrabani , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-07-05 10:55+0000\n" +"Last-Translator: Yaron Shahrabani \n" +"Language-Team: Arabic \n" +"Language: ar\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " +"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" +"X-Generator: Weblate 4.1.1\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "كلمة السر: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "كلمة سر %s الجديدة: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "كلمة سر الجديدة: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "كلمة سر %s الجديدة: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "كلمة سر الجديدة: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "أعد كتابة كلمة سر %s الجديدة: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "أعد كتابة كلمة سر الجديدة: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "عذرًا، يوجد عدم تطابق بين كلمات السر." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "كلمة السر التي تم إدخالها مستخدمة بالفعل. اختر كلمة سر أخرى." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "تسجيل الدخول:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "نجاح" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "خطأ جسيم - إيقاف فوري" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "الرمز غير موجود" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "خطأ في الوحدة النمطية للخدمة" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "خطأ بالنظام" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "خطأ في الذاكرة الوسيطة" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "الإذن مرفوض" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "فشل التصديق" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "صلاحيات غير كافية للوصول إلى بيانات التصديق" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "تعذر على خدمة التصديق استرجاع معلومات التصديق" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "المستخدم مجهول بالنسبة لوحدة التصديق النمطية الأساسية" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "تم استنفاذ الحد الأقصى للمحاولة بالنسبة لهذه الخدمة" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "لم يعد الرمز المميز للتصديق صالحًا، مطلوب رمز مميز جديد" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "انتهت صلاحية حساب المستخدم" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "لا يمكن إنشاء/إزالة إدخال بالنسبة للجلسة المحددة" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "لا يمكن لخدمة التصديق استرجاع صلاحيات المستخدم" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "صلاحيات المستخدم منتهية الصلاحية" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "فشل تعيين صلاحيات المستخدم" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "لا يوجد بيانات خاصة بالوحدات النمطية" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "تم تمرير عنصر سيء إلى pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "خطأ في المحادثة" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "خطأ في معالجة الرمز المميز للتصديق" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "لا يمكن استعادة معلومات التصديق" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "قفل الرمز المميز للتصديق مشغول" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "تم تعطيل تقادم الرمز المميز للتصديق" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "فشل التحقق الأولي بواسطة خدمة كلمة السر" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "يجب تجاهل القيمة الناتجة بواسطة إرسال PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "الوحدة النمطية غير معروفة" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "انتهت صلاحية الرمز المميز للتصديق" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "المحادثة تنتظر الحدث" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "يحتاج التطبيق إلى استدعاء libpam مرة أخرى" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "خطأ PAM غير معروف" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...الوقت ينفد...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...عذرًا، انتهى الوقت!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "محادثة خاطئة (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "%a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "من %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "في %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "تسجيل الدخول الأخير:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "مرحبًا بك في حسابك الجديد!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "مرات تسجيل دخول كثيرة جدًا لـ '%s'." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "لديك بريد جديد." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "لديك بريد جديد." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "لديك بريد قديم." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "لديك بريد." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "ليس لديك بريد في مجلد %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "لديك بريد جديد في مجلد %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "لديك بريد قديم في مجلد %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "لديك بريد في مجلد %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "كلمة السر التي تم إدخالها مستخدمة بالفعل. اختر كلمة سر أخرى." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +#, fuzzy +msgid "Password has been already used." +msgstr "كلمة السر التي تم إدخالها مستخدمة بالفعل. اختر كلمة سر أخرى." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "لا يصلح كسياق أمان" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "لا يصلح كسياق أمان" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "تم تخصيص سياق الأمان %s" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "تم تخصيص سياق الأمان %s" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "فشلت تهيئة PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "فشل pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "تسجيل الدخول: فشل تشعيب: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "انتهت مدة صلاحية الحساب الخاص بك؛ الرجاء الاتصال بمسؤول النظام" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "مطلوب منك تغيير كلمة السر على الفور (مفروض بواسطة المسؤول)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "مطلوب منك تغيير كلمة السر على الفور (كلمة السر قديمة جدًا)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" +msgstr[4] "" +msgstr[5] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "تعذر تغيير كلمة السر الخاصة بـ NIS." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "لم يتم إدخال كلمة السر" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "تعذر تغيير كلمة السر الخاصة بـ NIS." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "يجب عليك اختيار كلمة مرور أقصر." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "يجب اختيار كلمة سر أطول" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "يجب الانتظار فترة أطول لتغيير كلمة السر" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "لديك بريد جديد." + +#~ msgid "is the same as the old one" +#~ msgstr "لا يوجد اختلاف عن كلمة السر القديمة" + +#~ msgid "is a palindrome" +#~ msgstr "كلمة سر يمكن قراءتها من الجهتين" + +#~ msgid "case changes only" +#~ msgstr "لم يتم سوى تغيير حالة الأحرف" + +#~ msgid "is too similar to the old one" +#~ msgstr "كلمة السر الجديدة شديدة الشبه بكلمة السر القديمة" + +#~ msgid "is too simple" +#~ msgstr "كلمة السر شديدة البساطة" + +#~ msgid "is rotated" +#~ msgstr "كلمة مرور ملتفة" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "كلمة سر سيئة: %s" + +#~ msgid "Authentication error" +#~ msgstr "خطأ في التصديق" + +#~ msgid "Service error" +#~ msgstr "خطأ في الخدمة" + +#~ msgid "Unknown user" +#~ msgstr "مستخدم غير معروف" + +#~ msgid "Unknown error" +#~ msgstr "خطأ غير معروف" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: تم إعطاء رقم خطأ لـ ‎--reset=‎\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: خيار غير معروف %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: لا يمكن إعادة تعيين كافة المستخدمين إلى رقم غير الصفر\n" diff --git a/po/as.po b/po/as.po new file mode 100644 index 0000000..03ca1f4 --- /dev/null +++ b/po/as.po @@ -0,0 +1,592 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Amitakhya Phukan , 2008-2009 +# Amitakhya Phukan , 2007 +# ngoswami , 2012-2013 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2013-04-10 02:40-0400\n" +"Last-Translator: ngoswami \n" +"Language-Team: Assamese (http://www.transifex.com/projects/p/fedora/language/" +"as/)\n" +"Language: as\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "গুপ্তশব্দ:" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "নতুন %s গুপ্তশব্দ: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "নতুন গুপ্তশব্দ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "নতুন %s গুপ্তশব্দ: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "নতুন গুপ্তশব্দ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "নতুন %s গুপ্তশব্দ পুনঃ লিখক: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "নতুন গুপ্তশব্দ পুনঃ লিখক: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "ক্ষমা কৰিব, গুপ্তশব্দৰ অমিল " + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s পুনঃ লিখক" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "গুপ্ত শব্দ সলনি কৰা বাতিল কৰা হ'ল ।" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "প্ৰৱেশ:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "সফলতা" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "গুৰুত্বপূৰ্ণ ভুল - অবিলম্ব বন্ধ কৰক" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "অংশ তুলি লওঁতে বিফল" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "প্ৰতিকৃতি পোৱা ন'গ'ল" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "সেৱাৰ অংশত ভুল" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "ব্যৱস্থাপ্ৰণালীৰ ভুল" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "স্মৃতিশক্তি প্ৰশমকৰ ভুল" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "আজ্ঞা নাই" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "প্ৰমাণীকৰণত বিফল" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "প্ৰমাণীকৰণৰ তথ্য উপক্ৰম কৰিবলৈ প্ৰশংসা পত্ৰৰ অভাৱ" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "প্ৰমাণীকৰণৰ সেৱাই প্ৰমাণীকৰণৰ তথ্য উদ্ধাৰ কৰিব নোৱাৰিলে" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "অন্তৰ্নিহিত প্ৰমাণীকৰণ অংশৰ বাবে ব্যৱহাৰকৰোঁতা অপৰিচিত" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "সেৱাৰ বাবে সৰ্বাধিক পুনঃ চেষ্টাৰ সংখ্যাও আজৰাই দিয়া হ'ল" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "প্ৰমাণীকৰণৰ প্ৰতীক চিহ্ন বৈধ নহয়; নতুন এটাৰ প্ৰয়োজন" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "ব্যৱহাৰকৰোঁতাৰ হিচাপ শেষ হ'ল" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "নিৰ্দ্দিষ্ট অধিবেশনৰ এটা নিবেশ বনাব/আঁতৰাব নোৱাৰি" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "ব্যৱহাৰকৰোঁতাৰ প্ৰশংসা পত্ৰ প্ৰমাণীকৰন সেৱাই উদ্ধাৰ কৰিব পৰা নাই" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "ব্যৱহাৰকৰোঁতাৰ প্ৰশংসা পত্ৰ অন্ত হ'ল" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "ব্যৱহাৰকৰোঁতাৰ প্ৰশংসা পত্ৰ প্ৰতিষ্ঠা কৰাত বিফল" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "কোনো অংশ নিৰ্দ্দিষ্ট তথ্য নাই" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item() লৈ বেয়া পদাৰ্থ আগবঢ়োৱা হ'ল" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "সম্বাদৰ ভুল" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "প্ৰমাণীকৰণ প্ৰতীক চিহ্নৰ পৰিবৰ্ত্তনত (মেনিপুলেশন) ভুল" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "প্ৰমাণীকৰণৰ তথ্য উদ্ধাৰ কৰিব পৰা ন'গ'ল" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "প্ৰমাণীকৰণ প্ৰতীক চিহ্নৰ লক ব্যস্ত" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "প্ৰমাণীকৰণ প্ৰতীক চিহ্নৰ পুৰণি হোৱা নিষ্ক্ৰীয় কৰা হ'ল" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "গুপ্তশব্দ সেৱাৰ দ্বাৰা প্ৰাৰম্ভিক পৰীক্ষা বিফল" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "PAM dispatch ৰ দ্বাৰা প্ৰত্যুত্তৰৰ মান আওকাণ কৰিব লাগে" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "অজ্ঞাত অংশ" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "প্ৰমাণীকৰণৰ প্ৰতীক চিহ্ন পুৰণি হ'ল" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "সম্বাদ কোনো ঘটনাৰ বাবে অপেক্ষা কৰিছে" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "অনুপ্ৰয়োগে আকৌ libpam ক মাতিব লাগে" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "অজ্ঞাত PAM ভুল" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...সময় পাৰ হৈ গৈছে...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...ক্ষমা কৰিব, আপোনাৰ বাবে সময় অন্ত হ'ল!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "ভুল সম্বাদ (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s বিফল: প্ৰস্থানৰ কোড %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s বিফল: %d%s সঙ্কেত ধৰা গ'ল" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s বিফল: অজ্ঞাত অৱস্থা 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "প্ৰৱেশ বিফল শেহতীয়া বিফলতা -ৰ পৰা\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u বিফল প্ৰৱেশৰ বাবে হিচাপ লক কৰা হৈছে" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s ৰ পৰা" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s ত" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "শেহতীয়া প্ৰৱেশ:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "আপোনাৰ নতুন হিচাপলৈ স্বাগতম!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "শেহতীয়া প্ৰৱেশ বিফল:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "শেহতীয়া সফল প্ৰৱেশৰ পিছত %d বিফল হোৱা প্ৰৱেশৰ চেষ্টা চলোৱা হৈছিল ।" +msgstr[1] "শেহতীয়া সফল প্ৰৱেশৰ পিছত %d বিফল হোৱা প্ৰৱেশৰ চেষ্টা চলোৱা হৈছিল ।" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "শেহতীয়া সফল প্ৰৱেশৰ পিছত %d বিফল হোৱা প্ৰৱেশৰ চেষ্টা চলোৱা হৈছিল ।" + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s' ৰ বাবে বহুতো প্ৰৱেশ ।" + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "আপোনাৰ নতুন ডাক আহিছে ।" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "আপোনাৰ নতুন ডাক আহিছে ।" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "আপেনাৰ ওচৰত পুৰণি ডাক আছে ।" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "আপোনাৰ ডাক আহিছে ।" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "%s ফোল্ডাৰত আপোনাৰ কোনো ডাক নাই ।" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "%s ফোল্ডাৰত আপোনাৰ নতুন ডাক আছে ।" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "%s ফোলডাৰত আপোনাৰ পুৰণি ডাক আছে ।" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "%s ফোল্ডাৰত আপোনাৰ ডাক আছে ।" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "'%s' পঞ্জিকা সৃষ্টি কৰা হৈছে ।" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "%s পঞ্জিকা সৃষ্টি আৰু আৰম্ভ কৰিব পৰা নাই ।" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "গুপ্তশব্দ ইতিমধ্যে ব্যৱহৃত । অন্য এটা বাচি লওক ।" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "পাছৱাৰ্ড ইতিমধ্যে ব্যৱহাৰ হৈছে।" + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "অবিকল্পিত সুৰক্ষাৰ সন্দৰ্ভ %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "বেলেগ এটা সুৰক্ষাৰ ভূমিকা বা স্তৰ নিবেশ কৰিব খোজে নেকি ?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ভূমিকা: " + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "%s ভূমিকা বাবে অবিকল্পিত ধৰণ নাই\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "স্তৰ: " + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "এটা বৈধ সুৰক্ষাৰ সন্দৰ্ভ নহয়" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "সুৰক্ষাৰ সন্দৰ্ভ %s নিযুক্ত কৰা হ'ল" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "চাবি নিৰ্মাণৰ সন্দৰ্ভ %s নিযুক্ত কৰা হ'ল" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM আৰম্ভ কৰাত বিফল\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() কৰোঁতে বিফল\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "প্ৰৱেশ: forking ত বিফল: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "অভিগম্যতাৰ অনুমতি (শেহতীয়া অভিগম্যতা %ld ছেকেণ্ড আগতে) ।" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "আপোনাৰ হিচাপ অন্ত হ'ল; অনুগ্ৰহ কৰি আপোনাৰ ব্যৱাস্থাপ্ৰণালীৰ " + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "আপুনি আপোনাৰ গুপ্তশব্দ সলনি কৰাটো প্ৰয়োজনীয় হৈ পৰিছে (ৰূটৰ দ্বাৰা বলবৎ)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "আপুনি আপোনাৰ গুপ্তশব্দ সলনি কৰাটো প্ৰয়োজনীয় হৈ পৰিছে (গুপ্তশব্দ পুৰণি হ'ল)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "সকীয়নী: আপোনাৰ গুপ্তশব্দ %d দিনত অন্ত হ'ব" +msgstr[1] "সকীয়নী: আপোনাৰ গুপ্তশব্দ %d দিনত অন্ত হ'ব" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "সকীয়নী: আপোনাৰ গুপ্তশব্দ %d দিনত অন্ত হ'ব" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS গুপ্তশব্দ সলনি কৰিব পৰা নহয় ।" + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "কোনো গুপ্তশব্দ দিয়া হোৱা নাই" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS গুপ্তশব্দ সলনি কৰিব পৰা নহয় ।" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "আপুনি ইয়াতকৈ এটা দীঘল গুপ্তশব্দ নিৰ্ব্বাচন কৰিব লাগিব" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "আপুনি ইয়াতকৈ এটা দীঘল গুপ্তশব্দ নিৰ্ব্বাচন কৰিব লাগিব" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s ৰ বাবে গুপ্তশব্দ সলনি কৰা হৈছে ।" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "আপোনাৰ গুপ্তশব্দ সলনি কৰিবলৈ আপুনি আৰু কিছু পৰ অপেক্ষা কৰিব লাগিব" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "আপোনাৰ নতুন ডাক আহিছে ।" + +#~ msgid "is the same as the old one" +#~ msgstr "পুৰণিটোৰ সৈতে একেই" + +#~ msgid "memory allocation error" +#~ msgstr "মেমৰি আবন্টন ত্ৰুটি" + +#~ msgid "is a palindrome" +#~ msgstr "এটা অনুলোম‌-বিলোম বাক্য" + +#~ msgid "case changes only" +#~ msgstr "অকল কেচ সলনি কৰা" + +#~ msgid "is too similar to the old one" +#~ msgstr "পৰণিটোৰ সৈতে বহুত একেই" + +#~ msgid "is too simple" +#~ msgstr "বৰ সৰল" + +#~ msgid "is rotated" +#~ msgstr "পকোৱা হৈছে" + +#~ msgid "not enough character classes" +#~ msgstr "পৰ্যাপ্ত character classes নাই" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "এটাৰ পিছত এটা বহুতো একেই আখৰ আছে" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "অতি দীঘল একেধৰণৰ আখৰ ক্ৰমৰ অন্তৰ্ভুক্ত কৰে" + +#~ msgid "contains the user name in some form" +#~ msgstr "কিবা ধৰনত ব্যৱহাৰকৰ্তাৰ নাম আছে" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "বেয়া গুপ্তশব্দ: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "হিচাপ অস্থায়ীৰূপে লক কৰা হৈছে (%ld ছেকেণ্ড আৰু আছে)" + +#~ msgid "Authentication error" +#~ msgstr "প্ৰমাণীকৰণত ভুল" + +#~ msgid "Service error" +#~ msgstr "সেৱাৰ ভুল" + +#~ msgid "Unknown user" +#~ msgstr "অজ্ঞাত ব্যৱহাৰকৰোঁতা" + +#~ msgid "Unknown error" +#~ msgstr "অজ্ঞাত ভুল" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset= লৈ বেয়া সংখ্যা দিয়া গৈছে\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: অপৰিচিত বিকল্প %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: সকলো ব্যৱহাৰকৰোঁতাক শূণ্য নোহোৱা অৱস্থালৈ পুনঃ প্ৰতিষ্ঠা কৰিব নোৱাৰি\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/az.po b/po/az.po new file mode 100644 index 0000000..905a854 --- /dev/null +++ b/po/az.po @@ -0,0 +1,498 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the Linux-PAM package. +# Alesker Abdullayev , 2020. +# Alesker Abdullayev - FEDORA Azerbaijan , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM 1.4.0\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-04-25 00:40+0000\n" +"Last-Translator: Alesker Abdullayev - FEDORA Azerbaijan \n" +"Language-Team: Azerbaijani \n" +"Language: az\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.0.1\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Şifrə: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Cari %s şifrəniz: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Cari şifrəniz: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Yeni %s şifrəniz: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Yeni şifrəniz: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "daxil ol:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Uğurlu" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritik xəta - təcili əməliyyatı dayandırın" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Modul yüklənmədi" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Simvol tapılmadı" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Xidməti modulda xəta" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Sistem xətası" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Yaddaş qoruyucusunda xəta" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "İcazə rədd edildi" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Doğrulama uğursuzluğu" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" +"Doğrulama məlumatlarına daxil olmaq üçün yetərli sertifikatlar mövcud deyil" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Doğrulama xidməti identifikasiya məlumatlarını bərpa edə bilmir" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "İstifadəçi əsas identifikasiya modulu tərəfindən tanımlana bilmir" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Bağışlayın, vaxtınız bitdi!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "" diff --git a/po/be.po b/po/be.po new file mode 100644 index 0000000..2decea7 --- /dev/null +++ b/po/be.po @@ -0,0 +1,507 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Belarusian (http://www.transifex.com/projects/p/fedora/" +"language/be/)\n" +"Language: be\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Вы павінны выбраць больш кароткі пароль." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Вы павінны выбраць больш кароткі пароль." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Вы павінны выбраць больш кароткі пароль." diff --git a/po/bg.po b/po/bg.po new file mode 100644 index 0000000..a000d12 --- /dev/null +++ b/po/bg.po @@ -0,0 +1,586 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Valentin Laskov , 2012-2013 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-12-20 08:00+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Bulgarian \n" +"Language: bg\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Парола: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Nastoyashta парола %s: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Nastoyashta парола: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Нова парола %s: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Нова парола: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Отново новата парола %s: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Отново новата парола: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Съжаляваме, паролите не съвпадат." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Напишете пак %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Промяната на паролата е прекратена." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "влизане:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Успех" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Критична грешка - незабавно прекратяване" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Неуспех при зареждане на модул" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Не е намерен символ" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Грешка в обслужващ модул" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Системна грешка" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Грешка в буферна памет" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Позволението отказано" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Грешка при удостоверяване" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" +"Недостатъчно данни за удостоверяване за достъп до удостоверителни данни" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Услугата за удостоверяване не можа да извлече данни за удостоверяване" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Непознат потребител за поддържащия удостоверяването модул" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Максималният брой опити за услугата беше изчерпан" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Удостоверителният токен вече не е валиден; необходим е нов" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Потребителският акаунт е изтекъл" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Не мога да създам/премахна запис за зададената сесия" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"Удостоверяващата услуга не може да извлече данните за удостоверяване на " +"потребителя" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Данните за удостоверяване на потребителя са с изтекъл срок" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Неуспех при задаване данни за удостоверяване на потребител" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Липсват специфични за модула данни" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Грешен елемент, подаден към pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Грешка в диалога" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Грешка при манипулирането с удостоверителния токен" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Удостоверителната информация не може да бъде възстановена" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Заключването на удостоверителния токен е заето" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Следенето на възрастта на удостоверителния токен е забранено" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Предварителната проверка от услугата парола не успя" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Върнатата стойност трябва да бъде игнорирана от PAM dispatch" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Модулът е непознат" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Срокът на удостоверителния токен е изтекъл" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Диалогът чака събитие" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Приложението се нуждае от повторно извикване на libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Непозната PAM грешка" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Времето изтича...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Съжаляваме, Вашето време изтече!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "погрешен разговор (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s се провали: код на грешка %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s се провали: уловен сигнал %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s се провали: непознат статус 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Usage: %s [--dir /път/към/директорията-на-tally] [--user потребителско_име] " +"[--reset]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Влязъл Неуспехи Последен неуспех От\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Акаунтът е заключен поради %u неуспешни опита за влизане." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, fuzzy, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(остават %d мин. за отключване)" +msgstr[1] "(остават %d мин. за отключване)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(остават %d мин. за отключване)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " от %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " на %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Последно влизане:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Добре дошли в новия Ви акаунт!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Последно неуспешно влизане:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"След последното успешно влизане, имаше %d неуспешен опит за влизане." +msgstr[1] "" +"След последното успешно влизане, имаше %d неуспешни опита за влизане." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "След последното успешно влизане, имаше %d неуспешни опита за влизане." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Твърде много влизания за '%s'." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Имате нови писма." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Имате нови писма." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Имате стари писма." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Имате писма." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Нямате писма в папката %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Имате нови писма в папка %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Имате стари писма в папка %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Имате писма в папка %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Създаване на директория '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Не мога да създам и настроя директория '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Паролата вече е използвана. Изберете друга." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Паролата вече е използвана." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Подразбиращ се контекст за сигурност %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Искате ли да въведете различна роля или ниво?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "роля:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Няма подразбиращ се тип за роля %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "ниво:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Невалиден контекст за сигурност." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Не може да бъде получен валиден контекст за %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Зададен е контекст за сигурност %s." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Зададен е key creation context %s." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM инициализацията не успя\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() не успя \n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "вход: неуспех при стартиране: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Достъпът е предоставен (последният достъп бе преди %ld секунди)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"Срокът на акаунта Ви е изтекъл; моля, свържете се с администратора на " +"системата." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Изисква се веднага да смените паролата си (изискано от администратор)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Изисква се веднага да смените паролата си (срокът и изтича)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Внимание: срокът на паролата Ви изтича след %d ден." +msgstr[1] "Внимание: срокът на паролата Ви изтича след %d дни." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Внимание: паролата Ви ще изтече след %d дни." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS парола не може да бъде променена." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Не е предоставена парола." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Паролата не е променена." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Трябва да изберете по-кратка парола." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Трябва да изберете по-дълга парола." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Смяна на паролата за %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Трябва да изчакате повече, за да промените Вашата парола." + +#~ msgid "You have no mail." +#~ msgstr "Нямате писма." + +#~ msgid "is the same as the old one" +#~ msgstr "е същата като старата" + +#~ msgid "memory allocation error" +#~ msgstr "грешка при заделяне на памет" + +#~ msgid "is a palindrome" +#~ msgstr "е палиндром" + +#~ msgid "case changes only" +#~ msgstr "променена е само големината на буквите" + +#~ msgid "is too similar to the old one" +#~ msgstr "е твърде подобна на старата" + +#~ msgid "is too simple" +#~ msgstr "е много лесна" + +#~ msgid "is rotated" +#~ msgstr "е завъртяна" + +#~ msgid "not enough character classes" +#~ msgstr "недостатъчно класове знаци" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "съдържа твърде много еднакви последователни знаци" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "съдържа твърде дълга еднообразна последователност знаци" + +#~ msgid "contains the user name in some form" +#~ msgstr "под някаква форма съдържа името на потребителя" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ЛОША ПАРОЛА: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Акаунтът е заключен временно (%ld остават секунди)." + +#~ msgid "Authentication error" +#~ msgstr "Грешка при удостоверяването" + +#~ msgid "Service error" +#~ msgstr "Грешка в услугата" + +#~ msgid "Unknown user" +#~ msgstr "Непознат потребител" + +#~ msgid "Unknown error" +#~ msgstr "Непозната грешка" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Грешно число, подадено на --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Неразпозната опция %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Не мога да установя всички потребители на non-zero\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/bn.po b/po/bn.po new file mode 100644 index 0000000..f93c461 --- /dev/null +++ b/po/bn.po @@ -0,0 +1,599 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Ayesha Akhtar , 2012. +# Mahay Alam Khan , 2012. +# Mahay Alam Khan , 2012. +# Newton Baidya , 2012. +# Robin Mehdee , 2012. +# Runa Bhattacharjee , 2009. +# Runa Bhattacharjee , 2007, 2008. +# , 2012. +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2012-04-10 04:36-0400\n" +"Last-Translator: Mahay Alam Khan \n" +"Language-Team: Bengali \n" +"Language: bn\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "পাসওয়ার্ড: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "নতুন %s পাসওয়ার্ড: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "নতুন পাসওয়ার্ড: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "নতুন %s পাসওয়ার্ড: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "নতুন পাসওয়ার্ড: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "নতুন %s পাসওয়ার্ড পুনরায় লিখুন: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "নতুন পাসওয়ার্ড পুনরায় লিখুন: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "দুঃখিত, পাসওয়ার্ড দুটি এক নয়।" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s পুনরায় লিখুন" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "পাসওয়ার্ড পরিবর্তন প্রক্রিয়া বাতিল করা হয়েছে।" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "লগ-ইন:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "সফল" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "গুরুতর সমস্যা - এই মুহূর্তে পরিত্যাগ করা হবে" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "মডিউল লোড করতে ব্যর্থ" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "চিহ্ন পাওয়া যায়নি" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "পরিসেবা মডিউলে সমস্যা" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "সিস্টেম সংক্রান্ত সমস্যা" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "মেমরি বাফার সংক্রান্ত সমস্যা" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "অনুমতি প্রদান করা হয়নি" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "অনুমোদন ব্যর্থ" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "অনুমোদন সংক্রান্ত তথ্য প্রাপ্ত করার জন্য পর্যাপ্ত প্রমাণ উপলব্ধ নেই" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "অনুমোদন পরিসেবা দ্বারা অনুমোদন সংক্রান্ত তথ্য উদ্ধার করা সম্ভব হয়নি" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "পটভূমিতে চলমান অনুমোদন ব্যবস্থায় ব্যবহারকারী পরিচিত নন।" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "পরিসেবায় উপলব্ধ সর্বাধিক প্রচেষ্টার সুযোগ সংখ্যা সমাপ্ত" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "অনুমোদনের টোকেন বৈধ নয়; নতুন টোকেন ব্যবহার করা আবশ্যক" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "ব্যবহারকারী অ্যাকাউন্টের মেয়াদ পূর্ণ হয়েছে" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "সুনির্দিষ্ট সেশানের জন্য কোনো এন্ট্রি নির্মাণ/অপসারণ করা সম্ভব নয়" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "অনুমোদন পরিসেবা দ্বারা প্রয়োজনীয় প্রমাণ উদ্ধার করা সম্ভব হয়নি" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "ব্যবহারকারীর পরিচয়প্রমাণের তথ্যের মেয়াদ পূর্ণ হয়েছে" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "ব্যবহারকারীর পরিচয়প্রমাণের তথ্য নির্ধারণ করতে ব্যর্থ" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "মডিউল সংক্রান্ত কোনো তথ্য উপস্থিত নেই" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item()-এ সঠিক মান প্রেরিত হয়নি" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Conversation অর্থাৎ তথ্য বিনিময়কালীন সমস্যা" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "অনুমোদন টোকেন ব্যবস্থাপনা করতে সমস্যা" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "অনুমোদন সংক্রান্ত তথ্য পুনরুদ্ধার করতে ব্যর্থ" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "অনুমোদন টোকেনের লক ব্যস্ত" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "অনুমোদন টোকেনের মেয়াদ পূর্তী ব্যবস্থা নিষ্ক্রিয়" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "পাসওয়ার্ড পরিসেবা দ্বারা প্রারম্ভিক পরীক্ষা ব্যর্থ" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "উৎপন্ন মান PAM dispatch দ্বারা অগ্রাহ্য করা হবে" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "মডিউল অজানা" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "অনুমোদন টোকেনের মেয়াদ পূর্ণ হয়েছে" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Conversation অর্থাৎ তথ্য বিনিময় প্রক্রিয়া একটি ইভেন্টের অপেক্ষায় রয়েছে" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "অ্যাপ্লিকেশন দ্বারা পুনরায় libpam আরম্ভ করা আবশ্যক" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "PAM সংক্রান্ত অজানা ত্রুটি" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...সময় সমাপ্তির পথে...⏎ \n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...দুঃখিত, সময় সমাপ্ত!⏎ \n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "ত্রুটিপূর্ণ তথ্যবিনিময় (%d)⏎\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s বিফল: প্রস্থানকালীন কোড %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s বিফল: %d%s সিগনাল প্রাপ্ত" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s বিফল: অজানা অবস্থা 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "লগ-ইন বিফলতা সর্বশেষ বিফলতা চিহ্নিত স্থান থেকে\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u ব্যর্থ লগ-ইনের ফলে অ্যাকাউন্ট লক করা হয়েছে" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s থেকে" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s -র উপর" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "সর্বশেষ লগ-ইন:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "নতুন অ্যাকাউন্টে স্বাগতম!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "সর্বশেষ বিফল লগ-ইন:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "সর্বশেষ সফল লগ-ইনের পরে %d-টি ব্যর্থ লগ-ইনের প্রচেষ্টা করা হয়েছে।" +msgstr[1] "সর্বশেষ সফল লগ-ইনের পরে %d-টি ব্যর্থ লগ-ইনের প্রচেষ্টা করা হয়েছে।" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "সর্বশেষ সফল লগ-ইনের পরে %d-টি ব্যর্থ লগ-ইনের প্রচেষ্টা করা হয়েছে।" + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s'-র ক্ষেত্রে অত্যাধিক লগ-ইন" + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "নতুন মেইল প্রাপ্ত।" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "নতুন মেইল প্রাপ্ত।" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "পুরোনো মেইল রয়েছে।" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "মেইল রয়েছে।" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "%s ফোল্ডারে কোনো মেইল উপস্থিত নেই।" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "%s ফোল্ডারে নতুন মেইল উপস্থিত।" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "%s ফোল্ডারে পুরোনো মেইল উপস্থিত রয়েছে।" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "%s ফোল্ডারে মেইল উপস্থিত রয়েছে।" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "'%s' ডিরেক্টরি নির্মাণ করা হচ্ছে।" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "ডিরেক্টরি '%s' নির্মাণ ও আরম্ভ করতে ব্যর্থ।" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "পাসওয়ার্ড পূর্বে ব্যবহৃত হয়েছে। একটি পৃথক পাসওয়ার্ড নির্বাচন করুন।" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +#, fuzzy +msgid "Password has been already used." +msgstr "পাসওয়ার্ড পূর্বে ব্যবহৃত হয়েছে। একটি পৃথক পাসওয়ার্ড নির্বাচন করুন।" + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "ডিফল্ট Security Context %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "ভিন্ন role অথবা level লিখতে ইচ্ছুক কি?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "role: " + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "role %s-র জন্য কোনো ডিফল্ট type উপস্থিত নেই\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "level: " + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "বৈধ নিরাপত্তা সংক্রান্ত context নয়" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "Security Context %s ধার্য করা হয়েছে" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "কি নির্মাণের Context %s ধার্য করা হয়েছে" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM আরম্ভ করতে ব্যর্থ\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() করতে ব্যর্থ\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "লগ-ইন: fork করতে ব্যর্থ: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "প্রবেশাধিকার প্রদান করা হয়েছে (%ld পূর্বে সর্বশেষ লগ-ইন করা হয়েছে)।" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"আপনার অ্যাকাউন্টের মেয়াদপূর্ণ হয়েছে; অনুগ্রহ করে সিস্টেম অ্যাডমিনিস্ট্রেটরের সাথে " +"যোগাযোগ করুন।" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "আপনার পাসওয়ার্ড এই মুহূর্তে পরিবর্তন করা আবশ্যক (root দ্বারা কার্যকরী)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "আপনার পাসওয়ার্ড এই মুহূর্তে পরিবর্তন করা আবশ্যক (password-র মেয়াদ পূর্ণ হয়েছে)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "সতর্কবাণী: %d দিন পরে পাসওয়ার্ডের মেয়াদপূর্ণ হবে" +msgstr[1] "সতর্কবাণী: %d দিন পরে পাসওয়ার্ডের মেয়াদপূর্ণ হবে" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "সতর্কবাণী: %d দিন পরে পাসওয়ার্ডের মেয়াদপূর্ণ হবে" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS পাসওয়ার্ড পরিবর্তন করা সম্ভব হয়নি।" + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "কোনো পাসওয়ার্ড উল্লিখিত হয়নি" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS পাসওয়ার্ড পরিবর্তন করা সম্ভব হয়নি।" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "আপনি একটি ছোট পাসওয়ার্ড নির্বাচন করতে হবে।" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "চিহ্নিত পাসওয়ার্ডের থেকে লম্বা পাসওয়ার্ড উল্লেখ করা আবশ্যক" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s-র পাসওয়ার্ড পরিবর্তন করা হচ্ছে।" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "কিছু কাল পরে পাসওয়ার্ড পরিবর্তন করা সম্ভব হবে" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "নতুন মেইল প্রাপ্ত।" + +#~ msgid "is the same as the old one" +#~ msgstr "পুরোনোটির অনুরূপ" + +#~ msgid "memory allocation error" +#~ msgstr "মেমরি বরাদ্দ করতে সমস্যা" + +#~ msgid "is a palindrome" +#~ msgstr "উভমুখী শব্দ" + +#~ msgid "case changes only" +#~ msgstr "শুধুমাত্র হরফের ছাঁদ পরিবর্তন করা হয়েছে" + +#~ msgid "is too similar to the old one" +#~ msgstr "পুরোনো পাসওয়ার্ডের সমতূল্য" + +#~ msgid "is too simple" +#~ msgstr "জটিল নয়" + +#~ msgid "is rotated" +#~ msgstr "ঘোরানো হয়েছে" + +#~ msgid "not enough character classes" +#~ msgstr "পর্যাপ্ত অক্ষর শ্রেণী উপস্থিত নেই" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "একই অক্ষর অত্যাধিক বার ক্রমাগত ব্যবহার করা হয়েছে" + +#, fuzzy +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "একই অক্ষর অত্যাধিক বার ক্রমাগত ব্যবহার করা হয়েছে" + +#~ msgid "contains the user name in some form" +#~ msgstr "কোনো রূপে ব্যবহারকারী নাম অন্তর্ভুক্ত হয়েছে" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "পাসওয়ার্ড ভাল নয়: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "সাময়িকরূপে অ্যাকাউন্ট লক করা হয়েছে (%ld সেকেন্ড অবশিষ্ট)" + +#~ msgid "Authentication error" +#~ msgstr "অনুমোদন সংক্রান্ত সমস্যা" + +#~ msgid "Service error" +#~ msgstr "পরিসেবা সংক্রান্ত সমস্যা" + +#~ msgid "Unknown user" +#~ msgstr "অজানা ব্যবহারকারী" + +#~ msgid "Unknown error" +#~ msgstr "অজানা সমস্যা" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset= এর জন্য ভুল সংখ্যা উল্লিখিত\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: অজানা বিকল্প %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: সব ব্যবহারকারীর জন্য শূণ্য-ভিন্ন মান ধার্য করতে ব্যর্থ\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/bn_IN.po b/po/bn_IN.po new file mode 100644 index 0000000..f3172b0 --- /dev/null +++ b/po/bn_IN.po @@ -0,0 +1,594 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Runa Bhattacharjee , 2009. +# Runa Bhattacharjee , 2007, 2008. +# , 2012. +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2012-02-28 05:38-0500\n" +"Last-Translator: runa \n" +"Language-Team: Bengali (India) \n" +"Language: bn_IN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "পাসওয়ার্ড: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "নতুন %s পাসওয়ার্ড: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "নতুন পাসওয়ার্ড: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "নতুন %s পাসওয়ার্ড: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "নতুন পাসওয়ার্ড: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "নতুন %s পাসওয়ার্ড পুনরায় লিখুন: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "নতুন পাসওয়ার্ড পুনরায় লিখুন: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "দুঃখিত, পাসওয়ার্ড দুটি এক নয়।" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s পুনরায় লিখুন" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "পাসওয়ার্ড পরিবর্তনের কর্ম পরিত্যাগ করা হয়েছে।" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "লগ-ইন:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "সফল" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "গুরুতব সমস্যা - এই মুহূর্তে পরিত্যাগ করা হবে" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "মডিউল লোড করতে ব্যর্থ" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "চিহ্ন পাওয়া যায়নি" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "পরিসেবা মডিউলে সমস্যা" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "সিস্টেম সংক্রান্ত সমস্যা" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "মেমরি বাফার সংক্রান্ত সমস্যা" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "অনুমতি প্রদান করা হয়নি" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "অনুমোদন ব্যর্থ" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "অনুমোদন সংক্রান্ত তথ্য প্রাপ্ত করার জন্য পর্যাপ্ত প্রমাণ উপলব্ধ নেই" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "অনুমোদন পরিসেবা দ্বারা অনুমোদন সংক্রান্ত তথ্য উদ্ধার করা সম্ভব হয়নি" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "পটভূমিতে চলমান অনুমোদন ব্যবস্থায় ব্যবহারকারী পরিচিত নন।" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "পরিসেবায় উপলব্ধ সর্বাধিক প্রচেষ্টার সুযোগ সংখ্যা সমাপ্ত" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "অনুমোদনের টোকেন বৈধ নয়; নতুন টোকেন ব্যবহার করা আবশ্যক" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "ব্যবহারকারী অ্যাকাউন্টের মেয়াদ পূর্ণ হয়েছে" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "সুনির্দিষ্ট সেশানের জন্য কোনো এন্ট্রি নির্মাণ/অপসারণ করা সম্ভব নয়" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "অনুমোদন পরিসেবা দ্বারা প্রয়োজনীয় প্রমাণ উদ্ধার করা সম্ভব হয়নি" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "ব্যবহারকারীর পরিচয়প্রমাণের তথ্যের মেয়াদ পূর্ণ হয়েছে" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "ব্যবহারকারীর পরিচয়প্রমাণের তথ্য নির্ধারণ করতে ব্যর্থ" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "মডিউল সংক্রান্ত কোনো তথ্য উপস্থিত নেই" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item()-এ সঠিক মান প্রেরিত হয়নি" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Conversation অর্থাৎ তথ্য বিনিময়কালীন সমস্যা" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "অনুমোদন টোকেন ব্যবস্থাপনা করতে সমস্যা" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "অনুমোদন সংক্রান্ত তথ্য পুনরুদ্ধার করতে ব্যর্থ" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "অনুমোদন টোকেনের লক ব্যস্ত" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "অনুমোদন টোকেনের মেয়াদ পূর্তী ব্যবস্থা নিষ্ক্রিয়" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "পাসওয়ার্ড পরিসেবা দ্বারা প্রারম্ভিক পরীক্ষা ব্যর্থ" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "উৎপন্ন মান PAM dispatch দ্বারা অগ্রাহ্য করা হবে" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "মডিউল অজানা" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "অনুমোদন টোকেনের মেয়াদ পূর্ণ হয়েছে" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Conversation অর্থাৎ তথ্য বিনিময় প্রক্রিয়া একটি ইভেন্টের অপেক্ষায় রয়েছে" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "অ্যাপ্লিকেশন দ্বারা পুনরায় libpam আরম্ভ করা আবশ্যক" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "PAM সংক্রান্ত অজানা ত্রুটি" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...সময় সমাপ্তির পথে...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...দুঃখিত, সময় সমাপ্ত!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "ত্রুটিপূর্ণ তথ্যবিনিময় (conversation) (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s বিফল: প্রস্থানকালীন কোড %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s বিফল: %d%s সিগনাল প্রাপ্ত" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s বিফল: অজানা অবস্থা 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "লগ-ইন বিফলতা সর্বশেষ বিফলতা চিহ্নিত স্থান থেকে\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u ব্যর্থ লগ-ইনের ফলে অ্যাকাউন্ট লক করা হয়েছে" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s থেকে" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s -র উপর" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "সর্বশেষ লগ-ইন:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "নতুন অ্যাকাউন্টে স্বাগতম!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "সর্বশেষ বিফল লগ-ইন:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "সর্বশেষ সফল লগ-ইনের পরে %d-টি ব্যর্থ লগ-ইনের প্রচেষ্টা করা হয়েছে।" +msgstr[1] "সর্বশেষ সফল লগ-ইনের পরে %d-টি ব্যর্থ লগ-ইনের প্রচেষ্টা করা হয়েছে।" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "সর্বশেষ সফল লগ-ইনের পরে %d-টি ব্যর্থ লগ-ইনের প্রচেষ্টা করা হয়েছে।" + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s'-র ক্ষেত্রে অত্যাধিক লগ-ইন" + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "নতুন মেইল প্রাপ্ত।" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "নতুন মেইল প্রাপ্ত।" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "পুরোনো মেইল রয়েছে।" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "মেইল রয়েছে।" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "%s ফোল্ডারে কোনো মেইল উপস্থিত নেই।" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "%s ফোল্ডারে নতুন মেইল উপস্থিত।" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "%s ফোল্ডারে পুরোনো মেইল উপস্থিত রয়েছে।" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "%s ফোল্ডারে মেইল উপস্থিত রয়েছে।" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "'%s' ডিরেক্টরি নির্মাণ করা হচ্ছে।" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "ডিরেক্টরি '%s' নির্মাণ ও আরম্ভ করতে ব্যর্থ।" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "পাসওয়ার্ড পূর্বে ব্যবহৃত হয়েছে। একটি পৃথক পাসওয়ার্ড নির্বাচন করুন।" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +#, fuzzy +msgid "Password has been already used." +msgstr "পাসওয়ার্ড পূর্বে ব্যবহৃত হয়েছে। একটি পৃথক পাসওয়ার্ড নির্বাচন করুন।" + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "ডিফল্ট Security Context %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "ভিন্ন role অথবা level লিখতে ইচ্ছুক কি?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "role: " + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "role %s-র জন্য কোনো ডিফল্ট type উপস্থিত নেই\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "level: " + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "বৈধ নিরাপত্তা সংক্রান্ত context নয়" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "Security Context %s ধার্য করা হয়েছে" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "কি নির্মাণের Context %s ধার্য করা হয়েছে" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM আরম্ভ করতে ব্যর্থ\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() করতে ব্যর্থ\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "লগ-ইন: fork করতে ব্যর্থ: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "প্রবেশাধিকার প্রদান করা হয়েছে (%ld পূর্বে সর্বশেষ লগ-ইন করা হয়েছে)।" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"আপনার অ্যাকাউন্টের মেয়াদপূর্ণ হয়েছে; অনুগ্রহ করে সিস্টেম অ্যাডমিনিস্ট্রেটরের সাথে " +"যোগাযোগ করুন।" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "আপনার পাসওয়ার্ড এই মুহূর্তে পরিবর্তন করা আবশ্যক (root দ্বারা কার্যকরী)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "আপনার পাসওয়ার্ড এই মুহূর্তে পরিবর্তন করা আবশ্যক (password-র মেয়াদ পূর্ণ হয়েছে)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "সতর্কবাণী: %d দিন পরে পাসওয়ার্ডের মেয়াদপূর্ণ হবে" +msgstr[1] "সতর্কবাণী: %d দিন পরে পাসওয়ার্ডের মেয়াদপূর্ণ হবে" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "সতর্কবাণী: %d দিন পরে পাসওয়ার্ডের মেয়াদপূর্ণ হবে" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS পাসওয়ার্ড পরিবর্তন করা সম্ভব হয়নি।" + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "কোনো পাসওয়ার্ড উল্লিখিত হয়নি" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS পাসওয়ার্ড পরিবর্তন করা সম্ভব হয়নি।" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "আপনি একটি ছোট পাসওয়ার্ড নির্বাচন করতে হবে।" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "চিহ্নিত পাসওয়ার্ডের থেকে লম্বা পাসওয়ার্ড উল্লেখ করা আবশ্যক" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s-র পাসওয়ার্ড পরিবর্তন করা হচ্ছে।" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "কিছু কাল পরে পাসওয়ার্ড পরিবর্তন করা সম্ভব হবে" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "নতুন মেইল প্রাপ্ত।" + +#~ msgid "is the same as the old one" +#~ msgstr "পুরোনোটির অনুরূপ" + +#~ msgid "memory allocation error" +#~ msgstr "মেমরি বরাদ্দ করতে সমস্যা" + +#~ msgid "is a palindrome" +#~ msgstr "উভমুখী শব্দ" + +#~ msgid "case changes only" +#~ msgstr "শুধুমাত্র হরফের ছাঁদ পরিবর্তন করা হয়েছে" + +#~ msgid "is too similar to the old one" +#~ msgstr "পুরোনো পাসওয়ার্ডের সমতূল্য" + +#~ msgid "is too simple" +#~ msgstr "জটিল নয়" + +#~ msgid "is rotated" +#~ msgstr "ঘোরানো হয়েছে" + +#~ msgid "not enough character classes" +#~ msgstr "পর্যাপ্ত অক্ষর শ্রেণী উপস্থিত নেই" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "একই অক্ষর অত্যাধিক বার ক্রমাগত ব্যবহার করা হয়েছে" + +#, fuzzy +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "একই অক্ষর অত্যাধিক বার ক্রমাগত ব্যবহার করা হয়েছে" + +#~ msgid "contains the user name in some form" +#~ msgstr "কোনো রূপে ব্যবহারকারী নাম অন্তর্ভুক্ত হয়েছে" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "পাসওয়ার্ড ভাল নয়: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "সাময়িকরূপে অ্যাকাউন্ট লক করা হয়েছে (%ld সেকেন্ড অবশিষ্ট)" + +#~ msgid "Authentication error" +#~ msgstr "অনুমোদন সংক্রান্ত সমস্যা" + +#~ msgid "Service error" +#~ msgstr "পরিসেবা সংক্রান্ত সমস্যা" + +#~ msgid "Unknown user" +#~ msgstr "অজানা ব্যবহারকারী" + +#~ msgid "Unknown error" +#~ msgstr "অজানা সমস্যা" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset= এর জন্য ভুল সংখ্যা উল্লিখিত\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: অজানা বিকল্প %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: সব ব্যবহারকারীর জন্য শূণ্য-ভিন্ন মান ধার্য করতে ব্যর্থ\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/bs.po b/po/bs.po new file mode 100644 index 0000000..1c098c4 --- /dev/null +++ b/po/bs.po @@ -0,0 +1,504 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Bosnian (http://www.transifex.com/projects/p/fedora/language/" +"bs/)\n" +"Language: bs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Morate odabrati kraću lozinku." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Morate odabrati kraću lozinku." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Morate odabrati kraću lozinku." diff --git a/po/ca.po b/po/ca.po new file mode 100644 index 0000000..7a8e419 --- /dev/null +++ b/po/ca.po @@ -0,0 +1,595 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Albert Carabasa Giribet , 2009 +# Bernabé Borrero , 2012 +# Robert Antoni Buj i Gelonch , 2013 +# xqueralt , 2008 +# Tomáš Mráz , 2016. #zanata +# Robert Antoni Buj Gelonch , 2020. +# Dmitry V. Levin , 2020, 2021. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2021-07-22 00:54+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Catalan \n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.7.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Contrasenya: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Contrasenya actual de %s: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Contrasenya actual: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nova contrasenya de %s: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nova contrasenya: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Torneu a escriure la nova contrasenya de %s: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Torneu a escriure la nova contrasenya: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Les contrasenyes no coincideixen." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Torneu a escriure %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "S'ha cancel·lat el canvi de contrasenya." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "inici de sessió:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Correcte" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Error greu - s'avortarà l'operació immediatament" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Ha fallat en carregar el mòdul" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "No es troba el símbol" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Error en el mòdul de servei" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Error del sistema" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Error de la memòria intermèdia" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Permís denegat" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Error d'autenticació" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" +"No teniu suficients credencials per a accedir a les dades d'autenticació" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "El servei d'autenticació no pot recuperar la informació corresponent" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Usuari desconegut per al mòdul d'autenticació subjacent" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "S'ha exhaurit el nombre màxim d'intents per al servei" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "El testimoni d'autenticació ja no és vàlid; se'n necessita un de nou" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "El compte d'usuari ha vençut" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "No es pot crear/suprimir una entrada per a la sessió especificada" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "El servei d'autenticació no pot recuperar les credencials d'usuari" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Les credencials d'usuari han caducat" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "S'ha produït un error en definir les credencials d'usuari" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "No hi han dades específiques del mòdul" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "S'ha transmès un element incorrecte a pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Error de conversa" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Error de manipulació del testimoni d'autenticació" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "No es pot recuperar la informació d'autenticació" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "El bloqueig del testimoni d'autenticació està ocupat" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "L'envelliment del testimoni d'autenticació està inhabilitat" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Error durant la comprovació preliminar del servei de contrasenyes" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "El lliurament de PAM hauria d'ignorar el valor de retorn" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "El mòdul és desconegut" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "El testimoni d'autenticació ha caducat" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "La conversa està esperant un esdeveniment" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "L'aplicació necessita cridar novament libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Error de PAM desconegut" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...S'acaba el temps...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...S'ha acabat el temps.\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "conversa errònia (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s ha fallat: codi de sortida %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s ha fallat: s'ha atrapat el senyal %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s ha fallat: estat 0x%x desconegut" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Forma d’ús: %s: [--dir /directori/path/to/tally] [--user nom_usuari] [--" +"reset]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Entrada Fallades Última fallada Des de\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "El compte està bloquejat a causa de %u inicis fallits de sessió." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(resten %d minut per desbloquejar)" +msgstr[1] "(resten %d minuts per desbloquejar)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(resten %d minuts per desbloquejar)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %-d %b de %Y, %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " des de %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " a %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Últim inici de sessió:%s des de %s a %s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Benvingut al vostre nou compte!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Últim inici de sessió fallit:%s des de %s a %s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"S'han produït %d intents fallits d'inici de sessió des de l'últim inici de " +"sessió reeixit." +msgstr[1] "" +"S'han produït %d intents d'inici de sessió des de l'últim inici de sessió " +"reeixit." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"S'han produït %d intents fallits d'inici de sessió des de l'últim inici de " +"sessió reeixit." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Hi havia massa inicis de sessió per a '%s'." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Teniu correu nou." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Teniu correu nou." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Teniu correu antic." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Teniu correu." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "No teniu cap correu a la carpeta %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Teniu nou correu a la carpeta %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Teniu correu antic a la carpeta %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Teniu correu a la carpeta %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Creant el directori '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "No s'ha pogut crear i inicialitzar el directori '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Aquesta contrasenya ja s'ha fet servir. Trieu-ne una altra." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "La contrasenya ja ha estat utilitzada." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "El context de seguretat predeterminat és %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Voleu introduir un rol o nivell diferent?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rol:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "No hi ha cap tipus predeterminat per al rol %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nivell:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Aquest no és un context de seguretat vàlid." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "No s'ha pogut obtenir un context vàlid per a %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "S'ha assignat el context de seguretat %s." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "S'ha assignat el context de creació de clau %s." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "s'ha produït un error en inicialitzar PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "s'ha produït un error en pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "inici de sessió: ha fallat la bifurcació: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "S'ha concedit l'accés (l'últim accés va ser fa %ld segons)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"El vostre compte ha vençut; contacteu amb l'administrador del vostre sistema." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Heu de canviar la contrasenya immediatament (us hi obliga l'administrador)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" +"Heu de canviar la contrasenya immediatament (la contrasenya ha vençut)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Atenció: la contrasenya venç d'aquí a %d dia." +msgstr[1] "Atenció: la contrasenya venç d'aquí a %d dies." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Atenció: la contrasenya venç d'aquí a %d dies." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "No s'ha pogut canviar la contrasenya NIS." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "No s'ha proporcionat cap contrasenya." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "No s'ha canviat la contrasenya." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Heu de triar una contrasenya més curta." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Heu de triar una contrasenya més llarga." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "S'està canviant la contrasenya de %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Heu d'esperar més temps abans de canviar la contrasenya." + +#~ msgid "You have no mail." +#~ msgstr "No teniu cap correu." + +#~ msgid "is the same as the old one" +#~ msgstr "és la mateixa que l'antiga" + +#~ msgid "memory allocation error" +#~ msgstr "Error d'assignació de memòria" + +#~ msgid "is a palindrome" +#~ msgstr "és un palíndrom" + +#~ msgid "case changes only" +#~ msgstr "només canvien les majúscules i minúscules" + +#~ msgid "is too similar to the old one" +#~ msgstr "és massa semblant a l'antiga" + +#~ msgid "is too simple" +#~ msgstr "és massa senzilla" + +#~ msgid "is rotated" +#~ msgstr "està girada" + +#~ msgid "not enough character classes" +#~ msgstr "no hi ha suficients classes de caràcters" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "conté massa caràcters idèntics consecutius" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "conté una seqüència de caràcters monòtona massa llarga" + +#~ msgid "contains the user name in some form" +#~ msgstr "conté el nom d'usuari d'alguna forma" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "CONTRASENYA INCORRECTA: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "El compte està bloquejat temporalment (queden %ld segons)." + +#~ msgid "Authentication error" +#~ msgstr "Error d'autenticació" + +#~ msgid "Service error" +#~ msgstr "Error del servei" + +#~ msgid "Unknown user" +#~ msgstr "Usuari desconegut" + +#~ msgid "Unknown error" +#~ msgstr "Error desconegut" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: número incorrecte assignat a --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: opció %s no reconeguda\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file nom_fitxer_arrel] [--user nom_usuari] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "" +#~ "%s: no es poden restablir tots els usuaris a un valor diferent de zero\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f nom_fitxer_arrel] [--file nom_fitxer_arrel]\n" +#~ " [-u nom_usuari] [--user nom_usuari]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/cs.po b/po/cs.po new file mode 100644 index 0000000..fcd218e --- /dev/null +++ b/po/cs.po @@ -0,0 +1,586 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Klara Cihlarova , 2005, 2006. +# , 2011. +# Tomas Mraz , 2005, 2008, 2009. +# Tomáš Mráz , 2016. #zanata +# Tomáš Mráz , 2020. +# Dmitry V. Levin , 2020. +# Josef Hruska , 2023. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2023-01-24 18:20+0000\n" +"Last-Translator: Josef Hruska \n" +"Language-Team: Czech \n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Generator: Weblate 4.15.1\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Heslo: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Současné %s heslo: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Současné heslo: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nové %s heslo: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nové heslo: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Opakujte nové %s heslo: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Opakujte nové heslo: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Hesla se neshodují." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Opakujte %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Změna hesla byla přerušena." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "login:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Úspěch" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritická chyba - okamžité ukončení" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Nepodařilo se nahrát modul" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbol nenalezen" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Chyba v modulu služby" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Systémová chyba" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Chyba alokace paměti" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Přístup zamítnut" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Selhání autentizace" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Nedostatečná oprávnění pro přístup k autentizačním datům" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Autentizační služba nemůže získat informace pro autentizaci" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Uživatel není znám použitému autentizačnímu modulu" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Vyčerpán maximální počet pokusů pro službu" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Autentizační token již není platný; vyžadován nový" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Uživatelský účet vypršel" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Pro zadané sezení nelze vytvořit/odstranit záznam" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Autentizační služba nemůže získat údaje o oprávněních uživatele" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Údaje o oprávněních uživatele vypršely" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Chyba při nastavení údajů o oprávněních uživatele" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Nelze najít data potřebná pro modul" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Funkci pam_*_item() byla předána špatná položka" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Chyba konverzace" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Chyba manipulace s autentizačním tokenem" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Autentizační informace nelze získat" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Autentizační token je uzamčen" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Stárnutí autentizačního tokenu vypnuto" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Selhání předběžné kontroly ve službě hesla" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Návratová hodnota by měla být ignorována rozhodovacím mechanismem PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Neznámý modul" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Autentizační token vypršel" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Konverzace čeká na událost" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Aplikace musí znovu zavolat libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Neznámá chyba PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Dochází čas...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Promiňte, čas vypršel!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "nesprávná konverzace (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s selhal: návratový kód %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s selhal: dostal signál %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s selhal: neznámý kód stavu 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Použití: %s [--dir /cesta/k/tally-adresari] [--user uzivatelske_jmeno] [--" +"legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Login Selhání Poslední selhání Od\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Účet je uzamčen z důvodu %u neúspěšných pokusů o přihlášení." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minuta zbývá do odemčení)" +msgstr[1] "(%d minuty zbývají do odemčení)" +msgstr[2] "(%d minut zbývá do odemčení)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minut zbývá do odemčení)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %d.%m.%Y %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " z %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " na %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Poslední přihlášení:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Vítejte na vašem novém účtu!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Poslední neúspěšné přihlášení:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Od posledního úspěšného došlo k %d neúspěšnému pokusu o přihlášení." +msgstr[1] "Od posledního úspěšného došlo k %d neúspěšným pokusům o přihlášení." +msgstr[2] "Od posledního úspěšného došlo k %d neúspěšným pokusům o přihlášení." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Od posledního úspěšného došlo k %d neúspěšným pokusům o přihlášení." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Proběhlo příliš mnoho přihlášení pro '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Nemáte žádnou novou poštu." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Máte novou poštu." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Máte starou poštu." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Máte poštu." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Nemáte žádnou poštu ve složce %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Máte novou poštu ve složce %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Máte starou poštu ve složce %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Máte poštu ve složce %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Vytváření adresáře '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Nezdařilo se vytvořit a inicializovat adresář '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Heslo již bylo použito. Zvolte jiné." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Heslo již bylo použito." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Výchozí bezpečnostní kontext je %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Chcete zadat jinou roli nebo úroveň?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "role:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Chybí výchozí typ pro roli %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "úroveň:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Toto není platný bezpečnostní kontext." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Nepodařilo se získat platný kontext pro %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Bezpečnostní kontext %s byl přiřazen." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Kontext pro vytváření klíčů %s byl přidělen." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "chyba při inicializaci PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "chyba pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: chyba forku: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Přístup byl povolen (poslední přístup před %ld vteřinami)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Váš účet vypršel; kontaktujte prosím svého správce systému." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Nyní musíte změnit své heslo (vynuceno administrátorem)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Nyní musíte změnit své heslo (heslo vypršelo)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Varování: Vaše heslo vyprší za %d den." +msgstr[1] "Varování: Vaše heslo vyprší za %d dny." +msgstr[2] "Varování: Vaše heslo vyprší za %d dní." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Varování: Počet dní do vypršení hesla: %d" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS heslo se nepodařilo změnit." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Heslo nebylo zadáno." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Heslo nebylo změněno." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Musíte zvolit kratší heslo." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Musíte si zvolit delší heslo." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Změna hesla pro %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Na změnu svého hesla musíte počkat déle." + +#~ msgid "You have no mail." +#~ msgstr "Nemáte žádnou poštu." + +#~ msgid "is the same as the old one" +#~ msgstr "je stejné jako předcházející" + +#~ msgid "memory allocation error" +#~ msgstr "chyba alokace paměti" + +#~ msgid "is a palindrome" +#~ msgstr "je palindrom" + +#~ msgid "case changes only" +#~ msgstr "pouze mění velikost" + +#~ msgid "is too similar to the old one" +#~ msgstr "je příliš podobné předcházejícímu" + +#~ msgid "is too simple" +#~ msgstr "je příliš jednoduché" + +#~ msgid "is rotated" +#~ msgstr "je posunuté" + +#~ msgid "not enough character classes" +#~ msgstr "nemá dostatek různých druhů znaků" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "obsahuje příliš mnoho stejných znaků za sebou" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "obsahuje příliš dlouhou postupnou sekvenci znaků" + +#~ msgid "contains the user name in some form" +#~ msgstr "obsahuje v nějaké formě uživatelské jméno" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ŠPATNÉ HESLO: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Účet je dočasně uzamčen (zbývá %ld vteřin)." + +#~ msgid "Authentication error" +#~ msgstr "Chyba autentizace" + +#~ msgid "Service error" +#~ msgstr "Chyba služby" + +#~ msgid "Unknown user" +#~ msgstr "Neznámý uživatel" + +#~ msgid "Unknown error" +#~ msgstr "Neznámá chyba" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Zadána špatná hodnota --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Neznámá volba %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file jmeno_souboru] [--user uzivatelske_jmeno] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Nelze resetovat všechny uživatele nenulově\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f plna-cesta-k-souboru] [--file plna-cesta-k-souboru]\n" +#~ " [-u uzivatelske-jmeno] [--user uzivatelske-jmeno]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/cy.po b/po/cy.po new file mode 100644 index 0000000..e84e812 --- /dev/null +++ b/po/cy.po @@ -0,0 +1,507 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Welsh (http://www.transifex.com/projects/p/fedora/language/" +"cy/)\n" +"Language: cy\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != " +"11) ? 2 : 3;\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Rhaid i chi ddewis cyfrinair byrrach." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Rhaid i chi ddewis cyfrinair byrrach." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Rhaid i chi ddewis cyfrinair byrrach." diff --git a/po/da.po b/po/da.po new file mode 100644 index 0000000..0f88795 --- /dev/null +++ b/po/da.po @@ -0,0 +1,504 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Kris Thomsen , 2009,2012-2013 +# Tomáš Mráz , 2016. #zanata +# scootergrisen , 2020. +# Dmitry V. Levin , 2021. +# Alan Mortensen , 2024. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2024-03-19 18:57+0100\n" +"Last-Translator: Alan Mortensen \n" +"Language-Team: Danish \n" +"Language: da\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Poedit 3.0.1\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Adgangskode: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Nuværende %s adgangskode: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Nuværende adgangskode: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Ny %s adgangskode: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Ny adgangskode: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Genindtast ny %s adgangskode: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Genindtast ny adgangskode: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Adgangskoderne stemmer ikke overens." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Genindtast %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Ændring af adgangskode blev afbrudt." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "login:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Udført" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritisk fejl - afbryder omgående" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Kunne ikke indlæse modul" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbol ikke fundet" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Der opstod en fejl i tjenestemodul" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Systemfejl" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Fejl i hukommelsesbuffer" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Rettigheder nægtet" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Fejl ved godkendelse" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Utilstrækkelige oplysninger for at få adgang til godkendelsesdata" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Godkendelsestjenesten kan ikke hente godkendelsesoplysningerne" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Ukendt bruger for det underliggende godkendelsesmodul" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Antal forsøg på at få adgang til tjenesten er udløbet" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Godkendelses-token er ikke længere gyldig; en ny er påkrævet" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Brugerkontoen er udløbet" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Angivelsen til den angivne session kan ikke dannes/fjernes" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Godkendelsestjenesten kan ikke hente brugeroplysninger" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Brugeroplysningerne er udløbet" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Der opstod en fejl ved angivelse af brugeroplysninger" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Der findes ingen modulspecifikke data" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Ugyldigt objekt blev overført til pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Konversationsfejl" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Fejl ved manipulering af godkendelsestoken" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Godkendelsesoplysningerne kan ikke gendannes" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Lås til godkendelsestoken er optaget" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Udløb af godkendelsestoken er deaktiveret" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Indledende kontrol af adgangskodetjenesten mislykkedes" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Returværdien bør ignoreres af PAM-afsendelse" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modul er ukendt" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Godkendelsestoken er udløbet" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Konversation venter på hændelse" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Programmet skal kalde libpam igen" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Ukendt PAM-fejl" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Tiden er ved at udløbe...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Din tid er desværre gået!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "konversationsfejl (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s fejlede: afslutningskode %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s fejlede: fangede signal %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s fejlede: ukendt status 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Anvendelse: %s [--dir /sti/til/tally-mappe] [--user brugernavn] [--reset] [--" +"legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Login Fejl Seneste fejl Fra\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Konto låst på grund af %u fejlende logins." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minut tilbage hvor der kan låses op)" +msgstr[1] "(%d minutter tilbage hvor der kan låses op)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minutter tilbage hvor der kan låses op)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " fra %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " på %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Sidste login:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Velkommen til din nye konto!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Sidste fejlende login:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Der var %d fejlende loginforsøg siden sidste succesfulde login." +msgstr[1] "Der var %d fejlende loginforsøg siden sidste succesfulde login." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Der var %d fejlende loginforsøg siden sidste succesfulde login." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Der var for mange logins for '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Der er ingen nye mails." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Der er nye mails." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Der er gamle e-mails." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Der er mails." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Der er ingen mails i mappen %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Der er nye mails i mappen %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Der er gamle mails i mappen %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Der er mails i mappen %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Opretter mappe \"%s\"." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Kunne ikke oprette og initialisere mappe \"%s\"." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Adgangskoden er allerede blevet brugt. Vælg en anden." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Adgangskode er allerede i brug." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Standard sikkerhedskonteksten er %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Vil du angive en anden rolle eller niveau?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rolle:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Der er ikke nogen standardtype for rollen %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "niveau:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Der er ikke et gyldigt sikkerhedskontekst." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Der kunne ikke indhentes et gyldigt kontekst for %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Sikkerhedskontekst %s er blevet tildelt." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Nøgleoprettelseskonteksten %s er blevet tildelt." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "kunne ikke initialisere PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() mislykkedes\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: fejl ved forgrening: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Adgang er blevet givet (sidste adgang var for %ld sekunder siden)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Din konto er udløbet; kontakt venligst din systemadministrator." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Du skal ændre din adgangskode omgående (gennemtvunget af administrator)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Du skal ændre din adgangskode omgående (adgangskoden er god gammel)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Advarsel: Din adgangskode udløber om %d dag." +msgstr[1] "Advarsel: Din adgangskode udløber om %d dage." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Advarsel: din adgangskode udløber om %d dage." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS-adgangskoden kunne ikke ændres." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Der er ikke angivet nogen adgangskode." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Adgangskoden er ikke blevet ændret." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Du skal vælge en kortere adgangskode." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Du skal vælge en længere adgangskode." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Ændrer adgangskode for %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Du skal vente længere for at ændre din adgangskode." diff --git a/po/de.po b/po/de.po new file mode 100644 index 0000000..7723555 --- /dev/null +++ b/po/de.po @@ -0,0 +1,588 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Roman Spirgi , 2012 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020, 2021. +# Ettore Atalan , 2022. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-12-06 09:19+0000\n" +"Last-Translator: Ettore Atalan \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.14.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Passwort: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Geben Sie das aktuelle %s Passwort ein: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Geben Sie das aktuelle Passwort ein: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Geben Sie ein neues %s Passwort ein: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Geben Sie ein neues Passwort ein: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Geben Sie das neue %s Passwort erneut ein: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Geben Sie das neue Passwort erneut ein: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Die Passwörter stimmen nicht überein." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Neu eingeben %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Passwort Änderung wurde abgebrochen." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "Login:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Erfolg" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritischer Fehler - sofortiger Abbruch" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Fehler beim Laden des Moduls" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbol nicht gefunden" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Fehler im Service-Modul" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Systemfehler" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Fehler beim Zwischenspeichern" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Berechtigung verweigert" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Fehler bei Authentifizierung" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" +"Berechtigungsnachweis für Zugriff auf Authentifizierungsdaten nicht " +"ausreichend" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"Authentifizierungsdienst kann Authentifizierungsinformationen nicht abrufen" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Benutzer bei zu Grunde liegendem Authentifizierungsmodul nicht bekannt" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Maximale Anzahl an Versuchen für den Dienst erreicht" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Authentifizierungstoken ist nicht mehr gültig; neues erforderlich" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Benutzerkonto ist abgelaufen" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" +"Erstellen/Entfernen eines Eintrags für die angegebene Sitzung nicht möglich" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Authentifizierungsdienst kann keine Benutzerberechtigung abrufen" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Benutzerberechtigung abgelaufen" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Fehler beim Festlegen der Benutzerberechtigung" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Keine modulspezifischen Daten vorhanden" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Ungültiges Element an pam_*_item() übergeben" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Kommunikationsfehler" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Fehler beim Ändern des Authentifizierungstoken" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Authentifizierungsinformationen können nicht wiederhergestellt werden" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Sperre für Authentifizierungstoken belegt" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Zeitablauf für Authentifizierungstoken deaktiviert" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Vorabtest durch Passwortdienst gescheitert" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Der Rückgabewert sollte von PAM-Dispatch ignoriert werden" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modul ist nicht bekannt" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Authentifizierungstoken abgelaufen" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Kommunikation wartet auf Ereignis" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Anwendung muss libpam wieder aufrufen" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Unbekannter PAM-Fehler" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Zeit läuft ab...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Ihre Zeit ist abgelaufen!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "fehlerhafte Kommunikation (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s schlug fehl: Fehlercode %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s schlug fehl: Signal %d%s erhalten" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s schlug fehl: Unbekannter Status 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Aufruf: %s [--dir /path/to/tally-Verzeichnis] [--user Benutzername] [--" +"reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Anmeldung Fehler Letzter Fehler Von\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Das Konto ist wegen %u fehlgeschlagener Anmelde-Versuche gesperrt." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(noch %d Minute zum Entsperren)" +msgstr[1] "(noch %d Minuten zum Entsperren)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(noch %d Minuten zum Entsperren)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %A, den %d. %B %Y, %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " von %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " auf %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Letzte Anmeldung:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Willkommen in Ihrem neuen Konto!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Letzte fehlgeschlagene Anmeldung:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Es gab %d fehlgeschagenen Versuch seit der letzten erfolgreichen Anmeldung." +msgstr[1] "" +"Es gab %d fehlgeschlagene Versuche seit der letzten erfolgreichen Anmeldung." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Es gab %d fehlgeschlagene Versuche seit der letzten erfolgreichen Anmeldung." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Zu viele Anmeldungen für '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Sie haben keine neue E-Mail." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Sie haben neue Nachrichten." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Sie haben alte Nachrichten." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Sie haben Nachrichten." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Sie haben keine Nachrichten in %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Sie haben neue Nachrichten in %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Sie haben alte Nachrichten in %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Sie haben Nachrichten in %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Erstelle Verzeichnis '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Verzeichnis '%s' kann nicht erstellt und initialisiert werden." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Passwort wurde bereits verwendet. Wählen Sie ein anderes aus." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Das gleiche Passwort wurde bereits einmal verwendet." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Standard-Sicherheitskontext %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Wollen Sie eine andere Rolle oder Stufe eingeben?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "Funktion:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Keinen Standard-Typ für Rolle %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "Stufe:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Kein gültiger Sicherheitskontext." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Unfähig einen gültigen Kontext zu erhalten für %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Sicherheitskontext %s zugewiesen." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Schlüssel-Erzeugungskontext %s zugeordnet." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "Fehler beim Initialisieren von PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "Fehler bei pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "Anmeldung: Fehler bei Abspaltung: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Zugriff erlaubt (letzter Zugriff war vor %ld Sekunden)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Ihr Konto ist abgelaufen. Wenden Sie sich an den Systemadministrator." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Sie müssen Ihr Passwort sofort ändern (von root erzwungen)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Sie müssen Ihr Passwort sofort ändern (Passwortablauf)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Warnung: Ihr Passwort läuft in %d Tag ab." +msgstr[1] "Warnung: Ihr Passwort läuft in %d Tagen ab." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Warnung: Ihr Passwort läuft in %d Tagen ab." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Änderung des NIS-Passworts nicht möglich." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Kein Passwort angegeben." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Passwort nicht geändert." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Sie müssen ein kürzeres Passwort wählen." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Sie müssen ein längeres Passwort auswählen." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Ändern des Passworts für %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Sie können Ihr Passwort noch nicht ändern." + +#~ msgid "You have no mail." +#~ msgstr "Sie haben keine Nachrichten." + +#~ msgid "is the same as the old one" +#~ msgstr "ist das gleiche wie das Alte" + +#~ msgid "memory allocation error" +#~ msgstr "Fehler beim Allozieren von Speicher" + +#~ msgid "is a palindrome" +#~ msgstr "ist ein Palindrome" + +#~ msgid "case changes only" +#~ msgstr "nur Änderungen bei der Gross-/Kleinschreibung" + +#~ msgid "is too similar to the old one" +#~ msgstr "ist dem alten zu ähnlich" + +#~ msgid "is too simple" +#~ msgstr "ist zu einfach" + +#~ msgid "is rotated" +#~ msgstr "wurde gedreht" + +#~ msgid "not enough character classes" +#~ msgstr "nicht genug unterschiedliche Arten von Zeichen" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "das gleiche Zeichen wurde so oft hintereinander verwendet" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "Der gesetzte Wert beinhaltet zu lange monotone Zeichenfolgen" + +#~ msgid "contains the user name in some form" +#~ msgstr "enthält den Benutzernamen in irgendeiner Form" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "Schlechtes Passwort: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Konto temporär gesperrt (noch %ld Sekunden)." + +#~ msgid "Authentication error" +#~ msgstr "Authentifizierungsfehler" + +#~ msgid "Service error" +#~ msgstr "Dienstfehler" + +#~ msgid "Unknown user" +#~ msgstr "Unbekannter Benutzer" + +#~ msgid "Unknown error" +#~ msgstr "Unbekannter Fehler" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Ungültige Nummer für --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Nicht erkannte Option: %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file verwurzelter-Dateiname] [--user Benutzername] [--reset[=n]] " +#~ "[--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "" +#~ "%s: Es können nicht alle Benutzer auf Nicht-null zurückgesetzt werden\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/de_CH.po b/po/de_CH.po new file mode 100644 index 0000000..05ddf71 --- /dev/null +++ b/po/de_CH.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: German (Switzerland) (http://www.transifex.com/projects/p/" +"fedora/language/de_CH/)\n" +"Language: de_CH\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Sie müssen ein kürzeres Passwort wählen." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Sie müssen ein kürzeres Passwort wählen." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Sie müssen ein kürzeres Passwort wählen." diff --git a/po/el.po b/po/el.po new file mode 100644 index 0000000..dc095ce --- /dev/null +++ b/po/el.po @@ -0,0 +1,499 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Greek \n" +"Language: el\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Πρέπει να επιλέξετε μικρότερο κωδικό πρόσβασης." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Πρέπει να επιλέξετε μικρότερο κωδικό πρόσβασης." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Πρέπει να επιλέξετε μικρότερο κωδικό πρόσβασης." diff --git a/po/eo.po b/po/eo.po new file mode 100644 index 0000000..8a3919e --- /dev/null +++ b/po/eo.po @@ -0,0 +1,502 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +# Carmen Bianca Bakker , 2021. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2021-02-05 00:40+0000\n" +"Last-Translator: Carmen Bianca Bakker \n" +"Language-Team: Esperanto \n" +"Language: eo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.4.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Pasvorto: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Aktuala %s pasvorto: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Aktuala pasvorto: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nova %s pasvorto: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nova pasvorto: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Retajpu novan %s pasvorton: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Retajpu novan pasvorton: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Pardonon, la pasvortoj ne kongruas." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Retajpu %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Pasvortoŝanĝo ĉesis." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "saluto:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Sukceso" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritika eraro - tuja ĉeso" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Malsukcesis ŝargi modulon" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Ne trovis simbolon" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Sistema eraro" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Permeso rifuzita" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Aŭtentiga malsukceso" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Nesufiĉaj salutiloj por atingi aŭtentigajn datumojn" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Nekonata PAM-eraro" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, fuzzy, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(restas %d minutoj por malŝlosi)" +msgstr[1] "(restas %d minutoj por malŝlosi)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(restas %d minutoj por malŝlosi)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %e-a de %b %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " de %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " sur %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Lasta saluto: %s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Bonvenon al via nova konto!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Lasta malsukcesa saluto: %s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Estis %d malsukcesa salutprovo ekde la lasta sukcesa saluto." +msgstr[1] "Estis %d malsukcesaj salutprovoj ekde la lasta sukcesa saluto." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Estis %d malsukcesaj salutprovoj ekde la lasta sukcesa saluto." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Estis tro multaj salutoj por '%s'." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Vi havas novan poŝton." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Vi havas novan poŝton." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Vi havas malnovan poŝton." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Vi havas poŝton." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Vi havas neniun poŝton en dosierujo %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Vi havas novan poŝton en dosierujo %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Vi havas malnovan poŝton en dosierujo %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Vi havas poŝton en dosierujo %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Kreante dosierujon '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Pasvorto jam uzita. Elektu alian." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Pasvorto jam uzita." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Vi devas elekti pli mallongan pasvorton." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Vi devas elekti pli longan pasvorton." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "" + +#~ msgid "You have no mail." +#~ msgstr "Vi ne havas poŝton." diff --git a/po/es.po b/po/es.po new file mode 100644 index 0000000..f23160e --- /dev/null +++ b/po/es.po @@ -0,0 +1,604 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Andrei Garza , 2012 +# chris.rico , 2013 +# chris.rico , 2013 +# Domingo Becker , 2008 +# Héctor Daniel Cabrera , 2009 +# Manuel Ospina , 2007 +# Máximo Castañeda Riloba , 2016. #zanata +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +# Álvaro Castillo , 2020. +# Emilio Herrera , 2021, 2022. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-10-18 15:19+0000\n" +"Last-Translator: Emilio Herrera \n" +"Language-Team: Spanish \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.14.1\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Contraseña: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Contraseña %s actual: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Contraseña actual: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nueva %s contraseña: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nueva contraseña: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Vuelva a escribir la nueva %s contraseña: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Vuelva a escribir la nueva contraseña: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Las contraseñas no coinciden." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Vuelva a escribir %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Se ha abortado el cambio de contraseña." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "nombre de usuario:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Éxito" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Error crítico: cancelación inmediata" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Error al cargar el módulo" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Símbolo no encontrado" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Error en el módulo de servicios" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Error del sistema" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Error de buffer de memoria" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Permiso denegado" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Fallo de autenticación" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Credenciales insuficientes para acceder a los datos de autenticación" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"El servicio de autenticación no puede recuperar la información de " +"autenticación" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Usuario desconocido para el módulo de autenticación subyacente" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Se ha agotado el número máximo de reintentos para el servicio" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "El testigo de autenticación ya no es válido; necesitará uno nuevo" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "La cuenta del usuario ha caducado" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "No es posible crear o eliminar una entrada de la sesión especificada" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"El servicio de autenticación no puede recuperar las credenciales del usuario" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Las credenciales del usuario han caducado" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Error al definir las credenciales del usuario" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "No hay datos específicos del módulo presentes" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Elemento incorrecto enviado a pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Error de conversación" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Error de manipulación del testigo de autenticación" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "No es posible recuperar la información de autenticación" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "El testigo de autenticación parece ocupado" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Antigüedad del testigo de autenticación inhabilitada" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Error de comprobación preliminar del servicio de contraseña" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "El valor devuelto debe ser omitido por el expedidor PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Módulo desconocido" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Testigo de autenticación caducado" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "La conversación está esperando el evento" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "La aplicación debe llamar a libpam de nuevo" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Error desconocido de PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...El tiempo se está agotando...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Lo sentimos, el tiempo se ha agotado.\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "conversación incorrecta (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s fallido: código de salida %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s fallido: señal capturada %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s fallido: estado desconocido 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Usage: %s [--dir /ruta/al/directorio-de-conteo] [--user nombre-de-usuario] " +"[--reset]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Fallo de Ingresos Ultimo fallo desde\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "La cuenta está bloqueada debido a %u inicios de sesión fallidos." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minuto restante para el desbloqueo)" +msgstr[1] "(%d minutos restantes para el desbloqueo)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minutos restantes para el desbloqueo)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " de %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " en %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Último inicio de sesión:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "¡Bienvenido a su nueva cuenta!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Último inicio de sesión fallido:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Hubo %d intento de logueo fallido desde el último logueo exitosoo." +msgstr[1] "" +"Hubo %d intentos de logueo fallidos desde el último logueo exitoso. " + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Hubo %d intentos de logueo fallidos desde el último logueo exitoso. " + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Hubo demasiados inicios de sesión para \"%s\"." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Tiene correo nuevo." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Tiene correo nuevo." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Tiene correo antiguo." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Tiene correo." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "No tiene correo en la carpeta %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Tiene correo nuevo en la carpeta %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Tiene correo antiguo en la carpeta %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Tiene correo en la carpeta %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Creando directorio '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "No se pudo crear e inicializar el directorio '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "La contraseña ya se ha utilizado. Seleccione otra." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "La contraseña ya se ha utilizado. Seleccione otra." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Contexto de Seguridad Predeterminado %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "¿Desea introducir un nivel o función diferente?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "función:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "No hay tipo por defecto para la función %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nivel:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "No es un contexto de seguridad válido." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "No se pudo obtener un contexto válido para %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Contexto de seguridad %s asignado." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Contexto de creación de clave %s asignado." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "error al iniciar PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "error en pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "inicio de sesión: error en horquilla: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Acceso permitido (el último acceso fué hace %ld segundos)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"La cuenta ha caducado, póngase en contacto con el administrador del sistema" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Debe cambiar la contraseña inmediatamente (obligado por el administrador)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Debe cambiar la contraseña inmediatamente (la contraseña ha caducado)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Advertencia: la contraseña caducará dentro de %d día" +msgstr[1] "Advertencia: la contraseña caducará dentro de %d días" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Advertencia: la contraseña caducará dentro de %d días" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "No es posible cambiar la contraseña NIS." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "No se ha proporcionado ninguna contraseña" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "No es posible cambiar la contraseña NIS." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Debes elegir una contraseña más corta." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Debe elegir una contraseña más larga" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Cambiando la contraseña de %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Debe esperar más tiempo para cambiar la contraseña" + +#~ msgid "You have no mail." +#~ msgstr "No tiene correo nuevo." + +#~ msgid "is the same as the old one" +#~ msgstr "es igual que la antigua" + +#~ msgid "memory allocation error" +#~ msgstr "error de asignación de memoria" + +#~ msgid "is a palindrome" +#~ msgstr "es un palíndromo" + +#~ msgid "case changes only" +#~ msgstr "sólo hay cambios de minúsculas y mayúsculas" + +#~ msgid "is too similar to the old one" +#~ msgstr "es demasiado similar a la antigua" + +#~ msgid "is too simple" +#~ msgstr "es demasiado sencilla" + +#~ msgid "is rotated" +#~ msgstr "es igual pero al revés" + +#~ msgid "not enough character classes" +#~ msgstr "no hay suficientes clases de caracteres" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "contiene demasiados carateres iguales consecutivos" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "contiene demasiados carateres iguales consecutivos" + +#~ msgid "contains the user name in some form" +#~ msgstr "de alguna manera contiene el nombre del usuario" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "CONTRASEÑA INCORRECTA: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "La cuenta está temporalmente bloqueada (%ld segundos restantes)" + +#~ msgid "Authentication error" +#~ msgstr "Error de autenticación" + +#~ msgid "Service error" +#~ msgstr "Error de servicio" + +#~ msgid "Unknown user" +#~ msgstr "Usuario desconocido" + +#~ msgid "Unknown error" +#~ msgstr "Error desconocido" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Número incorrecto proporcionado a --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Opción no reconocida %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file nombre de archivo-raíz] [--user nombre de usuario] [--" +#~ "reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "" +#~ "%s: No es posible restaurar a todos los usuarios a un número distinto de " +#~ "cero\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f nombre-arch-completo] [--file nombre-arch-completo]\n" +#~ " [-u nombre-de-usuario] [--user nombre-de-usuario]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/et.po b/po/et.po new file mode 100644 index 0000000..509a117 --- /dev/null +++ b/po/et.po @@ -0,0 +1,535 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +# Tomáš Mráz , 2016. #zanata +# H A , 2022. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-06-21 23:18+0000\n" +"Last-Translator: H A \n" +"Language-Team: Estonian \n" +"Language: et\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.13\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Parool: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "Uus %s parool: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "Uus parool: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Uus %s parool: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Uus parool: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Kinnita uus %s parool: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Kinnita uus parool: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Kahjuks paroolid ei klapi." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Kirjuta uuesti %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "Paroolivahetus katkestatud." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Edukas" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Mooduli laadimine nurjus" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Sümbolit ei leitud" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Süsteemi viga" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Juurepääs keelatud" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Autentimine nurjus" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Kasutajakonto on aegunud" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Tundmatu moodul" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Tundmatu PAM-i viga" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Aeg hakkab otsa saama...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Kahjuks on su aeg otsas!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "vigane vestlus (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Sul ei ole posti kaustas %s." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Sul ei ole posti kaustas %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Sul on uus post kaustas %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Sul on vanu poste kaustas %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Sul on poste kaustas %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "loon kataloogi '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Parooli on juba kasutatud. Vali uus parool." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +#, fuzzy +msgid "Password has been already used." +msgstr "Parooli on juba kasutatud. Vali uus parool." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "roll:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "tase:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "Su konto on aegunud; palun kontakteeru oma süsteemiadministraatoriga." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Hoiatus: su parool aegub %d päeva pärast" +msgstr[1] "Hoiatus: su parool aegub %d päeva pärast" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Hoiatus: su parool aegub %d päeva pärast" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "Parooli on juba kasutatud. Vali uus parool." + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "Parooli on juba kasutatud. Vali uus parool." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Peate valima lühema parooli." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Sa pead valima pikema parooli" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Sa pead valima pikema parooli" + +#~ msgid "is the same as the old one" +#~ msgstr "on sama kui vana" + +#~ msgid "is a palindrome" +#~ msgstr "on palindroom" + +#~ msgid "is too similar to the old one" +#~ msgstr "on liiga sarnane vanale" + +#~ msgid "is too simple" +#~ msgstr "on liiga lihtne" + +#~ msgid "is rotated" +#~ msgstr "on roteeruv" + +#~ msgid "Authentication error" +#~ msgstr "Autentimise viga" + +#~ msgid "Service error" +#~ msgstr "Teenuse viga" + +#~ msgid "Unknown user" +#~ msgstr "Tundmatu kasutaja" + +#~ msgid "Unknown error" +#~ msgstr "Tundmatu viga" diff --git a/po/eu.po b/po/eu.po new file mode 100644 index 0000000..f65c7c9 --- /dev/null +++ b/po/eu.po @@ -0,0 +1,505 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Asier Iturralde Sarasola , 2012 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2012-12-24 08:54-0500\n" +"Last-Translator: Asier Iturralde Sarasola \n" +"Language-Team: Basque (http://www.transifex.com/projects/p/fedora/language/" +"eu/)\n" +"Language: eu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Pasahitza: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "%s Pasahitz berria: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "%s Pasahitz berria: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "%s Pasahitz berria: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Pasahitz berria: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Idatzi berriz %s pasahitz berria: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Idatzi berriz pasahitz berria: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Idatzi berriz %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Sistemaren errorea" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "maila:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Pasahitz laburragoa aukeratu behar duzu." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Pasahitz laburragoa aukeratu behar duzu." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Pasahitz laburragoa aukeratu behar duzu." + +#~ msgid "Unknown error" +#~ msgstr "Errore ezezaguna" diff --git a/po/fa.po b/po/fa.po new file mode 100644 index 0000000..fcfe635 --- /dev/null +++ b/po/fa.po @@ -0,0 +1,497 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Persian (http://www.transifex.com/projects/p/fedora/language/" +"fa/)\n" +"Language: fa\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "شما باید رمز عبور کوتاهتری انتخاب کنید." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "شما باید رمز عبور کوتاهتری انتخاب کنید." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "شما باید رمز عبور کوتاهتری انتخاب کنید." diff --git a/po/fi.po b/po/fi.po new file mode 100644 index 0000000..0293bee --- /dev/null +++ b/po/fi.po @@ -0,0 +1,592 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Ilkka Pirskanen , 2005 +# Janne Ahlskog , 2006 +# Juhani Numminen , 2013 +# Jyri Palokangas , 2006 +# Ville-Pekka Vainio , 2012 +# Tomáš Mráz , 2016. #zanata +# Jiri Grönroos , 2020. +# Jan Kuparinen , 2021, 2022. +# Ricky Tigg , 2021. +# Dmitry V. Levin , 2021. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-11-15 18:19+0000\n" +"Last-Translator: Jan Kuparinen \n" +"Language-Team: Finnish \n" +"Language: fi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.14.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Salasana: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Nykyinen %s salasana: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Nykyinen salasana: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Uusi %s salasana: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Uusi salasana: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Anna uudelleen uusi %s salasana: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Kirjoita uudelleen uusi salasana: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Salasanat eivät täsmää." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Kirjoita uudelleen %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Salasanan vaihtaminen keskeytettiin." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "sisäänkirjautuminen:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Onnistui" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kriittinen virhe - välitön keskeytys" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Moduulin lataus epäonnistui" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbolia ei löydetty" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Virhe palvelumoduulissa" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Järjestelmävirhe" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Muistipuskurivirhe" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Ei käyttöoikeutta" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Tunnistautumisvirhe" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Riittämätön valtuustieto tunnistautumistietojen saamiseksi" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Tunnistautumispalvelu ei pääse tunnistautumistietoihin" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Käyttäjä tuntematon taustajärjestelmän tunnistautumismoduulille" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Kaikki palvelun uudelleenyritykset käytetty" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Tunnistautumislipuke ei ole enää kelvollinen, tarvitaan uusi" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Käyttäjätili on vanhentunut" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Valitun istunnon merkintää ei voida tehdä/poistaa" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Tunnistautumispalvelu ei pääse käyttäjän valtuustietoihin" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Käyttäjän valtuustiedot vanhentuneet" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Käyttäjän valtuustiedon antaminen ei onnistunut" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Moduuli riippuvaista tietoa ei ole läsnä" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Väärä tieto välitettiin pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Keskusteluvirhe" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Tunnistautumislipukkeen käsittelyvirhe" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Tunnistautumistietoja ei voida palauttaa" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Tunnistautumislipukkeen lukko varattu" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Tunnistautumislipukkeen vanhentuminen otettu pois käytöstä" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Salasanapalvelun alustava tarkistus ei onnistunut" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "PAM-välittäjän tulisi hylätä paluuarvo" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Tuntematon moduuli" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Tunnistautumislipuke vanhentunut" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Keskustelussa odotetaan tapahtumaa" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Sovelluksen tarvitsee kutsua uudelleen libpam:ia" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Tuntematon PAM-virhe" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Aika on loppumassa...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Aikasi on loppunut!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "virheellinen keskustelu (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s epäonnistui: loppukoodi %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s epäonnistui: otettiin kiinni signaali %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s epäonnistui: tuntematon tila 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Käyttö: %s: [--dir /polku/missä/tally-hakemisto] [--user käyttäjätunnus] [--" +"reset][--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Tunnus Epäonnistuneita Viimeisin Koneelta\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Käyttäjätili on lukittu %u epäonnistuneen kirjautumisen vuoksi." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minuutti jäljellä avaamiseen)" +msgstr[1] "(%d minuuttia jäljellä avaamiseen)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minuuttia jäljellä avaamiseen)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " koneelta %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " päätteellä %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Viimeinen kirjautuminen:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Tervetuloa uudella käyttäjätilillä!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Viimeinen epäonnistunut kirjautuminen:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Edellisen onnistuneen kirjautumisen jälkeen kirjautuminen on epäonnistunut " +"%d kerran." +msgstr[1] "" +"Edellisen onnistuneen kirjautumisen jälkeen kirjautuminen on epäonnistunut " +"%d kertaa." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Edellisen onnistuneen kirjautumisen jälkeen kirjautuminen on epäonnistunut " +"%d kertaa." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Liian monta kirjautumista käyttäjälle '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Sinulle ei ole uutta postia." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Sinulle on uutta postia." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Sinulla on vanha posti." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Sinulle on postia." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Sinulla ei ole postia kansiossa %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Sinulla on postia kansiossa %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Sinulla on vanhaa postia kansiossa %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Sinulla on postia kansiossa %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Luodaan hakemisto ”%s”." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Hakemistoa ”%s” ei voida luoda eikä alustaa." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Salasana on jo käytetty. Valitse toinen." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Salasana on jo käytetty." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Oletustietoturvaympäristö on %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Haluatko syöttää eri roolin tai tason?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rooli:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Roolilla %s ei ole oletustyyppiä." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "taso:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Ei kelvollinen tietoturvaympäristö." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Kelvollista sisältöä %s:lle ei pystytty saamaan." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Tietoturvaympäristö %s asetettiin." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Avaimenluontiympäristö %s asetettiin." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM:in valmistelu epäonnistui\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() kutsu epäonnistui\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "sisäänkirjautuminen: virhe haarautumisessa: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Pääsy sallittu (edellinen kirjautuminen %ld sekuntia sitten)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Käyttäjätilisi on vanhentunut; ota yhteyttä järjestelmän ylläpitäjään." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Salasanasi täytyy vaihtaa heti (pääkäyttäjän vaatimus)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Salasanasi täytyy vaihtaa heti (salasana vanhentunut)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Varoitus: salasanasi vanhentuu %d päivän kuluttua." +msgstr[1] "Varoitus: salasanasi vanhentuu %d päivän kuluttua." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Varoitus: salasanasi vanhentuu %d päivän kuluttua." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS-salasanaa ei voitu vaihtaa." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Et antanut salasanaa." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Salasanaa ei vaihdettu." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Sinun on valittava lyhyempi salasana." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Sinun tulee valita pidempi salasana." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Vaihdetaan käyttäjän %s salasana." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Sinun täytyy odottaa kauemmin vaihtaaksesi salasanasi." + +#~ msgid "You have no mail." +#~ msgstr "Sinulle ei ole postia." + +#~ msgid "is the same as the old one" +#~ msgstr "on sama kuin vanha" + +#~ msgid "memory allocation error" +#~ msgstr "muistinvarausvirhe" + +#~ msgid "is a palindrome" +#~ msgstr "on palindromi" + +#~ msgid "case changes only" +#~ msgstr "vain kirjainkoko muutos" + +#~ msgid "is too similar to the old one" +#~ msgstr "on liian samankaltainen vanhan kanssa" + +#~ msgid "is too simple" +#~ msgstr "on liian yksinkertainen" + +#~ msgid "is rotated" +#~ msgstr "on kierrätetty" + +#~ msgid "not enough character classes" +#~ msgstr "liian vähän merkkiluokkia" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "sisältää liian monta samaa merkkiä peräkkäin" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "sisältää liian pitkän yksitoikkoisen merkkijonon" + +#~ msgid "contains the user name in some form" +#~ msgstr "sisältää käyttäjätunnuksen jossakin muodossa" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "HUONO SALASANA: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Käyttäjätili on lukittu tilapäisesti (%ld sekuntia jäljellä)." + +#~ msgid "Authentication error" +#~ msgstr "Tunnistautumisvirhe" + +#~ msgid "Service error" +#~ msgstr "Palveluvirhe" + +#~ msgid "Unknown user" +#~ msgstr "Tuntematon käyttäjä" + +#~ msgid "Unknown error" +#~ msgstr "Tuntematon virhe" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Väärä numero annettu valinnalle --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Tunnistamaton valinta %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file juurrutettu-tiedostonimi] [--user käyttäjätunnus] [--" +#~ "reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Ei voida palauttaa kaikkia käyttäjiä ei-nolliksi\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f juurrutettu-tiedostonimi] [--file juurrutettu-tiedostonimi]\n" +#~ " [-u käyttäjätunnus] [--user käyttäjätunnus]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..9daaaed --- /dev/null +++ b/po/fr.po @@ -0,0 +1,609 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# mrtomlinux , 2008 +# Charles-Antoine Couret , 2009 +# Jérôme Fenal , 2013 +# Kévin Raymond , 2012 +# myriam malga , 2007 +# Martin-Gomez Pablo , 2008 +# Tomáš Mráz , 2016. #zanata +# Julien Humbert , 2020. +# Dmitry V. Levin , 2020. +# Jérôme Fenal , 2021. +# Damien Pou , 2024. +# Léane GRASSER , 2024. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2024-06-27 08:36+0000\n" +"Last-Translator: Léane GRASSER \n" +"Language-Team: French \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.5.5\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Mot de passe : " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Mot de passe %s actuel : " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Mot de passe actuel : " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nouveau %s mot de passe : " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nouveau mot de passe : " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Retapez le nouveau %s mot de passe : " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Retapez le nouveau mot de passe : " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Les mots de passe ne correspondent pas." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Retapez %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Le changement de mot de passe a été annulé." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "login :" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Succès" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Erreur critique - abandon immédiat" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Échec de chargement du module" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbole non trouvé" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Erreur dans le module de service" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Erreur système" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Erreur du tampon mémoire" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Autorisation refusée" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Échec de l'authentification" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" +"Informations d'identification insuffisantes pour accéder aux données " +"d'authentification" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"Le service d'authentification n'a pas pu récupérer les informations " +"d'authentification" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Utilisateur inconnu par le module d'authentification sous-jacent" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Nombre maximum de tentatives épuisées pour le service" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" +"Le jeton d'authentification n'est plus valide ; un nouveau jeton est requis" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Le compte de l'utilisateur a expiré" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Impossible de créer/supprimer une entrée pour la session spécifiée" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"Le service d'authentification n'a pas pu récupérer les informations " +"d'identification de l'utilisateur" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Informations d'identification de l'utilisateur expirées" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" +"Échec lors de la définition des informations d'identification de " +"l'utilisateur" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Aucune donnée spécifique au module présente" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Élément incorrect transmis à pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Erreur de conversation" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Erreur de manipulation du jeton d'authentification" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Impossible de récupérer les informations d'authentification" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Jeton d'authentification occupé" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Péremption du jeton d'authentification désactivée" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Échec du contrôle préliminaire par le service de mot de passe" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "La valeur retournée devrait être ignorée par la répartition PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Module inconnu" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Le jeton d'authentification a expiré" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Conversation en attente d'évènement" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "L'application doit à nouveau appeler libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Erreur PAM inconnue" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Temps bientôt écoulé...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Votre temps est épuisé !\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "conversation erronnée (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s échec : code de sortie %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s échec : signal capté %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s échec : statut 0x inconnu%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Utilisation : %s [--dir /chemin/vers/dossier-tally] [--user nom " +"d'utilisateur] [--reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Connexion Échecs Dernier échec De\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Le compte est temporairement verrouillé dû aux %u connexions échouées." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minute restante pour déverrouiller)" +msgstr[1] "(%d minutes restantes pour déverrouiller)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minutes restantes pour déverrouiller)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %A %e %B %Y à %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " de %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " sur %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Dernière connexion :%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Bienvenue sur votre nouveau compte !" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Dernière connexion échoué : %s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Il y a %d tentative échouée de connexion depuis la dernière connexion " +"réussie." +msgstr[1] "" +"Il y a %d tentatives échouées de connexion depuis la dernière connexion " +"réussie." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Il y a eu %d tentatives de connexion échouées depuis la dernière connexion " +"réussie." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Trop de connexions pour « %s »." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Vous n'avez pas reçu de nouveau courrier." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Vous avez reçu du courrier." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Vous avez du courrier en attente." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Vous avez du courrier." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Vous n'avez aucun e-mail dans le dossier %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Vous avez reçu du courrier dans le dossier %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Vous avez du courrier en attente dans le dossier %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Vous avez du courrier dans le dossier %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Création du répertoire « %s »." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Impossible de créer et d'initialiser le répertoire « %s »." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Mot de passe déjà utilisé. Choisissez-en un autre." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Mot de passe déjà utilisé." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Le contexte de sécurité par défaut est %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Voulez-vous entrer un niveau ou un rôle différent ?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rôle :" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Il n'y a aucun type par défaut pour le rôle %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "niveau :" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Ceci n'est pas un contexte de sécurité valide." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Impossible d'obtenir un contexte valide pour %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Le contexte de sécurité %s a été attribué." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Le contexte de création de clés %s a été attribué." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "échec de l'initialisation de PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "échec de pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login : échec du clonage : %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "L'accès a été accordé (dernier accès il y a %ld secondes)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"Votre compte a expiré ; veuillez contacter votre administrateur système." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Vous devez changer votre mot de passe immédiatement (imposé par " +"l'administrateur)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" +"Vous devez changer votre mot de passe immédiatement (mot de passe expiré)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Attention : votre mot de passe expirera dans %d jour." +msgstr[1] "Attention : votre mot de passe expirera dans %d jours." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Attention : votre mot de passe expirera dans %d jours." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Le mot de passe NIS n'a pas pu être changé." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Aucun mot de passe n'a été fourni." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Le mot de passe n'a pas été modifié." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Vous devez choisir un mot de passe plus court." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Vous devez choisir un mot de passe plus long." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Changement du mot de passe pour %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "" +"Vous devez attendre plus longtemps afin de pouvoir changer votre mot de " +"passe." + +#~ msgid "You have no mail." +#~ msgstr "Vous n’avez pas de message." + +#~ msgid "is the same as the old one" +#~ msgstr "est identique à l’ancien" + +#~ msgid "memory allocation error" +#~ msgstr "erreur d’allocation de mémoire" + +#~ msgid "is a palindrome" +#~ msgstr "est un palindrome" + +#~ msgid "case changes only" +#~ msgstr "changement de casse uniquement" + +#~ msgid "is too similar to the old one" +#~ msgstr "ressemble trop à l’ancien" + +#~ msgid "is too simple" +#~ msgstr "est trop simple" + +#~ msgid "is rotated" +#~ msgstr "est inversé" + +#~ msgid "not enough character classes" +#~ msgstr "les caractères utilisés ne sont pas suffisamment variés" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "contient trop de caractères consécutifs identiques" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "contient trop de caractères consécutifs identiques" + +#~ msgid "contains the user name in some form" +#~ msgstr "contient le nom d’utilisateur d’une certaine manière" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "MOT DE PASSE INCORRECT : %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Le compte est temporairement verrouillé (%ld secondes restantes)." + +#~ msgid "Authentication error" +#~ msgstr "Erreur d’authentification" + +#~ msgid "Service error" +#~ msgstr "Erreur de service" + +#~ msgid "Unknown user" +#~ msgstr "Utilisateur inconnu" + +#~ msgid "Unknown error" +#~ msgstr "Erreur inconnue" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s : Numéro incorrect attribué à --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s : Option non reconnue %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s : [--file chemin du fichier] [--user nom d’utilisateur] [--reset[=n]] " +#~ "[--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s : Impossible de réinitialiser tous les utilisateurs à non-zéro\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s : [-f chemin du fichier] [--file chemin du fichier]\n" +#~ " [-u nom d’utilisateur] [--user nom d’utilisateur]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ga.po b/po/ga.po new file mode 100644 index 0000000..e516934 --- /dev/null +++ b/po/ga.po @@ -0,0 +1,603 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# leftmostcat , 2012 +# leftmostcat , 2012-2013 +# Tomáš Mráz , 2016. #zanata +# Aindriu Mac Giolla Eoin , 2024. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2024-08-08 15:41+0000\n" +"Last-Translator: Aindriu Mac Giolla Eoin \n" +"Language-Team: Irish \n" +"Language: ga\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : " +"4);\n" +"X-Generator: Weblate 5.6.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Focal faire: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Focal faire reatha %s: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Pasfhocal reatha: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Focal faire %s nua: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Focal faire nua: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Iontráil focal faire %s nua arís: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Iontráil focal faire nua arís: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Tá brón orm, ní ionann na focail fhaire." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Iontráil %s arís" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Cuireadh deireadh le hathrú pasfhocail." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "logáil isteach:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "D'éirigh leis" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Earráid chriticiúil - tobscor láithreach" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Theip ar luchtú modúil" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Siombail gan aimsiú" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Earráid i modúl seirbhíse" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Earráid chórais" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Earráid mhaolán cuimhne" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Diúltaíodh cead" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Teip fíordheimhnithe" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Níl cead agat sonraí fhíordheimhnithe a rochtain" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"Ní féidir leis an tseirbhís fhíordheimhnithe faisnéis fhíordheimhnithe a " +"fháil" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Níl aithne ar an úsáideoir ag an modúl fíordheimhnithe bunúsach" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Líon uasta na hatrialacha don tseirbhís ídithe" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" +"Níl an ceadchomhartha fíordheimhnithe bailí a thuilleadh; ceann nua de dhíth" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Chuaigh an cuntas úsáideora as feidhm" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Ní féidir iontráil don tseirbhís sonraithe a dhéanamh/bhaint" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"Ní féidir leis an tseirbhís fhíordheimhnithe dintiúir úsáideora a fháil" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Chuaigh dintiúir úsáideora as feidhm" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Theip ar shocrú dintiúir úsáideora" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Níl aon sonraí shainiúil don mhodúl ann" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Tugadh drochmhír do pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Earráid chomhrá" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Earráid ionramháil an cheadchomhartha fíordheimhnithe" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Ní féidir faisnéis fhíordheimhnithe a fháil ar ais" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Tá glas an cheadchomhartha fíordheimhnithe gnóthach" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Díchumasaíodh dul in aois cheadchomharthaí fíordheimhnithe" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Theip ar réamhsheiceáil ag an tseirbhís fhocail faire" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Ba chóir don seoladh PAM neamhaird a dhéanamh ar an luach fillte" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Tá an modúl anaithnid" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Chuaigh an ceadchomhartha fíordheimhnithe as feidhm" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Tá an comhrá ag fanacht le teagmhas" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Ní mór don fheidhmchlár libpam a ghlaoch arís" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Earráid PAM anaithnid" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Tá am ag imeacht...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Tá brón orm, tá do chuid ama imithe!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "comhrá earráideach (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "Theip %s: cód scortha %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "Theip %s: fuarthas comhartha %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "Theip %s: stádas anaithnid 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Úsáid: %s [--dir /path/to/tally-directory] [--ainm úsáideora] [--athshocrú] " +"[--legacy-aschur]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Logála Teipeanna An teip is déanaí Ó\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Tá an cuntas glasáilte mar gheall ar theip %u ar logáil isteach." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d nóiméad fágtha le díghlasáil)" +msgstr[1] "(%d nóiméid fágtha le díghlasáil)" +msgstr[2] "(%d nóiméid fágtha le díghlasáil)" +msgstr[3] "(%d nóiméid fágtha le díghlasáil)" +msgstr[4] "(%d nóiméid fágtha le díghlasáil)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d nóiméad fágtha le díghlasáil)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %e %b %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " ó %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " ar %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Logáil isteach is déanaí:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Fáilte go dtí do chuntas nua!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Logáil isteach teipthe is déanaí:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Theip ar %d iarracht logáil isteach ón logáil isteach dheireanach." +msgstr[1] "" +"Theip ar %d iarrachtaí logáil isteach ón logáil isteach dheireanach." +msgstr[2] "" +"Theip ar %d iarrachtaí logáil isteach ón logáil isteach dheireanach." +msgstr[3] "" +"Theip ar %d iarrachtaí logáil isteach ón logáil isteach dheireanach." +msgstr[4] "" +"Theip ar %d iarrachtaí logáil isteach ón logáil isteach dheireanach." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Theip ar %d iarracht logáil isteach ón logáil isteach dheireanach." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Bhí an iomarca logáil isteach le haghaidh '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Níl aon ríomhphost nua agat." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Tá post nua agat." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Tá seanphost agat." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Tá post agat." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Níl aon phost agat i bhfillteán %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Tá post nua agat i bhfillteán %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Tá seanphost agat i bhfillteán %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Tá post agat i bhfillteán %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Comhadlann '%s' á cruthú." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Ní féidir comhadlann '%s' a chruthú agus a thúsú." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Úsáidtear an focal faire cheana. Roghnaigh ceann eile." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Úsáidtear an focal faire cheana." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Is é %s an comhthéacs slándála réamhshocraithe." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "An bhfuil fonn ort ról nó leibhéal difriúil a iontráil?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ról:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Níl aon chineál réamhshocraithe le haghaidh ról %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "leibhéal:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Ní comhthéacs bailí slándála é seo." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Níorbh fhéidir comhthéacs bailí do %s a fháil." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Comhthéacs slándála %s sannta." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Tá comhthéacs cruthú eochrach %s sannta." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "theip ar thúsú PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "theip ar pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "logáil isteach: theip ar dhéanamh foirc: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Tugadh rochtain (bhí an rochtain dheireanach %ld soicind ó shin)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Tá do chuntas imithe in éag; déan teagmháil le do riarthóir córais." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Iarrtar ort do phasfhocal a athrú láithreach (curtha i bhfeidhm ag an " +"riarthóir)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" +"Iarrtar ort do phasfhocal a athrú láithreach (an pasfhocal imithe in éag)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Rabhadh: rachaidh do phasfhocal in éag i gceann %d lá." +msgstr[1] "Rabhadh: rachaidh d'fhocal faire as feidhm i gceann %d laethanta." +msgstr[2] "Rabhadh: rachaidh d'fhocal faire as feidhm i gceann %d laethanta." +msgstr[3] "Rabhadh: rachaidh d'fhocal faire as feidhm i gceann %d laethanta." +msgstr[4] "Rabhadh: rachaidh d'fhocal faire as feidhm i gceann %d laethanta." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Rabhadh: rachaidh do phasfhocal in éag i gceann %d lá." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Níorbh fhéidir focal faire NIS a athrú." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Níor soláthraíodh aon phasfhocal." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Níor athraíodh an pasfhocal." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Ní mór duit pasfhocal níos giorra a roghnú." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Caithfidh tú pasfhocal níos faide a roghnú." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Focal faire %s á athrú." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Caithfidh tú fanacht níos faide chun do phasfhocal a athrú." + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "Tá post nua agat." + +#~ msgid "is the same as the old one" +#~ msgstr "tá sé díreach cosúil leis an seancheann" + +#~ msgid "memory allocation error" +#~ msgstr "earráid dála chuimhne" + +#~ msgid "is a palindrome" +#~ msgstr "is palandróm é" + +#~ msgid "case changes only" +#~ msgstr "athruithe cáis amháin" + +#~ msgid "is too similar to the old one" +#~ msgstr "tá sé ró-chosúil leis an seancheann" + +#~ msgid "is too simple" +#~ msgstr "tá sé ró-shimplí" + +#~ msgid "is rotated" +#~ msgstr "tá sé rothlaithe" + +#~ msgid "not enough character classes" +#~ msgstr "níl go leor aicmí carachtar ann" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "tá an iomarca carachtar céanna ann go leantach" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "tá seicheamh carachtar aontonach ró-fhada ann" + +#~ msgid "contains the user name in some form" +#~ msgstr "tá an t-ainm úsáideora ann i bhfoirm éigin" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "DROCHFHOCAL FAIRE: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Cuireadh an cuntas faoi ghlas go sealadach (%ld soicind fágtha)" + +#~ msgid "Authentication error" +#~ msgstr "Earráid fhíordheimhnithe" + +#~ msgid "Service error" +#~ msgstr "Earráid seirbhíse" + +#~ msgid "Unknown user" +#~ msgstr "Úsáideoir anaithnid" + +#~ msgid "Unknown error" +#~ msgstr "Earráid anaithnid" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Tugadh drochuimhir do --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Rogha anaithnid %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file ainm-comhad-le-fréamh] [--user úsáideoir] [--reset[=u]]\n" +#~ "[--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Ní féidir gach úsáideoir a athrú go neamhnialasach\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f ainm-comhad-le-fréamh] [--file ainm-comhad-le-fréamh]\n" +#~ " [-u úsáideoir] [--user úsáideoir]\n" +#~ " [-r] [--reset[=u]] [--quiet]\n" diff --git a/po/gl.po b/po/gl.po new file mode 100644 index 0000000..3ca75b8 --- /dev/null +++ b/po/gl.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Galician (http://www.transifex.com/projects/p/fedora/language/" +"gl/)\n" +"Language: gl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Debes escoller un contrasinal máis curto." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Debes escoller un contrasinal máis curto." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Debes escoller un contrasinal máis curto." diff --git a/po/gu.po b/po/gu.po new file mode 100644 index 0000000..50f7296 --- /dev/null +++ b/po/gu.po @@ -0,0 +1,591 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Ankit Patel , 2007-2008 +# sweta , 2009-2010 +# sweta , 2013 +# sweta , 2012 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2013-04-14 08:16-0400\n" +"Last-Translator: sweta \n" +"Language-Team: Gujarati \n" +"Language: gu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "પાસવર્ડ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "નવો %s પાસવર્ડ: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "નવો પાસવર્ડ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "નવો %s પાસવર્ડ: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "નવો પાસવર્ડ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "નવો %s પાસવર્ડ ફરી લખો: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "નવો પાસવર્ડ ફરી લખો: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "માફ કરજો, પાસવર્ડો બંધબેસતા નથી." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s ને પુન:ટાઇપ કરો" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "પાસવર્ડ બદલાવનો અંત આવેલ છે." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "પ્રવેશ:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "સફળતા" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "જટિલ ભૂલ - તુરંત બંધ કરો" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "મોડ્યુલ લાવવામાં નિષ્ફળ" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "સંજ્ઞા મળી નહિં" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "સેવા મોડ્યુલમાં ભૂલ" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "સિસ્ટમ ભૂલ" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "મેમરી બફર ભૂલ" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "પરવાનગી નામંજૂર" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "સત્તાધિકરણ નિષ્ફળતા" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "સત્તાધિકરણ માહિતી વાપરવા માટે અપૂરતી પરવાનગીઓ" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "સત્તાધિકરણ સેવા સત્તાધિકરણ જાણકારી મેળવી શકે નહિં" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "સત્તાધિકરણ મોડ્યુલથી વપરાશકર્તા અજ્ઞાત છે" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "સેવા માટે મહત્તમ પ્રયાસો કર્યા" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "સત્તાધિકરણ ટોકન લાંબા સમય સુધી માન્ય નથી; એક નવું જરૂરી છે" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "વપરાશકર્તા ખાતું નિવૃત્ત થયું" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "સ્પષ્ટ થયેલ સત્ર માટે પ્રવેશ બનાવી શકતા નથી/દૂર કરી શકતા નથી" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "સત્તાધિકરણ સેવા વપરાશકર્તા પરવાનગીઓ પ્રાપ્ત કરી શકતી નથી" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "વપરાશકર્તા પરવાનગીઓ નિવૃત્ત થઈ" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "વપરાશકર્તા પરવાનગીઓ સુયોજિત કરવામાં નિષ્ફળતા" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "કોઈ મોડ્યુલ લગતી માહિતી હાજર નથી" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item() ને ખરાબ વસ્તુ પસાર થઈ" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "વાર્તાલાપ ભૂલ" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "સત્તાધિકરણ ટોકન મેનીપ્યુલેશન ભૂલ" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "સત્તાધિકરણ જાણકારી પુનઃપ્રાપ્ત કરી શકાશે નહિં" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "સત્તાધિકરણ ટોકન તાળું વ્યસ્ત" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "સત્તાધિકરણ ટોકન વયમર્યાદા નિષ્ક્રિય કરાયેલ" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "પાસવર્ડ સેવા મારફતે પ્રાથમિક ચકાસણી નિષ્ફળ" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "પાછી મળતી કિંમત PAM ડિસ્પેચ દ્વારા અવગણવામાં આવવી જોઈએ" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "મોડ્યુલ અજ્ઞાત છે" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "સત્તાધિકરણ ટોકન નિવૃત્ત થયું" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "વાર્તાલાપ ઘટના માટે રાહ જોઈ રહ્યો છે" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "કાર્યક્રમને libpam ફરીથી બોલાવવાની જરૂર છે" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "અજ્ઞાત PAM ભૂલ" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...સમય ચાલ્યો જઈ રહ્યો છે...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...માફ કરજો, તમારો સમય સમાપ્ત થયો!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "ક્ષતિયુક્ત વાર્તાલાપ (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s નિષ્ફળ: બહાર નીકળ્યાનો કોડ %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s નિષ્ફળ: મળેલ સંકેત %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s નિષ્ફળ: અજ્ઞાત પરિસ્થિતિ 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "પ્રવેશ એ તે માંથી તાજેતરની નિષ્ફળતાને નિષ્ફળ કરે છે\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u પ્રવેશો ને નિષ્ફળ કરે છે તે દરમ્યાન ખાતાને તાળુ મારેલ છે" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s તરફથી" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s પર" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "છેલ્લો પ્રવેશ:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "તમારા નવા ખાતામાં તમારું સ્વાગત છે!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "છેલ્લો નિષ્ફળ થયેલ પ્રવેશ:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "છેલ્લે સફળ પ્રવેશ સુધી પ્રવેશનો પ્રયત્ન કરવામાં %d નિષ્ફળ થયેલ હતુ." +msgstr[1] "છેલ્લે સફળ પ્રવેશ સુધી પ્રવેશનો પ્રયત્નો કરવામાં %d નિષ્ફળ થયેલ હતુ." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "છેલ્લે સફળ પ્રવેશ સુધી પ્રવેશનાં પ્રયત્નો કરવામાં %d નિષ્ફળ થયેલ હતુ." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s' માટે ઘણા બધા પ્રવેશો." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "તમારી પાસે નવો મેઈલ છે." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "તમારી પાસે નવો મેઈલ છે." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "તમારી પાસે જૂનો મેઈલ છે." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "તમારી પાસે મેઈલ છે." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "તમારી પાસે ફોલ્ડર %s માં કોઈ મેઈલ નથી." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "તમારી પાસે ફોલ્ડર %s માં નવો મેઈલ છે." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "તમારી પાસે ફોલ્ડર %s માં જૂનો મેઈલ છે." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "તમારી પાસે ફોલ્ડર %s માં મેઈલ છે." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "ડિરેક્ટરી '%s' બનાવી રહ્યા છીએ." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "ડિરેક્ટરી '%s' ને શરૂ કરવામાં અને બનાવવામાં અસમર્થ." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "પાસવર્ડ પહેલાથી જ વપરાઈ ગયેલ છે. બીજો પસંદ કરો." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "પાસવર્ડને વાપરી દેવામાં આવ્યો છે." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "મૂળભૂત સુરક્ષા સંદર્ભ %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "શું તમે અલગ ભૂમિકા કે સ્તર દાખલ કરવા ઈચ્છો છો?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ભૂમિકા:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "ભૂમિકા %s માટે કોઈ મૂળભૂત પ્રકાર નથી\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "સ્તર:" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "માન્ય સુરક્ષા સંદર્ભ નથી" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "સુરક્ષા સંદર્ભ %s સોંપાયેલ" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "કી બનાવટ સંદર્ભ %s સોંપાયેલ" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM નો આરંભ કરવામાં નિષ્ફળ\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() કરવામાં નિષ્ફળ\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "પ્રવેશ: ફોર્કમાં નિષ્ફળ: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "પ્રવેશની મંજૂરી આપેલ છે (છેલ્લો પ્રવેશ એ %ld સેકંડો પહેલા હતો)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "તમારું ખાતું નિવૃત્ત થઈ ગયું છે; મહેરબાની કરીને તમારા સિસ્ટમ સંચાલકનો સંપર્ક કરો" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "તમારે તમારો પાસવર્ડ તુરંત જ બદલવાની જરૂર છે (root દબાણ કરેલ)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "તમારે તમારો પાસવર્ડ તુરંત જ બદલવાની જરૂર છે (પાસવર્ડ વયમર્યાદિત કરાયેલ)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "ચેતવણી: તમારો પાસવર્ડ %d દિવસમાં નિવૃત્ત થઈ જશે" +msgstr[1] "ચેતવણી: તમારો પાસવર્ડ %d દિવસોમાં નિવૃત્ત થઈ જશે" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "ચેતવણી: તમારો પાસવર્ડ %d દિવસોમાં નિવૃત્ત થઈ જશે" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS પાસવર્ડ બદલી શક્યા નહિં." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "કોઈ પાસવર્ડ પૂરો પડાયેલ નથી" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS પાસવર્ડ બદલી શક્યા નહિં." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "તમારે ટૂંકા પાસવર્ડને પસંદ કરવો આવશ્યક છે." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "તમારે લાંબો પાસવર્ડ જ પસંદ કરવો જોઈએ" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s માટે પાસવર્ડ બદલવાનું." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "તમારો પાસવર્ડ બદલવા માટે તમારે લાંબો સમય રાહ જોવી જ પડશે" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "તમારી પાસે નવો મેઈલ છે." + +#~ msgid "is the same as the old one" +#~ msgstr "એ જૂના જેવો જ છે" + +#~ msgid "memory allocation error" +#~ msgstr "મેમરી ફાળવણી ભૂલ" + +#~ msgid "is a palindrome" +#~ msgstr "એ પેલીન્ડ્રોમ છે" + +#~ msgid "case changes only" +#~ msgstr "કેસ ફેરફાર માત્ર" + +#~ msgid "is too similar to the old one" +#~ msgstr "એ જૂના સાથે એકદમ સરખો છે" + +#~ msgid "is too simple" +#~ msgstr "એ ખૂબ સાદો છે" + +#~ msgid "is rotated" +#~ msgstr "એ ફેરવાયેલ છે" + +#~ msgid "not enough character classes" +#~ msgstr "પૂરતા અક્ષર વર્ગો નથી" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "એકપછી એક ઘણા બધા સરખા અક્ષરોને સમાવે છે" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "મૉનોટોનિક અક્ષર ક્રમ ઘણો લાંબો સમાવે છે" + +#~ msgid "contains the user name in some form" +#~ msgstr "અમુક ફોર્મમાં વપરાશકર્તા નામ ને સમાવે છે" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ખરાબ પાસવર્ડ: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "ખાતુ થોડા વખત માટે તાળુ મારેલ છે (%ld સેકંડો ડાબે)" + +#~ msgid "Authentication error" +#~ msgstr "સત્તાધિકરણ ભૂલ" + +#~ msgid "Service error" +#~ msgstr "સેવા ભૂલ" + +#~ msgid "Unknown user" +#~ msgstr "અજ્ઞાત વપરાશકર્તા" + +#~ msgid "Unknown error" +#~ msgstr "અજ્ઞાત ભૂલ" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset= ને ખોટો નંબર અપાયેલ છે\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: નહિં ઓળખાતો વિકલ્પ %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: બધા વપરાશકર્તાઓને બિન-શૂન્યમાં પુનઃસુયોજિત કરી શકતા નથી\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/he.po b/po/he.po new file mode 100644 index 0000000..c4ce312 --- /dev/null +++ b/po/he.po @@ -0,0 +1,575 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +# Yaron Shahrabani , 2020, 2023. +# Dmitry V. Levin , 2021. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2023-02-17 14:20+0000\n" +"Last-Translator: Yaron Shahrabani \n" +"Language-Team: Hebrew \n" +"Language: he\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Weblate 4.15.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "ססמה: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "ססמת %s נוכחית: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "ססמה נוכחית: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "ססמת %s חדשה: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "ססמה חדשה: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "להקליד שוב ססמת %s חדשה: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "נא להקליד ססמה חדשה שוב: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "סליחה, הססמאות לא תואמות." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "להקליד את %s מחדש" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "שינוי ססמה בוטל." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "כניסה:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "הצליח" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "שגיאה קריטית - ביטול מידי" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "טעינת המודול נכשלה" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "הסמל לא נמצא" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "שגיאה במודול השירות" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "שגיאת מערכת" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "שגיאת מכלא זיכרון" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "ההרשאה נדחתה" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "כשל באימות" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "אין מספיק הרשאות כדי לגשת לנתוני האימות" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "שירות האימות לא יכול לקבל את פרטי האימות" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "המשתמש לא מוכר למודול האימות התומך" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "מספר הניסיונות מחדש המרבי נוצל במלואו עבור השירות" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "אסימון האימות אינו תקף עוד, נדרש אחד חדש" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "חשבון משתמש פג" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "לא ניתן ליצור/להסיר רשומה להפעלה שצוינה" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "שירות האימות לא יכול לקבל פרטי גישה של משתמשים" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "תוקף פרטי הגישה של המשתמש פג" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "הגדרת פרטי הגישה של המשתמש נכשלה" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "לא קיימים נתונים מסוימים למודול" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "פריט פסול הועבר אל to pam_*_item()‎" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "שגיאת דיון" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "שגיאת טיפול באסימון אימות" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "לא ניתן לשחזר את פרטי האימות" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "נעילת אסימון האימות עסוקה" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "התיישנות אסימון אימות מושבתת" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "בדיקת הקדם נכשלה על ידי שירות הססמאות" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "הערך החוזר אמור לזכות מהתעלמות על ידי שילוח ה־PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "המודול לא ידוע" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "תוקף אסימון האימות פג" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "הדיון ממתין לאירוע" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "היישום צריך לקרוא ל־libpam שוב" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "שגיאת PAM לא מוכרת" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...הזמן עומד להיגמר...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...סליחה, זמנך עבר!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "דיון שגוי (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s נכשל: קוד היציאה %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s נכשל: נתפס האות %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s נכשל: מצב לא ידוע 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "כניסה כשלים כשל אחרון מאת\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "החשבון ננעל בעקבות %u ניסיונות התחברות שנכשלו." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(נותרה דקה לשחרור)" +msgstr[1] "(נותרו %d דקות לשחרור)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(נותרו %d דקות לשחרור)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " מהמארח %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " על גבי %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "כניסה אחרונה:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "ברוך בואך לחשבונך החדש!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "כניסה כושלת אחרונה:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "היה ניסיון התחברות %d שנכשל מאז ההתחברות האחרונה שהצליחה." +msgstr[1] "היו %d ניסיונות התחברות שנכשלו מאז ההתחברות האחרונה שהצליחה." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "היו %d ניסיונות התחברות שנכשלו מאז ההתחברות האחרונה שהצליחה." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "יותר מדי פעילויות כניסה עבור ‚%s’." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "אין לך דואר." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "יש לך דואר חדש." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "יש לך דואר ישן." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "יש לך דואר." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "אין לך דוא״ל בתיקייה %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "יש לך דוא״ל חדש בתיקייה %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "יש לך דוא״ל ישן בתיקייה %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "יש לך דוא״ל בתיקייה %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "התיקייה ‚%s’ נוצרת." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "לא ניתן ליצור ולאתחל את התיקייה ‚%s’." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "כבר נעשה שימוש בססמה. נא לבחור באחרת." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "כבר נעשה שימוש בססמה הזאת." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "הקשר האבטחה כבררת מחדל הוא %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "מעניין אותך למלא תפקיד או רמה שונים?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "תפקיד:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "אין סוג בררת מחדל לתפקיד %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "רמה:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "זה לא הקשר אבטחה תקני." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "לא ניתן לקבל הקשר תקני עבור %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "הקשר האבטחה %s הוקצה." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "הקשר יצירת המפתח %s הוקצה." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "האתחול של PAM נכשל\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item()‎ נכשל\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "כניסה: פיצול נכשל: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "הוענקה גישה (הכניסה האחרונה הייתה לפני %ld שניות)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "תוקף חשבונך פג, נא ליצור קשר עם הנהלת המערכת שלך." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "עליך לשנות את ססמתך מיידית (דרישת הנהלת המערכת)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "עליך לשנות את ססמתך מיידית (תוקף הססמה פג)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "אזהרה: הססמה שלך תפוג מחר." +msgstr[1] "אזהרה: הססמה שלך תפוג תוך %d ימים." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "אזהרה: הססמה שלך תפוג תוך %d ימים." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "לא היה ניתן לשנות ססמת NIS." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "לא סופקה ססמה." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "הססמה לא הוחלפה." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "עליך לבחור סיסמה קצרה יותר." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "עליך לבחור ססמה ארוכה יותר." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "הססמה עבור %s מוחלפת." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "עליך להמתין זמן רב יותר כדי להחליף את ססמתך." + +#~ msgid "is the same as the old one" +#~ msgstr "זהה לישנה" + +#~ msgid "memory allocation error" +#~ msgstr "שגיאת הקצאת זכרון" + +#~ msgid "is a palindrome" +#~ msgstr "פילנדרום" + +#~ msgid "case changes only" +#~ msgstr "שינויי גודל אותיות בלבד" + +#~ msgid "is too similar to the old one" +#~ msgstr "יותר מדי דומה לישנה" + +#~ msgid "is too simple" +#~ msgstr "פשוטה מדי" + +#~ msgid "is rotated" +#~ msgstr "האם שונה הסדר" + +#~ msgid "not enough character classes" +#~ msgstr "אין מספיק מחלקות תווים" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "יותר מדי תווים זהים בזה אחר זה" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "מכיל רצף תווים מונוטוני ארוך מדי" + +#~ msgid "contains the user name in some form" +#~ msgstr "מכיל את שם המשתמש בצורה כלשהי" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ססמה לא טובה: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "החשבון נעול זמנית (%ld שניות נותרו)." + +#~ msgid "Authentication error" +#~ msgstr "שגיאת אימות" + +#~ msgid "Service error" +#~ msgstr "שגיאת שירות" + +#~ msgid "Unknown user" +#~ msgstr "משתמש לא ידוע" + +#~ msgid "Unknown error" +#~ msgstr "שגיאה לא מוכרת" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: המספר שהועבר ל־‎--reset=‎ שגוי\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: אפשרות לא מוכרת %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: אין אפשרות לאפס את כל המשתמשים למספר שאינו אפס\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/hi.po b/po/hi.po new file mode 100644 index 0000000..9a96e5e --- /dev/null +++ b/po/hi.po @@ -0,0 +1,592 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# kumarvimal , 2013 +# Rajesh Ranjan , 2009 +# Rajesh Ranjan , 2007 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-03-06 23:59+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Hindi \n" +"Language: hi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Weblate 3.11.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "शब्दकूट: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "नया %s password: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "नया password: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "नया %s password: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "नया password: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "नया %s password फिर टाइप करें: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "नया password फिर टाइप करें: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "क्षमा करें, शब्दकूट नहीं मिलते हैं." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "फिर टाइप करें %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "कूटशब्द परिवर्तन छोड़ा गया." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "लॉगिन:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "सफल" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "गंभीर त्रुटि - तत्काल छोड़ें" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "मॉड्यूल लोड करने में विफल" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "संकेत नहीं मिला" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "सेवा मॉड्यूल में त्रुटि" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "सिस्टम त्रुटि" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "स्मृति बफर त्रुटि" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "अनुमति मनाही" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "सत्यापन विफलता" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "सत्यापन आंकड़ा की पहुँच में अपर्याप्त श्रेय" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "सत्यापन सेवा सत्यापन सूचना नहीं हासिल कर सकता है" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "उपयोक्ता अंतर्निहित सत्यापन मॉड्यूल से परिचित नहीं हैं" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "सेवा के लिए अधिकतम संख्या में फिर कोशिश करना समाप्त कर चुका है" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "सत्यापन टोकन अब वैध नहीं है; नया जरूरी है" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "उपयोक्ता खाता की अवधि समाप्त हो चुकी है" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "विशेष सत्र के लिए एक प्रविष्टि नहीं बना/हटा सकता है" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "सत्यापन सेवा उपयोक्ता श्रेय फिर हासिल नहीं कर सकता है" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "उपयोक्ता श्रेय समाप्त" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "विफल जमावट उपयोक्ता श्रेय" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "कोई मॉड्यूल विशेष आंकड़ा मौजूद है" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "खराब मद pam_*_item() में भेजा गया" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "रूपांतरक त्रुटि" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "सत्यापन टोकन मैनिपुलेशन त्रुटि" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "सत्यापन सूचना फिर हासिल नहीं किया जा सकता है" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "सत्यापन टोकन लॉक व्यस्त" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "सत्यापन टोकन एजिंग निष्क्रिय" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "शब्दकूट सेवा के द्वारा प्राथमिक जांच पूरी" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "लौटाए मान को PAM डिस्पैच के द्वारा अनदेखा किया जाना चाहिए" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "मॉड्यूल अनजान है" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "सत्यापन टोकन समाप्त" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "रूपांतरण घटना के प्रतीक्षारत" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "अनुप्रयोग के libpam फिर आह्वान जरूरी" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "अनजान PAM त्रुटि" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...समय समाप्त हो रहा है...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...क्षमा करें, आपका समय समाप्त हो गया!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "अनियमित बातचीत (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s विफल: निकास कोड %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s विफल: संकेत घेरा %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s विफल: अनजान स्थिति 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Login Failures Latest failure From\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "खाता %u विफल लॉगिन के कारण लॉक" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s से" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s पर" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "अंतिम लॉगिन:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "नए खाता में आपका स्वागत है!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "अंतिम लॉगिन विफल:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "%d विफल लॉगिन प्रयास था अंतिम सफल लॉगिन के बाद." +msgstr[1] "%d विफल लॉगिन प्रयास थे अंतिम सफल लॉगिन के बाद." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "%d विफल लॉगिन प्रयास थे अंतिम सफल लॉगिन के बाद." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s' के लिए बहुत लॉगिन." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "आपके लिए नया मेल है." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "आपके लिए नया मेल है." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "आपके पास पुराना मेल है." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "आपके पास मेल है." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "आपके पास %s फोल्डर में कोई मेल नहीं है." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "आपके लिए %s फोल्डर में नया मेल है." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "आपके लिए %s फोल्डर में पुराना मेल है." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "आपके लिए %s फोल्डर में मेल है." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "निर्देशिका '%s' बना रहा है." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "निर्देशिका '%s' बनाने और आरंभ करने में असमर्थ." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "शब्दकूट को पहले ही बदला जा चुका है. दूसरा चुनें." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "शब्दकूट प्रयोग हो चूका है. दूसरा चुनें " + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "तयशुदा सुरक्षा संदर्भ %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "क्या आप भिन्न भूमिका या स्तर दाखिल करना चाहेंगे?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "भूमिका: " + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "भूमिका %s के लिए कोई तयशुदा प्रकार नहीं\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "स्तर: " + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "एक वैध सुरक्षा संदर्भ नहीं" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "सुरक्षा संदर्भ %s नियत" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "कुंजी निर्माण संदर्भ %s नियत" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM को आरंभ करने में विफल\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() में विफल\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "लॉगिन: विफल फोर्किंग: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "पहुँच दिया गया (last access was %ld seconds ago)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "आपका खाता समाप्त हो चुका है; कृपया अपने सिस्टम प्रशासक को संपर्क करें" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "आपके लिए अपना शब्दकूट तत्काल बदलना जरूरी है (रूट पुनर्बलित)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "आपके लिए अपना शब्दकूट तत्काल बदलना जरूरी है (शब्दकूट एज्ड)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "चेतावनी: आपका शब्दकूट %d दिन में समाप्त हो जायेगा" +msgstr[1] "चेतावनी: आपका शब्दकूट %d दिन में समाप्त हो जायेगा" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "चेतावनी: आपका शब्दकूट %d दिनों में समाप्त हो जायेगा" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS शब्दकूट बदला नहीं जा सका." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "कोई कूटशब्द नहीं दिया गया है" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS शब्दकूट बदला नहीं जा सका." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "आपको एक छोटा पासवर्ड चुनना होगा।" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "आपको जरूर एक लंबा शब्दकूट चुनना चाहिए" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s के लिए कूटशब्द बदल रहा है" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "आपको अपना शब्दकूट बदलने के लिए लंबी प्रतीक्षा करनी होगी" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "आपके लिए नया मेल है." + +#~ msgid "is the same as the old one" +#~ msgstr "पुराने की तरह समान है" + +#~ msgid "memory allocation error" +#~ msgstr "स्मृति आबंटन त्रुटि" + +#~ msgid "is a palindrome" +#~ msgstr "एक पालिनड्रोम है" + +#~ msgid "case changes only" +#~ msgstr "स्थिति परिवर्तन सिर्फ" + +#~ msgid "is too similar to the old one" +#~ msgstr "पुराने के बहुत समान है" + +#~ msgid "is too simple" +#~ msgstr "बहुत सरल है" + +#~ msgid "is rotated" +#~ msgstr "घुमाया गया है" + +#~ msgid "not enough character classes" +#~ msgstr "पर्याप्त वर्ण वर्ग नहीं" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "कई समान वर्ण लगातार समाहित करता है" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "समान वर्ण का लम्बा अनुक्रम समाहित करता है " + +#~ msgid "contains the user name in some form" +#~ msgstr "कुछ रूप में उपयोक्ता नाम समाहित करता है" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "खराब शब्दकूट: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "खाता अस्थायी रूप से लॉक (%ld seconds left)" + +#~ msgid "Authentication error" +#~ msgstr "सत्यापन त्रुटि" + +#~ msgid "Service error" +#~ msgstr "सेवा त्रुटि" + +#~ msgid "Unknown user" +#~ msgstr "अनजान उपयोक्ता" + +#~ msgid "Unknown error" +#~ msgstr "अनजान त्रुटि" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: खराब संख्या को --reset= में दिया गया\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: अपरिचित विकल्प %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: सभी उपयोक्ता को गैर शून्य में फिर सेट नहीं कर सकता है\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/hr.po b/po/hr.po new file mode 100644 index 0000000..a11f9ed --- /dev/null +++ b/po/hr.po @@ -0,0 +1,514 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +# Gogo Gogsi , 2021, 2022. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-12-25 09:20+0000\n" +"Last-Translator: Gogo Gogsi \n" +"Language-Team: Croatian \n" +"Language: hr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.15\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Lozinka: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Trenutna %s lozinka: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Trenutna lozinka: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nova %s lozinka: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nova lozinka: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Ponovno upišite novu %s lozinku: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Ponovno upišite novu lozinku: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Nažalost, lozinke se ne podudaraju." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Ponovno upišite %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Promjena lozinke je prekinuta." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "prijava:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Uspješno" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritična greška - trenutan prekid" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Greška pri učitavanju modula" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Simbol nije pronađen" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Greška u modulu usluge" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Greška sustava" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Greška međuspremnika memorije" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Pristup odbijen" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Greška ovjere" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Nedovoljne ovlasti za pristup podacima ovjere" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Usluga ovjere ne može dohvatiti informacije ovjere" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Korisnik nije poznat temeljnom modulu ovjere" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Iskorišten najveći dopušteni broj pokušaja za uslugu" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Token ovjere više nije valjan, potreban je novi" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Korisnički račun je istekao" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Nemoguće stvoriti/ukloniti stavku za odabranu sesiju" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Usluga ovjere ne može dohvatiti korisničke podatke" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Korisnički podaci su istekli" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Greška pri postavljanju korisničkih podataka" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Podaci određeni za module nisu prisutni" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Loša stavka je proslijeđena prema pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Greška razgovora" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Manipulacijska greška tokena ovjere" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Nemoguće je vratiti informacije ovjere" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Zauzeto zaključavanje tokena ovjere" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Zastarijevanje tokena ovjere onemogućeno" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Preliminarna provjera od strane usluge lozinke nije uspjela" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Povratnu vrijednost bi PAM dispačer trebao zanemariti" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modul nije poznat" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Modul ovjere je istekao" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Razgovor čeka za događaj" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Aplikacija treba ponovno pozvati libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Nepoznata PAM greška" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Vrijeme istječe...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Nažalost, vaše je vrijeme isteklo!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "pogrešan razgovor (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s neuspjelo: izlazni kôd %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s neuspjelo: uhvaćen signal %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s neuspjelo: nepoznati status 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Upotreba: %s [--dir /putanja/do/tally-direktorija] [--user korisničko ime] " +"[--reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Prijava Neuspjesi Posljedni neuspjeh Od\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Račun je zaključan zbog %u neuspjelih prijava." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minuta preostala za prijavu)" +msgstr[1] "(%d minute preostale za prijavu)" +msgstr[2] "(%d minuta preostale za prijavu)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minuta preostalo za otključavanje)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " od %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " na %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Posljednja prijava:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Dobrodošli u vaš novi račun!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Posljednja neuspjela prijava:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Nakon posljednje uspješne prijave bio je %d neuspjeli pokušaj prijave." +msgstr[1] "" +"Nakon posljednje uspješne prijave bila su %d neuspjela pokušaja prijave." +msgstr[2] "" +"Nakon posljednje uspješne prijave bilo je %d neuspjelih pokušaja prijave." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Nakon posljednje uspješne prijave, bilo je %d neuspjelih pokušaja prijave." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Previše prijava za '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Nemate novu e-poštu." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Imate novu e-poštu." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Imate staru e-poštu." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Imate e-poštu." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Nemate e-poštu u mapi %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Imate novu e-poštu u mapi %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Imate staru e-poštu u mapi %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Imate e-poštu u mapi %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Stvaranje direktorija '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Stvaranje i pokretanje direktorija '%s' nije uspjelo." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Lozinka je već korištena. Odaberite neku drugu." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Lozinka se već korsiti." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Zadani sigurnosni sadržaj je %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Želite li unijeti drugu namjenu ili razinu?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "namjena:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Nema zadane vrste za namjenu %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "razina:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Ovo nije zadani sigurnosni sadržaj." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Valjani sadržaj za %s ne može se dobiti." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Sigurnosni sadržaj za %s nije dodijeljen." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Sadržaj stvaranja ključa %s je dodijeljen." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "neuspjelo pokretanje PAM-a\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "neuspjelo pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "prijava: greška pri račvanju: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Pristup je dopušten (Posljednja prijava je bila prije %ld sekunde)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Vaš račun je istekao; kontaktirajte svojeg administratora sustava." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Od vas je zatraženo da odmah promijenite svoju lozinku (zahtjev od strane " +"administratora)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" +"Od vas je zatraženo da odmah promijenite svoju lozinku (lozinka je istekla)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Upozorenje: vaša lozinka će isteći za %d dan." +msgstr[1] "Upozorenje: vaša lozinka će isteći za %d dana." +msgstr[2] "Upozorenje: vaša lozinka će isteći za %d dana." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Upozorenje: vaša lozinka će isteći za %d dana." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS lozinku nije moguće promijeniti." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Lozinka nije navedena." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Lozinka nije promijenjena." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Morate odabrati kraću lozinku." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Morate odabrati dužu lozinku." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Promjena lozinke za %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Morate pričekati duže za promjenu lozinke." + +#~ msgid "You have no mail." +#~ msgstr "Nemate e-poštu." diff --git a/po/hu.po b/po/hu.po new file mode 100644 index 0000000..c2ac599 --- /dev/null +++ b/po/hu.po @@ -0,0 +1,596 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Kalman Kemenczy , 2006-2007 +# Keresztes Ákos , 2006 +# Papp Zsolt , 2006 +# Zoltan Hoppár , 2012-2013 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +# Balázs Meskó , 2021. +# Balázs Úr , 2023. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2023-09-27 20:35+0000\n" +"Last-Translator: Balázs Úr \n" +"Language-Team: Hungarian \n" +"Language: hu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.0.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Jelszó: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Jelenlegi %s jelszó: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Jelenlegi jelszó: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Új %s jelszó: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Új jelszó: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Új %s jelszó újra megadása: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Új jelszó újra megadása: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Elnézést, a jelszavak nem egyeznek." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s újra megadása" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "A jelszó megváltoztatása meg lett szakítva." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "bejelentkezés:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Sikeres" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritikus hiba – azonnali megszakítás" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Nem sikerült betölteni a modult" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "A szimbólum nem található" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Hiba a szolgáltatásmodulban" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Rendszerhiba" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Memóriapuffer-hiba" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Engedély megtagadva" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Hitelesítési hiba" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Elégtelen hitelesítési adatok a hitelesítési adatok eléréséhez" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"A hitelesítési szolgáltatás nem tudja lekérni a hitelesítési információkat" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "A felhasználó nem ismert az alapjául szolgáló hitelesítési modulhoz" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Kimerítette a szolgáltatás újrapróbálkozásainak legnagyobb számát" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "A hitelesítési token már nem érvényes, újra van szükség" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "A felhasználói fiók lejárt" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Nem készíthető vagy törölhető bejegyzés az adott munkamenethez" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"A hitelesítési szolgáltatás nem tudja lekérni a felhasználó hitelesítési " +"adatait" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "A felhasználó hitelesítési adatai lejártak" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Hiba a felhasználó hitelesítési adatainak beállításakor" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Nem található modulspecifikus adat" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Hibás elem lett átadva a pam_*_item() számára" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Beszélgetési hiba" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Hitelesítési token módosítási hiba" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "A hitelesítési információk nem állíthatók helyre" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "A hitelesítési token zárolása foglalt" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "A hitelesítési token öregítése letiltva" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Nem sikerült a jelszószolgáltatás előzetes ellenőrzése" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" +"A PAM-kézbesítésnek figyelmen kívül kell hagynia a visszatérési értéket" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "A modul ismeretlen" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "A hitelesítési token lejárt" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "A beszélgetés egy eseményre várakozik" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Az alkalmazásnak újra meg kell hívnia a libpam modult" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Ismeretlen PAM hiba" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "…Fogy az idő…\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "…Elnézést, az idő lejárt!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "hibás beszélgetés (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "a(z) %s sikertelen: kilépési kód: %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "a(z) %s sikertelen: %d%s szignál fogadva" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "a(z) %s sikertelen: ismeretlen állapot: 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Használat: %s [--dir /útvonal/az/összesítő-könyvtárhoz] [--user " +"felhasználónév] [--reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Bejelentkezés Hibák Utolsó hiba Honnan\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "A fiók zárolva van %u sikertelen bejelentkezés miatt." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d perc van hátra a feloldáshoz)" +msgstr[1] "(%d perc van hátra a feloldáshoz)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d perc van hátra a feloldáshoz)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %Y. %b. %-e., %a. %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " innen: %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " ekkor: %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Utolsó bejelentkezés:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Üdvözöljük az új fiókjában!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Utolsó sikertelen bejelentkezés:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"%d sikertelen bejelentkezési kísérlet volt az utolsó sikeres bejelentkezés " +"óta." +msgstr[1] "" +"%d sikertelen bejelentkezési kísérlet volt az utolsó sikeres bejelentkezés " +"óta." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"%d sikertelen bejelentkezési kísérlet volt az utolsó sikeres bejelentkezés " +"óta." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Túl sok bejelentkezés történt „%s” részéről." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Nincs új levele." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Új levele érkezett." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Régebbi levelei vannak." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Levele van." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Nincs levele a(z) %s mappában." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Új levele van a(z) %s mappában." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Régi levelei vannak a(z) %s mappában." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Levelei vannak a(z) %s mappában." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "A(z) „%s” könyvtár létrehozása." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Nem lehet létrehozni és előkészíteni a(z) „%s” könyvtárat." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "A jelszó már használatban van. Válasszon másikat." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "A jelszó már használatban van." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Az alapértelmezett biztonsági környezet a(z) %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Szeretne más szerepet vagy szintet megadni?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "szerep:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Nincs alapértelmezett típus a(z) %s szerephez." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "szint:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Ez nem érvényes biztonsági környezet." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "A(z) %s érvényes környezetét nem sikerült megszerezni." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "A(z) %s biztonsági környezet hozzá lett rendelve." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "A(z) %s kulcslétrehozási környezet hozzá lett rendelve." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "nem sikerült a PAM előkészítése\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "a pam_set_item() sikertelen\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "bejelentkezés: elágaztatási hiba: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Hozzáférés engedélyezve (az utolsó hozzáférés %ld másodperce volt)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "A fiókja lejárt. Vegye fel a kapcsolatot a rendszergazdával." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Azonnal meg kell változtatnia a jelszavát (rendszergazda által kényszerítve)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Azonnal meg kell változtatnia a jelszavát (a jelszó lejárt)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Figyelmeztetés: a jelszava %d nap múlva lejár." +msgstr[1] "Figyelmeztetés: a jelszava %d nap múlva lejár." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Figyelmeztetés: a jelszava %d nap múlva lejár." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "A NIS jelszót nem sikerült megváltoztatni." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Nem lett jelszó megadva." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "A jelszó nem lett megváltoztatva." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Rövidebb jelszót kell választania." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Hosszabb jelszót kell választania." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s jelszavának megváltoztatása." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Tovább kell várnia a jelszava megváltoztatásához." + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "Új levele érkezett." + +#~ msgid "is the same as the old one" +#~ msgstr "ugyanaz, mint a régi" + +#~ msgid "memory allocation error" +#~ msgstr "memória allokációs hiba" + +#~ msgid "is a palindrome" +#~ msgstr "palindrom" + +#~ msgid "case changes only" +#~ msgstr "csak a kis/nagybetűkben változott" + +#~ msgid "is too similar to the old one" +#~ msgstr "túl hasonló a régihez" + +#~ msgid "is too simple" +#~ msgstr "túl egyszerű" + +#~ msgid "is rotated" +#~ msgstr "forgatva" + +#~ msgid "not enough character classes" +#~ msgstr "elégtelen betűcsoport" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "túl sok egymást követő betű egyezik meg" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "túl hosszú monoton karaktersorozatot tartalmaz" + +#~ msgid "contains the user name in some form" +#~ msgstr "valahogy tartalmazza a felhasználó nevét" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ROSSZ JELSZÓ: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Felhasználói azonosító átmenetileg zárolt (még %ld másodpercig)" + +#~ msgid "Authentication error" +#~ msgstr "Hitelesítési hiba" + +#~ msgid "Service error" +#~ msgstr "Szolgáltatás hiba" + +#~ msgid "Unknown user" +#~ msgstr "Ismeretlen felhasználó" + +#~ msgid "Unknown error" +#~ msgstr "Ismeretlen hiba" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Rossz szám a --reset= opcióban\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: %s ismeretlen opció\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-fájlnév] [--user használó] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Nem állítható vissza minden felhasználó nem-nullára\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-fájlnév] [--file rooted-fájlnév]\n" +#~ " [-u használó] [--user használó]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ia.po b/po/ia.po new file mode 100644 index 0000000..7a0f700 --- /dev/null +++ b/po/ia.po @@ -0,0 +1,598 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Nik Kalach , 2012 +# Nik Kalach , 2012 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2012-12-17 05:00-0500\n" +"Last-Translator: Nik Kalach \n" +"Language-Team: Interlingua \n" +"Language: ia\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Contrasigno: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "Nove %s contrasigno: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "Nove contrasigno: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nove %s contrasigno: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nove contrasigno: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Retypa nove %s contrasigno: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Retypa nove contrasigno: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Le contrasignos non es equal." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Retypa %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "Cambiamento de contrasigno abortate." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "conto:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Successo" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Error critic - abandono immediate" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Insuccesso de cargar un modulo" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbolo non trovate" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Error in le modulo de servicio" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Error de systema" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Error de buffer de memoria" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Permission denegate" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Fallimento de authentication" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Credentiales insufficiente pro acceder a datos de authentication" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"Le servicio de authentication non pote recuperar le information de " +"authentication" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Usator es incognite pro le modulo de authentication subjacente" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Il ha superate le numero maxime de tentativas pro le servicio" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" +"Le token de authentication non es jam valide; un nove token es necessari" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Le conto del usator ha perimite" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Impossibile de crear o eliminar un entrata pro le session specificate" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"Le servicio de authentication non pote obtener le credentiales del usator" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Credentiales del usator perimite" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Insuccesso al definition de credentiales de usator" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Nulle datos specific del modulo es presente" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Elemento incorrecte passate a pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Error de conversation" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Error de manipulation del token de authentication" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Impossibile de recuperar le information de authentication" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Le token de authentication pare occupate" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Peremption del token de authentication disactivate" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Insuccesso del controlo preliminar per le servicio de contrasignos" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Le valor restitute debe esser ignorate per le repartitor PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modulo incognite" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Le token de authentication ha perimite" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Conversation in expectation de un evento" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Le application debe appellar a libpam de nove" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Error incognite de PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Le tempore es perimente...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Le tempore ha perimite!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "conversation erronee (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s fallite: codice de exito %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s fallite: signal capturate %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s fallite: stato incognite 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file percurso-integre] [--user usator] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Conto Fallimentos Ultime fallimento De\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Le conto es blocate a causa de %u insuccessos al authentication" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " de %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " via %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Ultime connexion:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Benvenite al nove conto!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Ultime connexion fallite:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Il esseva %d insuccesso de initiar le session desde le ultime connexion." +msgstr[1] "" +"Il esseva %d insuccessos de initiar le session desde le ultime connexion." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Il esseva %d insuccessos a initiar le session desde le ultime connexion." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "Troppo de connexiones pro '%s'." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Il ha nove currero." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Il ha nove currero." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Il ha currero vetule." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Il ha currero." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Il non ha necun curreros in le dossier %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Il ha nove currero in le dossier %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Il ha currero vetule in le dossier %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Il ha currero in le dossier %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Creation del directorio '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Impossibile de crear e de initiar le directorio '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Le contrasigno jam se ha utilisate. Selige un altere." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Le contrasigno se ha jam usate." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "Contexto de securitate predefinite %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Velle tu introducer un rolo e nivello differente?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rolo:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "Necun typo predefinite pro le rolo %s\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nivello:" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "Contexto de securitate incorrecte" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "Contexto de securitate %s attribuite" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "Contexto de creation de clave %s attribuite" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "error al initiar PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "error in pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "apertura de session: insuccesso de autoclonage: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Accesso permittite (le ultime accesso eveniva ante %ld secundas)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "Le conto ha perimite; contacte le administrator del systema" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Le administrator necessita le cambiamento immediate del contrasigno." + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Le contrasigno es troppo vetule e debe esser cambiate immediatemente." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Advertimento: le contrasigno perimera in %d die" +msgstr[1] "Advertimento: le contrasigno perimera in %d dies" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Advertimento: le contrasigno perimera in %d dies" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Le contrasigno NIS non pote esser cambiate." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "Necun contrasigno fornite" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "Le contrasigno NIS non pote esser cambiate." + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Selige un contrasigno plus longe" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Selige un contrasigno plus longe" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Cambiamento del contrasigno pro %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Attende ancora pro cambiar le contrasigno" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "Il ha nove currero." + +#~ msgid "is the same as the old one" +#~ msgstr "nove contrasigno es equl al previe" + +#~ msgid "memory allocation error" +#~ msgstr "error al allocation de memoria" + +#~ msgid "is a palindrome" +#~ msgstr "nove contrasigno es un polindromo" + +#~ msgid "case changes only" +#~ msgstr "nove contrasigno cambia solo le registro de litteras" + +#~ msgid "is too similar to the old one" +#~ msgstr "nove contrasigno es troppo simile al previe" + +#~ msgid "is too simple" +#~ msgstr "nove contrasigno es troppo simple" + +#~ msgid "is rotated" +#~ msgstr "nove contrasigno es un rotation del previe" + +#~ msgid "not enough character classes" +#~ msgstr "nove contrasigno non ha satis de classes de character" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "nove contrasigno contine troppo de characteres identic consequente" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "" +#~ "nove contrasigno contine tro de characteres monotone in un sequentia" + +#~ msgid "contains the user name in some form" +#~ msgstr "nove contrasigno contine le nomine de usator in alicun forma" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "CONTRASIGNO MALFORMATE: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Le conto es blocate temporarimente (%ld secundas remanente)" + +#~ msgid "Authentication error" +#~ msgstr "Error de authentication" + +#~ msgid "Service error" +#~ msgstr "Error de servicio" + +#~ msgid "Unknown user" +#~ msgstr "Usator incognite" + +#~ msgid "Unknown error" +#~ msgstr "Error incognite" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Numero incorrecte fornite a --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Option non recognoscite %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file percurso-integre] [--user usator] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Impossibile de reinitiar tote le usatores a non-zero\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f percurso-integre] [--file percurso-integre]\n" +#~ " [-u usator] [--user usator]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/id.po b/po/id.po new file mode 100644 index 0000000..dc67b38 --- /dev/null +++ b/po/id.po @@ -0,0 +1,509 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# sentabi, 2013 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2013-04-22 01:07-0400\n" +"Last-Translator: sentabi\n" +"Language-Team: Indonesian \n" +"Language: id\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Kata Sandi: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Kata sandi %s saat ini: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Kata sandi saat ini: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Kata sandi %s baru: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Kata sandi baru: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Ketik ulang kata sandi %s baru: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Ketik ulang kata sandi baru: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Maaf, kata sandi yang Anda masukkan tidak sama." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Ketik ulang %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Pergantian kata sandi telah digugurkan." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "log masuk:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Berhasil" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kesalahan kritis - digugurkan seketika" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Gagal memuat modul" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Simbol tidak ditemukan" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Kesalahan di modul layanan" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Kesalahan sistem" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Kesalahan penyangga memori" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Izin ditolak" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Otentikasi gagal" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Tidak cukup kredensial untuk mengakses data otentikasi" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Layanan otentikasi tidak bisa mendapatkan info otentikasi" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Pengguna tidak dikenal oleh modul otentikasi yang mendasari" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Sudah melebihi batas percobaan servis" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Token otentikasi tidak valid lagi, dibutuhkan yang baru" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Akun pengguna sudah berakhir" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Tidak dapat membuat/menghapus entri untuk sesion" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Otentikasi servis tidak mendapat kredential pengguna" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Kredential pengguna telah berakhir" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Gagal dalam mengkonfigurasi izin pengguna" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Tidak ada modul yang ditampilkan" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Kesalahan item diteruskan ke pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Kesalahan percakapan" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Kesalahan manipulasi token otentikasi" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Informasi otentikasi tidak dapat dikembalikan" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Kunci otentikasi sedang sibuk" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Batas token otentikasi tidak aktif" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Gagal pemeriksaan awal oleh layanan kata sandi" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Nilai balikan mesti diabaikan oleh dispatch PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modul tak dikenal" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Token otentikasi kedaluwarsa" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Percakapan sedang menunggu kejadian" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Aplikasi perlu memanggil libpam lagi" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Kesalahan PAM yang tak dikenal" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Waktu hampir habis...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Maaf, waktu Anda habis!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "percakapan yang ada kesalahan (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s gagal: kode keluar %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s gagal: menangkap sinyal %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s gagal: status tak dikenal 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Cara pakai: %s [--dir /path/ke/direktori-tally] [--user nama_pengguna] [--" +"reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Akun dikunci karena %u log masuk yang gagal." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d menit tersisa untuk membuka kunci)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d menit tersisa untuk membuka kunci)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %e %b %Y %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " dari %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " pada %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Log masuk terakhir:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Selamat datang ke akun baru Anda!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Log masuk gagal terakhir:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Ada %d upaya log masuk yang gagal sejak log masuk sukses yang terakhir." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Ada %d upaya log masuk yang gagal sejak log masuk sukses terakhir." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Ada terlalu banyak log masuk bagi '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Anda tidak punya surat baru." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Anda menerima surel baru." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Anda memiliki surel lama." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Anda memiliki surel." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Anda tidak memiliki surel di folder %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Anda memiliki surel baru di folder %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Anda memiliki email lama di folder %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Anda memiliki surel di folder %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Membuat direktori '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Tidak dapat membuat direktori '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Sandi sudah digunakan sebelumnya. Pilih sandi yang lain." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Sandi sudah digunakan." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Konteks keamanan baku adalah %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Apakah anda mau menambah izin atau level yang berbeda?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "Sebagai:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Tidak ada tipe baku bagi peran %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "Level:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Ini bukan konteks keamanan yang benar." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Konteks yang valid bagi %s tidak bisa diperoleh." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Konteks keamanan %s telah ditugaskan." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Konteks pembuatan kunci %s telah ditugaskan." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "gagal menginisialisasi PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "gagal untuk pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "log masuk: kegagalan fork: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "Sandi sudah digunakan." + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "Sandi sudah digunakan." + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Anda harus memilih kata sandi yang lebih pendek." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Anda harus memilih kata sandi yang lebih pendek." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Mengubah kata sandi untuk %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Anda harus memilih kata sandi yang lebih pendek." + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "Anda menerima surel baru." + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "terlalu panjang karakter berurutan" diff --git a/po/is.po b/po/is.po new file mode 100644 index 0000000..78a83e1 --- /dev/null +++ b/po/is.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Icelandic (http://www.transifex.com/projects/p/fedora/" +"language/is/)\n" +"Language: is\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Þú verður að velja styttri lykilorð." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Þú verður að velja styttri lykilorð." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Þú verður að velja styttri lykilorð." diff --git a/po/it.po b/po/it.po new file mode 100644 index 0000000..e29c624 --- /dev/null +++ b/po/it.po @@ -0,0 +1,593 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# fvalen , 2013 +# fvalen , 2012 +# Luca Bruno , 2007 +# mario_santagiuliana , 2009 +# Novell Language , 2007 +# Tomáš Mráz , 2016. #zanata +# Milo Casagrande , 2020. +# Dmitry V. Levin , 2020, 2021. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2021-07-22 00:54+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Italian \n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.7.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Password: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Password attuale %s: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Password attuale: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nuova password %s: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nuova password: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Reimmettere la nuova password %s: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Reimmettere la nuova password: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Le password non corrispondono." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Reimmettere %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Modifica della password terminata." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "login:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Successo" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Errore critico - interruzione immediata" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Caricamento del modulo non riuscito" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Simbolo non trovato" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Errore nel modulo di servizio" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Errore di sistema" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Errore buffer memoria" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Permesso negato" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Autenticazione non riuscita" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Credenziali insufficienti per accedere ai dati di autenticazione" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"Il servizio di autenticazione non è in grado di recuperare le informazioni " +"di autenticazione" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Utente sconosciuto al modulo di autenticazione sottostante" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Superato il numero massimo di tentativi per il servizio" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Token di autenticazione non più valido; ne è richiesto uno nuovo" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Account utente scaduto" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Impossibile creare/rimuovere una voce per la sessione specificata" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"Il servizio di autenticazione non è in grado di recuperare le credenziali " +"utente" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Credenziali utente scadute" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Impostazione delle credenziali utente non riuscita" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Dati specifici del modulo non presenti" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Elemento errato passato a pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Errore di conversazione" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Errore manipolazione token di autenticazione" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Impossibile ripristinare informazioni di autenticazione" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Blocco token di autenticazione occupato" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Invecchiamento del token di autenticazione disabilitato" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Controllo preliminare del servizio password non riuscito" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Il valore restituito dovrebbe essere ignorato dal dispatch PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modulo sconosciuto" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Token di autenticazione scaduto" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Conversazione in attesa di un evento" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "L'applicazione richiede una nuova chiamata a libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Errore PAM sconosciuto" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Il tempo sta per scadere...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Tempo scaduto!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "conversazione errata (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s non riuscita: codice d'uscita %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s non riuscita: intercettato il segnale %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s non riuscita: stato sconosciuto 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Uso: %s [--dir /path/to/tally-directory] [--user nomeutente] [--reset]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Accesso Errori Ultimi errori Da\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Account bloccato a causa di %u accessi non riusciti." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minuto rimanenti per sbloccare)" +msgstr[1] "(%d minuti rimanenti per sbloccare)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minuti rimanenti per sbloccare)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %e %b %Y %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " da %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " su %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Ultimo accesso:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Benvenuti nel nuovo account!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Ultimo accesso non riuscito:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Dall'ultimo accesso si è verificato %d tentativo non riuscito di accesso." +msgstr[1] "" +"Dall'ultimo accesso si sono verificati %d tentativi non riusciti di accesso." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Dall'ultimo accesso si sono verificati %d tentativi non riusciti di accesso." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Sono stati effettuati troppi accessi per «%s»." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Ci sono nuove email." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Ci sono nuove email." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Ci sono email vecchie." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Ci sono email." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "La cartella %s non contiene alcuna email." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "La cartella %s contiene nuove email." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "La cartella %s contiene vecchie email." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "La cartella %s contiene email." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Creazione della directory «%s»." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Impossibile creare e inizializzare la directory «%s»." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Password già utilizzata, sceglierne un'altra." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "La password è stata già utilizzata." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Il contesto di sicurezza predefinito è %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Immettere un ruolo o livello differente?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ruolo:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Nessun tipo predefinito per il ruolo %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "livello:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Non è un contesto di sicurezza valido." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Impossibile ottenere un contesto valido per %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Contesto di sicurezza %s assegnato." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Contesto di creazione chiave %s assegnato." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "impossibile inizializzare PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "Impossibile eseguire pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: fork non riuscita: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Accesso permesso (l'ultimo accesso risale a %ld secondi fa)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Account scaduto; contattare l'amministratore di sistema." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"È richiesta la modifica immediata della password (imposto " +"dall'amministratore)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "È richiesta la modifica immediata della password (password scaduta)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Avviso: la password scadrà tra %d giorno." +msgstr[1] "Avviso: la password scadrà tra %d giorni." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Avviso: la password scadrà tra %d giorni." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Impossibile modificare la password NIS." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Nessuna password fornita." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "La password non è stata modificata." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Scegliere una password più corta." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Scegliere una password più lunga." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Cambio password per %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Attendere ancora per cambiare la password." + +#~ msgid "You have no mail." +#~ msgstr "Non ci sono email." + +#~ msgid "is the same as the old one" +#~ msgstr "è la stessa di quella precedente" + +#~ msgid "memory allocation error" +#~ msgstr "errore allocazione memoria" + +#~ msgid "is a palindrome" +#~ msgstr "è un palindromo" + +#~ msgid "case changes only" +#~ msgstr "cambiano solo le maiuscole/minuscole" + +#~ msgid "is too similar to the old one" +#~ msgstr "è troppo simile alla precedente" + +#~ msgid "is too simple" +#~ msgstr "è troppo semplice" + +#~ msgid "is rotated" +#~ msgstr "è una rotazione della precedente" + +#~ msgid "not enough character classes" +#~ msgstr "non ha abbastanza classi di caratteri" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "contiene troppi caratteri simili consecutivi" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "contiene una sequenza troppo lunga di caratteri simili" + +#~ msgid "contains the user name in some form" +#~ msgstr "contiene il nome utente in alcune forme" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "Password scadente: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Account momentaneamente bloccato (%ld secondi rimanenti)." + +#~ msgid "Authentication error" +#~ msgstr "Errore di autenticazione" + +#~ msgid "Service error" +#~ msgstr "Errore del servizio" + +#~ msgid "Unknown user" +#~ msgstr "Utente sconosciuto" + +#~ msgid "Unknown error" +#~ msgstr "Errore sconosciuto" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Numero errato fornito a --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: opzione non riconosciuta %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user nomeutente] [--reset[=N]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "" +#~ "%s: impossibile ripristinare tutti gli utenti a valori diversi da zero\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u nomeutente] [--user nomeutente]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ja.po b/po/ja.po new file mode 100644 index 0000000..81f2de6 --- /dev/null +++ b/po/ja.po @@ -0,0 +1,581 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Jiro Matsuzawa , 2013 +# Kiyoto Hashida , 2008-2009 +# Noriko Mizumoto , 2007 +# Tomoyuki KATO , 2012 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +# Tomohiro KATO , 2021, 2024. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2024-09-04 03:38+0000\n" +"Last-Translator: Tomohiro KATO \n" +"Language-Team: Japanese \n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.7.1\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "パスワード: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "現在の%sパスワード: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "現在のパスワード: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "新しい%sパスワード: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "新しいパスワード: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "新しい%sパスワードを再入力してください: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "新しいパスワードを再入力してください: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "パスワードが一致しません。" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s を再入力して下さい" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "パスワード変更を中断しました。" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "ログイン:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "成功" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "致命的エラー - 即時中止" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "モジュールのロードに失敗しました" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "シンボルが見つかりません" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "サービスモジュールのエラー" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "システムエラー" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "メモリーバッファエラー" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "許可がありません" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "認証失敗" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "認証データにアクセスするには資格情報が不充分です" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "認証サービスが認証情報を検索できません" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "基礎となる認証モジュールを知らないユーザー" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "サービスの最大試行回数に達しました" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "認証トークンはもはや有効ではありません。新しい認証トークンが必要です" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "ユーザーアカウントは失効しました" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "指定したセッションに対するエントリーを作成/削除できません" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "認証サービスがユーザー資格認定を検索できません" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "ユーザー資格認定の失効" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "ユーザー資格認定の設定に失敗しました" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "モジュール固有データがありません" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item()に渡された不正項目" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "会話エラー" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "認証トークン操作エラー" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "認証情報を回復できません" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "認証トークンをロック中です" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "認証トークンエージングが無効です" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "パスワードサービスによる予備チェックに失敗しました" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "戻り値は PAM タスク指名により無視する必要があります" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "モジュールが不明です" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "認証トークンの有効期限切れです" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "会話はイベントを待っています" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "アプリケーションはlibpamを再び呼び出す必要があります" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "不明な PAM エラー" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...時間が切れかかっています...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...時間切れです。\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "誤った会話(%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s 失敗: 終了コード %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s 失敗: シグナルをキャッチ %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s 失敗: 不明な状態 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"使用法: %s [--dir /path/to/tally-directory] [--user username] [--reset][--" +"legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "ログイン 失敗 最終日時 接続元\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u 回のログイン失敗によりアカウントはロックされました。" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(ロック解除まで %d 分)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(ロック解除まで残り %d 分)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %Y/%m/%d (%a) %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " ホスト:%.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " 端末:%.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "最終ログイン:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "新しいアカウントへようこそ!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "最後の失敗ログイン:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "最後の正しいログインの後に %d 回のログイン試行失敗があります。" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "最後の正しいログインの後に %d 回のログインの試行失敗があります。" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s'のログイン回数が多すぎます。" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "新しいメールはありません。" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "新しいメールがあります。" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "古いメールがあります。" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "メールがあります。" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "フォルダ%sにメールがありません。" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "フォルダ%sに新しいメールがあります。" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "フォルダ%sに古いメールがあります。" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "フォルダ%sにメールがあります。" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "ディレクトリ '%s' を作成しています。" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "ディレクトリ %s の作成・初期化ができません。" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" +"パスワードはすでに使用されています。 別のパスワードを選択してください。" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "パスワードはすでに使用されています。" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "デフォルトセキュリティコンテキストは %s です。" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "異なるロール又はレベルを入力しますか?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ロール:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "ロール %s にはデフォルトタイプがありません。" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "レベル:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "有効なセキュリティコンテキストではありません。" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "%s の有効なセキュリティコンテキストを取得できません。" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "セキュリティコンテキスト %s が割り当てられました。" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "キー作成コンテキスト %s が割り当てられました。" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAMの初期化に失敗しました\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item()に失敗しました\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "ログイン: fork失敗: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "アクセスが許可されました(最後のアクセスは %ld 秒前)。" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "アカウントは失効しています。システム管理者にお問い合わせください。" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "パスワードを直ちに変更する必要があります(管理者による強制)。" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "パスワードを直ちに変更する必要があります(パスワード失効)。" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "警告: パスワードは %d 日で失効します。" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "警告: パスワードは %d 日で失効します。" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS パスワードを変更できませんでした。" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "パスワードが与えられていません。" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "パスワードを変更できませんでした。" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "より短いパスワードを選択する必要があります。" + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "より長いパスワードを選択する必要があります。" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s 用のパスワードを変更しています。" + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "パスワードを変更するにはより長い時間の経過が必要です。" + +#~ msgid "You have no mail." +#~ msgstr "メールはありません。" + +#~ msgid "is the same as the old one" +#~ msgstr "パスワードが古いものと同じです。" + +#~ msgid "memory allocation error" +#~ msgstr "メモリー割り当てエラー" + +#~ msgid "is a palindrome" +#~ msgstr "前後どちらから読んでも同じパスワードです。" + +#~ msgid "case changes only" +#~ msgstr "大文字小文字を変えただけのパスワード" + +#~ msgid "is too similar to the old one" +#~ msgstr "古いものと似ています" + +#~ msgid "is too simple" +#~ msgstr "簡単すぎます" + +#~ msgid "is rotated" +#~ msgstr "回転しています" + +#~ msgid "not enough character classes" +#~ msgstr "文字クラスが不十分です" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "連続的な同一文字が多く含まれ過ぎです" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "単調な文字列が数多く含まれています" + +#~ msgid "contains the user name in some form" +#~ msgstr "なんらかの形式のユーザー名を含みます" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "よくないパスワード: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "アカウントは一時的にロックされています (残り %ld 秒)" + +#~ msgid "Authentication error" +#~ msgstr "認証エラー" + +#~ msgid "Service error" +#~ msgstr "サービスエラー" + +#~ msgid "Unknown user" +#~ msgstr "不明なユーザー" + +#~ msgid "Unknown error" +#~ msgstr "不明なエラー" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: 不正番号が--reset=に与えられました\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: 未認識オプション%s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: すべてのユーザーを非ゼロにリセットできません\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ka.po b/po/ka.po new file mode 100644 index 0000000..b8d102b --- /dev/null +++ b/po/ka.po @@ -0,0 +1,530 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# George Machitidze , 2013 +# Tomáš Mráz , 2016. #zanata +# Temuri Doghonadze , 2022. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-11-13 09:19+0000\n" +"Last-Translator: Temuri Doghonadze \n" +"Language-Team: Georgian \n" +"Language: ka\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.14.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "პაროლი: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "%s-ის ახალი პაროლი: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "მიმდინარე პაროლი: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "%s-ის ახალი პაროლი: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "ახალი პაროლი: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "გაიმეორეთ %s-ის ახალი პაროლი: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "გაიმეორეთ ახალი პაროლი: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "პაროლები არ ემთხვევა." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "ხელახლა შეიყვანეთ %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "პაროლის შეცვლის პროცედურა გაუქმდა." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "მომხმარებელი:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "წარმატებული" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "კრიტიკული შეცდომა - დაუყოვნებელი გაუქმება" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "მოდულის ჩატვირთვა ვერ მოხერხდა" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "სიმბოლო ვერ მოიძებნა" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "შეცდომა სერვისის მოდულში" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "სისტემური შეცდომა" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "მეხსიერების ბუფერის შეცდომა" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "წვდომა უარყოფილია" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "ავთენტიკაციის შეცდომა" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "ავთენტიკაციის მონაცემებთან წვდომისათვის არასაკმარისი უფლებები გაქვთ" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "ავთენტიკაციის სერვისს არ შეუძლია ავთენტიკაციის ინფორმაციის მიღება" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "მომდევნო ავთენტიკაციის მოდულისთვის მომხმარებელი უცნობია" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "სერვისის გაშვების ცდების მაქსიმალური რაოდენობა გახარჯულია" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "ავთენტიკაციის კოდი აღარ მუშაობს. გჭირდებათ ახალი" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "მომხმარებლის ვადა გასულია" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "მითითებული სესიის ჩანაწერის შექმნის/წაშლის შეცდომა" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "ავთენტიკაციის სერვისს არ შეუძლია მომხმარებლისა და პაროლის მიღება" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "მომხმარებელი ან პაროლი ვადაგასულია" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "მომხმარებლის ან პაროლის დაყენების შეცდომა" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "მოდულის საკუთარი მონაცემები არ არსებობს" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item()-ს არასწორი ელემენტი გადაეცა" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "საუბრის შეცდომა" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "ავთენტიკაციის კოდის მანიპულირების შეცდომა" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "ავთენტიკაციის ინფორმაციის აღდგენის შეცდომა" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "ავთენტიკაციის კოდის ბლოკი დაკავებულია" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "ავთენტიკაციის კოდის ვადის გასვლა გამორთულია" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "პაროლის წინასწარი შემოწმების შეცდომა" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "დაბრუნებული ნიშვნელობა იგნორირებული უნდა იყოს PAM dispatch-ის მიერ" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "მოდული უცნობია" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "ავთენტიკაციის კოდი ვადაგასულია" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "საუბარი მოვლენას ელოდება" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "აპლიკაციას libpam-ის თავიდან გაშვება სჭირდება" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "PAM-ის უცნობი შეცდომა" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...დრო გადის...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...უკაცრავად, თქვენი დრო გავიდა!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "შეცდომითი საუბარი (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s-ის შეცდომა: გამოსვლის კოდი %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s-ის შეცდომა: გადაჭერილი სიგნალი %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s-ის შეცდომა: უცნობიბ სტატუსი 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"გამოყენება: %s [--dir /ბილიკი/tally-ის-საქაღალდემდე] [--user მომხმარებელი] " +"[--reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "მომხმარებელი შეცდომები ბოლოშეცდომა საიდან\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "ანგარიში დაბლოკილია %u არასწორი ცდის შემდეგ." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(განბლოკვმდე დარჩენილია %d წთ)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(განბლოკვამდე დარჩენილია %d წთ)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s-დან" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s-ზე" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "ბოლო შესვლა: %s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "კეთილი იყოს თქვენი მობრძაება ახალ ანგარიშში!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "ბოლო წარუმატებელი ცდა: %s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"ბოლო წარმატებული შემოსვლის შემდეგ დაფიქსირებულია შემოსვლის %d წარუმატებელი " +"ცდა." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"ბოლო წარმატებული შემოსვლის შემდეგ დაფიქსირებულია შემოსვლის %d წარუმატებელი " +"ცდა." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "მომხმარებელი %s გამოიყენებოდა მეტისმეტად ხშირად." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "ახალი ელფოსტა არ მოგსვლიათ." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "თქვენ გაქვთ ახალი წერილი." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "თქვენ გაქვთ ძველი წერილი." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "თქვენ გაქვთ ფოსტა." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "საქაღალდეში %s ფოსტა არაა." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "საქაღალდეში %s გაქვთ ახალი ელ-ფოსტა." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "საქაღალდეში %s ძველი ფოსტა გაქვთ." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "საქაღალდეში %s გაქვთ ფოსტა." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "'%s' დირექტორიის შექმნა." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "საქაღალდის (%s) შექმნისა და ინიციალიზაციის შეცდომა." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "პაროლი უკვე იყო გამოყენებული. სხვა სცადეთ." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "პაროლი უკვე იყო გამოყენებული." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "უსაფრთხოების ნაგულისხმევი კონტექსტია %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "გნებავთ სხვა როლის ან დონის შეყვანა?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "როლი:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "როლისთვის %s ნაგულისხმევი ტიპი არ არსებობს." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "დონე:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "უსაფრთხოების არასწორი კონტექსტი." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "%s-ის სწორი კონტექსტის მიღების შეცდომა." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "უსაფრთხოების კონტექსტი %s ახლა მიმაგრებულია." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "გასაღების შექმნის კონტექსტი %s ახალ მიმაგრებულია." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM-ის ინიციალიზაციის შეცდომა\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_tem()-ის შეცდომა\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "შესვლა: %m-ის ახალი ასლის გაშვების შეცდომა" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "წვდომა მიღებულია (ბოლო წვდომა %ld წამის წინ)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "ანგარიში ვადაგასულია; დაუკავშირდით თქვენს სისტემურ ადმინისტრატორს." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"საჭიროა თქვენი პაროლი ახლავე შეცვალოთ (მითითებულია ადმინისტრატორის მიერ)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "საჭიროა თქვენი პაროლის დაუყოვნებლივ შეცვლა (პაროლი ვადაგასულია)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "გაფრთხილება: თქვენს პაროლს ვადა %d დღეში გაუვა." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "გაფრთხილება: თქვენს პაროლს ვადა %d დღეში გაუვა." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS პაროლის შეცვლა შეუძლებელია." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "პაროლი არ იქნა მითითებული." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "პაროლი არ შეცვლილა." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "უნდა აირჩიოთ უფრო მოკლე პაროლი." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "თქვენ უნდა აირჩიოთ უფრო გრძელი პაროლი." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s-ის პაროლის შეცვლა." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "პაროლის შესაცვლელად კიდევ უნდა მოითმინოთ." + +#~ msgid "You have no mail." +#~ msgstr "თქვენ გაქვთ ახალი წერილი." + +#~ msgid "is too similar to the old one" +#~ msgstr "ძალიან გავს ძველს" + +#~ msgid "is too simple" +#~ msgstr "ძალიან მარტივია" + +#~ msgid "is rotated" +#~ msgstr "შებრუნებულია" + +#~ msgid "contains the user name in some form" +#~ msgstr "რაღაც ფორმით გავს მომხმარებლის სახელს" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ცუდი პაროლი: %s" + +#~ msgid "Service error" +#~ msgstr "სერვისის შეცდომა" + +#~ msgid "Unknown user" +#~ msgstr "უცნობი მომხმარებელი" + +#~ msgid "Unknown error" +#~ msgstr "უცნობი შეცდომა" diff --git a/po/kk.po b/po/kk.po new file mode 100644 index 0000000..2737cff --- /dev/null +++ b/po/kk.po @@ -0,0 +1,578 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Baurzhan Muftakhidinov , 2012, 2020, 2022. +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-12-07 16:19+0000\n" +"Last-Translator: Baurzhan Muftakhidinov \n" +"Language-Team: Kazakh \n" +"Language: kk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.14.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Пароль: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "%s үшін ағымдағы пароль: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Ағымдағы пароль: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "%s үшін жаңа пароль: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "үшін жаңа пароль: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "%s үшін жаңа парольді қайта енгізіңіз: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "үшін жаңа парольді қайта енгізіңіз: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Кешіріңіз, парольдер өзара сәйкес емес." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s қайта енгізіңіз" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Парольді өзгертуден бас тартылды." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "тіркелгі:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Сәтті" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Қатаң қате - ұзамай-ақ шығу" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Модульді жүктеу мүмкін емес" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Таңба табылмады" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Қызмет модулінде қате" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Жүйелік қате" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Жады буфер қатесі" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Рұқсат жоқ" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Шындылықты тексеру қатесі" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" +"Шындылықты тексеру мәліметтерге қол жеткізу үшін тіркелгі ақпараты " +"жеткіліксіз" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Шындылықты тексеру қызметі мәліметтерді жүктей алмады" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Пайдаланушы шындылықты тексеру қызметіне белгісіз" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Қызмет үшін анықталған талаптар саны біткен" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Шындылықты тексеру маркері бұдан былай қате; жаңасы керек" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Пайдаланушы тіркелгісінің мерзімі аяқталған" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Көрсетілген сессия үшін жазбаны жасау/өшіру мүмкін емес" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Шындылықты тексеру қызметі пайдаланушының мәліметтерін жүктей алмады" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Пайдаланушы тіркелгісінің мерзімі аяқталған" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Пайдаланушы мәліметін орнату мүмкін емес" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Модуль үшін керек ақпарат жоқ болып тұр" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item() құрамында қате элемент берілген" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Сұхбат қатесі" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Шындылықты тексеру маркерімен әрекет өткізу қатесі" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Шындылықты тексеру ақпаратын қайтару мүмкін емес" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Шындылықты тексеру маркерінің оқшаулауы бос емес" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Шындылықты тексеру маркерінің мерзіммен шектеу сөндірілген" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Парольдерді тексеру қызметі алдын-ала тексеруду өткізе алмады" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Қайтарылған мәнді PAM-ға берілген кезде елемеу керек" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Модуль белгісіз" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Шындылықты тексеру маркерінің мерзімі аяқталған" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Сұхбат үрдісі оқиғаны күтіп тұр" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Бағдарлама libpam-ды қайтадан шақыруы керек" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Белгісіз PAM қатесі" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Уақытыңыз бітіп барады...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Кешіріңіз, сіздің уақытыңыз бітті!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "қате сұхбат (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s қатесі: шығу коды %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s қатесі: алынған сигнал %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s қатесі: белгісіз қалып-күйі 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Қолданылуы: %s: [--dir /tally-бумасына/дейінгі/жол] [--user пайдаланушы] [--" +"reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Пайдаланушы аты Сәтсіз кіру саны Соңғы қате Қайдан\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Тіркелгі %u рет қате кіру талабы салдарынан бұғатталды." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(бұғатты шешуге дейін %d минут қалды)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(бұғатты шешуге дейін %d минут қалды)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " қайдан: %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " қайда: %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Соңғы рет жүйеге кіру:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Жаңа тіркелгңізіге қош келдіңіз!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Соңғы сәтсіз жүйеге кіру талабы:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Соңғы сәтті жүйеге кіру реттен кейін %d қате талап болған." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Соңғы сәтті жүйеге кіру реттен кейін %d қате талап болған." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "\"%s\" үшін жүйеге кіру талап саны тым көп." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Сізде жаңа пошта жоқ." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Сізде жаңа поштаңыз бар." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Сізде ескі поштаңыз бар." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Сізде пошта бар." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Сізде %s бумасында пошта жоқ." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Сізде %s бумасында жаңа поштаңыз бар." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Сізде %s бумасында ескі поштаңыз бар." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Сізде %s бумасында поштаңыз бар." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "'%s' бумасын жасау." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "'%s' бумасын жасау мүмкін емес." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Пароль осыған дейін қолданған. Басқасын таңдаңыз." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Пароль осыған дейін қолданылған." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Бастапқы қауіпсіздік контексті %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Басқа роль не деңгейді енгізуді қалайсыз ба?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ролі:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "%s ролі үшін бастапқы түрі көрсетілмеген." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "деңгейі:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Дұрыс қауіпсіздік контексті емес." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "%s үшін жарамды контекстті алу мүмкін емес." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "%s қауіпсіздік контексті тағайындалды." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "%s кілттерді жасау қауіпсіздік контексті тағайындалды." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM-ды іске қосу мүмкін емес\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() орындау мүмкін емес\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: үрдісті бастау мүмкін емес: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Рұқсат расталған (соңғы қатынау %ld секунд бұрын болған)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Тіркелгіңіздің мерзімі аяқталған; жүйелік әкімшіңізге хабарласыңыз." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Сізге пароліңізді қазір ауыстыру керек (әкімші мәжбүрлеген)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Сізге пароліңізді қазір ауыстыру керек (парольдің мерзімі аяқталған)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Ескерту: сіздің пароліңіздің мерзімі %d күнде бітеді." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Ескерту: сіздің пароліңіздің мерзімі %d күнде бітеді." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS паролін өзгерту мүмкін емес." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Пароль көрсетілмеді." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Пароль өзгертілмеді." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Сіз қысқа парольді таңдауыңыз керек." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Сізге ұзынырақ парольді таңдау керек." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s үшін парольді өзгерту." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Пароліңізді өзгерті үшін біраз күтуіңіз керек." + +#~ msgid "You have no mail." +#~ msgstr "Сізде жаңа пошта жоқ." + +#~ msgid "is the same as the old one" +#~ msgstr "алдыңғысына сәйкес болып тұр" + +#~ msgid "memory allocation error" +#~ msgstr "жадыны бөлу қатесі" + +#~ msgid "is a palindrome" +#~ msgstr "палиндром болып тұр" + +#~ msgid "case changes only" +#~ msgstr "өзгерістер таңбалардың регистрінде ғана" + +#~ msgid "is too similar to the old one" +#~ msgstr "ескі парольге өте ұқсас" + +#~ msgid "is too simple" +#~ msgstr "өте оңай болып тұр" + +#~ msgid "is rotated" +#~ msgstr "аударылған ескі пароль" + +#~ msgid "not enough character classes" +#~ msgstr "керек таңбалар кластары жоқ" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "құрамында бірдей таңбалардың тізбегі бар" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "құрамында біркелкі таңбалардың тым ұзын тізбегі бар" + +#~ msgid "contains the user name in some form" +#~ msgstr "құрамында пайдаланушы аты бар" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ҚАТЕ ПАРОЛЬ: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Тіркелгі уақытша бұғатталған (%ld секунд қалды)." + +#~ msgid "Authentication error" +#~ msgstr "Шындылықты анықтау қатесі" + +#~ msgid "Service error" +#~ msgstr "Қызмет қатесі" + +#~ msgid "Unknown user" +#~ msgstr "Белгісіз пайдаланушы" + +#~ msgid "Unknown error" +#~ msgstr "Белгісіз қате" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset= үшін қате сан берілді\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: %s опциясы белгісіз\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file түбірлік_файл_аты] [--user пайдаланушы] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Барлық пайдаланушыларды нөлдік емес мәнге тастау мүмкін емес\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f түбірлік_файл_аты] [--file түбірлік_файл_аты]\n" +#~ " [-u пайдаланушы_аты] [--user пайдаланушы_аты]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/km.po b/po/km.po new file mode 100644 index 0000000..02742b0 --- /dev/null +++ b/po/km.po @@ -0,0 +1,559 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Khoem Sokhem , 2006. +# Leang Chumsoben , 2006. +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-29 07:00-0500\n" +"Last-Translator: Tomáš Mráz \n" +"Language-Team: LANGUAGE \n" +"Language: km\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "ពាក្យសម្ងាត់ ៖ " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "ពាក្យ​សម្ងាត់ %s ថ្មី ៖" + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "ពាក្យ​សម្ងាត់ ថ្មី ៖" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "ពាក្យ​សម្ងាត់ %s ថ្មី ៖" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "ពាក្យ​សម្ងាត់ ថ្មី ៖" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "វាយ​ពាក្យ​សម្ងាត់ %s ថ្មី​ឡើង​វិញ ៖" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "វាយ​ពាក្យ​សម្ងាត់ ថ្មី​ឡើង​វិញ ៖" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "សូម​ទោស ពាក្យ​សម្ងាត់​មិន​ដូច​គ្នា​ឡើយ ។" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "ពាក្យសម្ងាត់​ត្រូវ​បាន​ប្រើ​រួច​ហើយ ។ សូម​ជ្រើស​មួយ​ទៀត ។" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "ចូល ៖" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "ជោគជ័យ" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "កំហុស​ធ្ងន់ធ្ងរ - បោះបង់ភ្លាមៗ" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "រក​មិន​ឃើញ​និមិត្ត​សញ្ញា" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "កំហុស​នៅ​ក្នុង​ម៉ូឌុល​សេវា" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "កំហុស​ប្រព័ន្ធ" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "កំហុស​សតិ​បណ្ដោះ​អាសន្ន" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "សិទ្ធិ​ត្រូវ​បាន​បដិសេធ" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "បរាជ័យ​ក្នុង​ការ​ផ្ទៀងផ្ទាត់​ភាព​ត្រឹមត្រូវ" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "លិខិត​សម្គាល់​មិន​គ្រប់គ្រាន់ដើម្បី​ចូល​ដំណើរ​ការ​ទិន្នន័យ​ដែល​មាន​ការ​ផ្ទៀងផ្ទាត់​ភាព​ត្រឹមត្រូវ" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "អ្នក​ប្រើ មិន​ស្គាល់​ម៉ូឌុលផ្ទៀងផ្ទាត់​ភាពត្រឹមត្រូវមូលដ្ឋាន" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "គណនី​អ្នក​ប្រើ​បាន​ផុត​កំណត់​ហើយ" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "មិន​អាច​បង្កើត ឬ យក​ធាតុ​មួយ​ចេញ សម្រាប់សម័យ​ដែល​បាន​បញ្ជាក់" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "សេវា​ផ្ទៀងផ្ទាត់​ភាព​ត្រឹមត្រូវ មិន​អាច​ទៅ​យក​លិខិត​សម្គាល់របស់អ្នក​ប្រើ" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "លិខិត​សម្គាល់របស់​អ្នក​ប្រើ​បាន​ផុត​កំណត់" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "បរាជ័យ​ក្នុង​ការ​កំណត់​លិខិត​សម្គាល់របស់អ្នក​ប្រើ" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "គ្មាន​ម៉ូឌុល​ទិន្នន័យជាក់លាក់​ត្រូវបង្ហាញ" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "បាន​ហុច​ធាតុមិន​ល្អ​ទៅ​ឲ្យ to pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "កំហុស​សន្ទនា" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "កំហុស​រៀបចំ​ថូខឹន​ផ្ទៀងផ្ទាត់​ភាព​ត្រឹមត្រូវ" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "ព័ត៌មាន​ផ្ទៀងផ្ទាត់​ភាព​ត្រឹមត្រូវ​មិន​អាច​ត្រូវ​យកបាន​វិញ" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "ការ​ចាក់សោ​ថូខឹន​ផ្ទៀងផ្ទាត់​ភាព​ត្រឹមត្រូវកំពុង​ជាប់​រវល់" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "ភាព​ចាស់​របស់​ថូខឹន​ផ្ទៀងផ្ទាត់​ភាព​ត្រឹមត្រូវ មិនបាន​អនុញ្ញាត" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "ការពិនិត្យ​មើលដំបូង​ដោយ​សេវា​ពាក្យ​សម្ងាត់​បាន​បរាជ័យ" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "តម្លៃ​ត្រឡប់​គួរត្រូវ​បាន​មិនអើពើ​ដោយ​ការ​បញ្ជូន PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "មិន​ស្គាល់​ម៉ូឌុល" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "ថូខឹនផ្ទៀងផ្ទាត់​ភាព​ត្រឹមត្រូវ​បាន​ផុត​កំណត់" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "ការ​សន្ទនា​កំពុង​រង់ចាំ​ព្រឹត្តិការណ៍" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "កម្មវិធី​ត្រូវ​តែ​ហៅ libpam ម្ដង​ទៀត" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "មិន​ស្គាល់​កំហុស PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...អស់​ពេល...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...សូម​ទោស អ្នក​អស់​ពេល​ហើយ !\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "សន្ទនាច្រឡំ (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s ៖ [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " ពី %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " លើ %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "ចូល​ចុងក្រោយ ៖%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "សូម​ស្វាគមន៍​មក​កាន់​គណនី​ថ្មី​របស់​អ្នក !" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "មាន​ការ​ចូល​ច្រើន​ពេក​សម្រាប់ '%s' ។" + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "អ្នក​មាន​សំបុត្រ​ថ្មី ។" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "អ្នក​មាន​សំបុត្រ​ថ្មី ។" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "អ្នក​មាន​សំបុត្រ​ចាស់ ។" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "អ្នក​មាន​សំបុត្រ ។" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "អ្នក​គ្មានសំបុត្រនៅ​ក្នុង​ថត %s ។" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "អ្នក​មាន​សំបុត្រ​ថ្មី​នៅ​ក្នុង​ថត %s ។" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "អ្នក​មាន​សំបុត្រ​ចាស់​នៅ​ក្នុង​ថត %s ។" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "អ្នក​មាន​សំបុត្រ​នៅ​ក្នុង​ថត %s ។" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "ពាក្យសម្ងាត់​ត្រូវ​បាន​ប្រើ​រួច​ហើយ ។ សូម​ជ្រើស​មួយ​ទៀត ។" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +#, fuzzy +msgid "Password has been already used." +msgstr "ពាក្យសម្ងាត់​ត្រូវ​បាន​ប្រើ​រួច​ហើយ ។ សូម​ជ្រើស​មួយ​ទៀត ។" + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "មិន​មែន​ជា​បរិបទ​សុវត្ថិភាព​ត្រឹមត្រូវ​មួយឡើយ" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "មិន​មែន​ជា​បរិបទ​សុវត្ថិភាព​ត្រឹមត្រូវ​មួយឡើយ" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "បរិបទ​សុវត្ថិភាព %s បាន​ផ្ដល់​តម្លៃ​" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "បរិបទ​សុវត្ថិភាព %s បាន​ផ្ដល់​តម្លៃ​" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "បាន​បរាជ័យ​ក្នុង​ការ​ចាប់ផ្ដើម PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "បាន​បរាជ័យ pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "ចូល ៖ ចម្លង​ខ្លួន​ឯង​មិន​បាន​ជោគជ័យ ៖ %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "គណនី​របស់​អ្នក​បាន​ផុតកំណត់​ហើយ សូម​ទាក់ទង​អ្នក​គ្រប់គ្រង​ប្រព័ន្ធ​របស់​អ្នក" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "អ្នក​ត្រូវ​តែ​ផ្លាស់ប្ដូរ​ពាក្យសម្ងាត់​របស់​អ្នក​ឥឡូវ​នេះ (root បាន​ចេញ​បញ្ជា)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "អ្នក​ត្រូវ​តែ​ផ្លាស់ប្ដូរ​ពាក្យសម្ងាត់​របស់​អ្នក​ឥឡូវ​នេះ (ពាក្យសម្ងាត់​ចាស់​ហើយ)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "មិន​អាច​ផ្លាស់ប្ដូរ​ពាក្យសម្ងាត់ NIS បាន​ឡើយ ។" + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "មិន​បាន​ផ្ដល់​ពាក្យសម្ងាត់" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "មិន​អាច​ផ្លាស់ប្ដូរ​ពាក្យសម្ងាត់ NIS បាន​ឡើយ ។" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "អ្នកត្រូវតែជ្រើសរើសពាក្យសម្ងាត់ខ្លី។" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "អ្នក​ត្រូវ​តែ​ជ្រើស​ពាក្យសម្ងាត់​វែង​ជាង​នេះ" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "អ្នក​ត្រូវ​តែ​រង់ចាំ​បន្តិច ដើម្បី​ផ្លាស់ប្ដូរ​ពាក្យសម្ងាត់​របស់​អ្នក" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "អ្នក​មាន​សំបុត្រ​ថ្មី ។" + +#~ msgid "is the same as the old one" +#~ msgstr "ដូច​គ្នា​នឹង​ពាក្យ​សម្ងាត់​ចាស់" + +#~ msgid "is a palindrome" +#~ msgstr "ត្រឡប់​ចុះ​ឡើង" + +#~ msgid "case changes only" +#~ msgstr "គ្រាន់​តែ​ផ្លាស់ប្ដូរ​លក្ខណៈ​អក្សរ​ប៉ុណ្ណោះ​" + +#~ msgid "is too similar to the old one" +#~ msgstr "ស្រដៀង​គ្នា​ណាស់​នឹង​ពាក្យ​សម្ងាត់​ចាស់" + +#~ msgid "is too simple" +#~ msgstr "សាមញ្ញ​ពេក" + +#~ msgid "is rotated" +#~ msgstr "បាន​បង្វិល" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ពាក្យ​សម្ងាត់​មិន​ល្អ ៖ %s" + +#~ msgid "Authentication error" +#~ msgstr "កំហុស​ក្នុង​ការ​ផ្ទៀងផ្ទាត់​ភាព​ត្រឹមត្រូវ" + +#~ msgid "Service error" +#~ msgstr "កំហុស​សេវា" + +#~ msgid "Unknown user" +#~ msgstr "មិន​ស្គាល់​អ្នក​ប្រើ" + +#~ msgid "Unknown error" +#~ msgstr "មិន​ស្គាល់​កំហុស" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s ៖ លេខ​មិន​ល្អ​បាន​ផ្ដល់​ទៅ --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s ៖ ជម្រើស​ដែល​មិន​ស្គាល់ %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s ៖ [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s ៖ មិន​អាច​កំណត់​អ្នក​ប្រើ​ទាំងអស់​ទៅ​មិនមែន​សូន្យ​ឡើងវិញ​បានទេ\n" diff --git a/po/kn.po b/po/kn.po new file mode 100644 index 0000000..c503830 --- /dev/null +++ b/po/kn.po @@ -0,0 +1,590 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# shanky , 2013 +# shanky , 2012 +# shankar , 2007-2009 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2013-04-15 04:49-0400\n" +"Last-Translator: shanky \n" +"Language-Team: Kannada (http://www.transifex.com/projects/p/fedora/language/" +"kn/)\n" +"Language: kn\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "ಗುಪ್ತಪದ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "ಹೊಸ %sಗುಪ್ತಪದ: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "ಹೊಸ ಗುಪ್ತಪದ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "ಹೊಸ %sಗುಪ್ತಪದ: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "ಹೊಸ ಗುಪ್ತಪದ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "ಹೊಸ %sಗುಪ್ತಪದವನ್ನು ಪುನರ್ ಟೈಪಿಸಿ: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "ಹೊಸ ಗುಪ್ತಪದವನ್ನು ಪುನರ್ ಟೈಪಿಸಿ: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "ಕ್ಷಮಿಸಿ, ಗುಪ್ತಪದಗಳು ತಾಳೆಯಾಗುತ್ತಿಲ್ಲ." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s ಅನ್ನು ಮರಳಿ ನಮೂದಿಸಿ" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "ಗುಪ್ತಪದ ಬದಲಾವಣೆಯನ್ನು ಸ್ಥಗಿತಗೊಳಿಸಲಾಗಿದೆ." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "ಲಾಗಿನ್:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "ಯಶಸ್ಸು" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "ಸಂದಿಗ್ಧ ದೋಷ - ತಕ್ಷಣ ಸ್ಥಗಿತಗೊಳಿಸು" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "ಮಾಡ್ಯೂಲನ್ನು ಲೋಡ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "ಸಂಜ್ಞೆ ಪತ್ತೆಯಾಗಿಲ್ಲ" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "ಸೇವಾ ಮಾಡ್ಯೂಲಿನಲ್ಲಿ ದೋಷ" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "ವ್ಯವಸ್ಥೆಯ ದೋಷ" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "ಮೆಮೊರಿ ಬಫರ್ ದೋಷ" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "ಅನುಮತಿಯನ್ನು ನಿರಾಕರಿಸಲಾಗಿದೆ" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "ದೃಢೀಕರಣವು ವಿಫಲಗೊಂಡಿದೆ" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "ದೃಢೀಕರಣ ದತ್ತಾಂಶವನ್ನು ನಿಲುಕಿಸಿಕೊಳ್ಳಲು ರುಜುವಾತು ಸಾಕಷ್ಟು ಇಲ್ಲ" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "ದೃಢೀಕರಣ ಸೇವೆಯು ದೃಢೀಕರಣ ಮಾಹಿತಿಯನ್ನು ಹಿಂಪಡೆಯಲು ಆಗುತ್ತಿಲ್ಲ" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "ಕೆಳಗಿರುವ ದೃಢೀಕರಣ ಮಾಡ್ಯೂಲಿಗೆ ಬಳಕೆದಾರನ ಬಗ್ಗೆ ತಿಳಿದಿಲ್ಲ್ಲ" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "ಸೇವೆಗಾಗಿನ ಗರಿಷ್ಟ ಸಂಖ್ಯೆಯ ಪುನರ್ ಪ್ರಯತ್ನಗಳೆಲ್ಲಾ ಖಾಲಿಯಾಗಿವೆ" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "ದೃಢೀಕರಣ ಸಂಕೇತವು ಅಮಾನ್ಯವಾಗಿದೆ; ಹೊಸದೊಂದರ ಅಗತ್ಯವಿದೆ" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "ಬಳಕೆದಾರ ಖಾತೆಯು ಅವಧಿ ಅಂತ್ಯವಾಗಿದೆ" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "ನಿಗದಿತ ಸೆಶನ್ನಿಗೆ ಒಂದು ನಮೂದನ್ನು ಮಾಡಲು/ತೆಗೆದುಹಾಕಲು ಆಗುತ್ತಿಲ್ಲ" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "ದೃಢೀಕರಣ ಸೇವೆಗೆ ಬಳಕೆದಾರನ ರುಜುವಾತುಗಳನ್ನು ಹಿಂಪಡೆಯಲು ಆಗುತ್ತಿಲ್ಲ" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "ಬಳಕೆದಾರ ರುಜುವಾತುಗಳ ಅವಧಿ ಅಂತ್ಯಗೊಂಡಿದೆ" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "ಬಳಕೆದಾರ ರುಜುವಾತುಗಳನ್ನು ಸಂಯೋಜಿಸುವಲ್ಲಿ ವಿಫಲತೆ" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "ಮಾಡ್ಯೂಲ್ ನಿಶ್ಚಿತ ದತ್ತಾಂಶವು ಅಸ್ತಿತ್ವದಲ್ಲಿಲ್ಲ" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item() ಗೆ ಒಂದು ಕೆಟ್ಟ ಅಂಶವನ್ನು ರವಾನಿಸಲಾಗಿದೆ" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "ಸಂವಾದಾತ್ಮಕ ದೋಷ" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "ದೃಢೀಕರಣ ಸಂಕೇತದ ಕುಶಲ ನಿರ್ವಹಣೆಯಲ್ಲಿನ ದೋಷ" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "ದೃಢೀಕರಣ ಮಾಹಿತಿಯನ್ನು ಮರುಗಳಿಸಲು ಆಗುವುದಿಲ್ಲ" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "ದೃಢೀಕರಣ ಸಂಕೇತ ಲಾಕ್ ಕಾರ್ಯಮಗ್ನವಾಗಿದೆ" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "ದೃಢೀಕರಣ ಸಂಕೇತದ ಕಾಲಾವಧಿ ಮೀರುವುದನ್ನು ಅಶಕ್ತಗೊಳಿಸಲಾಗಿದೆ" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "ಗುಪ್ತಪದ ಸೇವೆಯ ಪ್ರಾಥಮಿಕ ಪರೀಕ್ಷೆಗಳು ವಿಫಲಗೊಂಡಿವೆ" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "ಮರಳುವ ಮೌಲ್ಯವನ್ನು PAM ರವಾನೆಯಿಂದ ಅಲಕ್ಷಿಸಬೇಕು" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "ಮಾಡ್ಯೂಲು ತಿಳಿದಿಲ್ಲ" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "ದೃಢೀಕರಣ ಸಂಕೇತದ ಅವಧಿ ಅಂತ್ಯಗೊಂಡಿದೆ" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "ಸಂವಾದವು ಕಾರ್ಯಕ್ರಮಕ್ಕಾಗಿ ಕಾಯುತ್ತಿದೆ" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "ಅನ್ವಯವು libpam ಅನ್ನು ಪುನಃ ಕರೆಯಬೇಕಿದೆ" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "ಗೊತ್ತಿರದ PAM ದೋಷ" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...ಸಮಯ ಸರಿಯುತ್ತಿದೆ...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...ಕ್ಷಮಿಸಿ, ನಿಮ್ಮ ಸಮಯ ಮುಗಿಯಿತು!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "ದೋಷಪೂರಿತ ಸಂವಾದ (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s ವಿಫಲಗೊಂಡಿದೆ: ನಿರ್ಗಮಿಸಲು ಸಂಜ್ಞೆ %d " + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s ವಿಫಲಗೊಂಡಿದೆ: ಹಿಡಿಯಲಾದ ಸೂಚನೆ %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s ವಿಫಲಗೊಂಡಿದೆ: ಗೊತ್ತಿರದ ಸ್ಥಿತಿ 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "ಪ್ರವೇಶ ವಿಫಲತೆಗಳು ಇತ್ತೀಚಿನ ವಿಫಲತೆ ಇಂದ\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "ವಿಫಲಗೊಂಡ %u ಪ್ರವೇಶಗಳಿಂದಾಗಿ ಖಾತೆಯನ್ನು ಲಾಕ್ ಮಾಡಲಾಗುತ್ತಿದೆ" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s ನಿಂದ" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s ನಲ್ಲಿ" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "ಕೊನೆಯ ಲಾಗಿನ್:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "ನಿಮ್ಮ ಹೊಸ ಖಾತೆಗೆ ಸುಸ್ವಾಗತ!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "ಕೊನೆಯ ಲಾಗಿನ್:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "ಕೊನೆಯ ಬಾರಿಯ ಯಶಸ್ವಿ ಪ್ರವೇಶದ ನಂತರ %d ವಿಫಲಗೊಂಡ ಪ್ರಯತ್ನಗಳಿವೆ." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "ಕೊನೆಯ ಬಾರಿಯ ಯಶಸ್ವಿ ಪ್ರವೇಶದ ನಂತರ %d ಪ್ರವೇಶದ ಪ್ರಯತ್ನಗಳು ವಿಫಲಗೊಂಡಿದೆ." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s'ಗಾಗಿ ಬಹಳಷ್ಟು ಲಾಗಿನ್ನುಗಳು." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "ನಿಮಗಾಗಿ ಹೊಸ ಮೈಲ್ ಇದೆ." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "ನಿಮಗಾಗಿ ಹೊಸ ಮೈಲ್ ಇದೆ." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "ನಿಮಗಾಗಿ ಹಳೆ ಮೈಲ್ ಇದೆ." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "ನಿಮಗಾಗಿ ಮೈಲ್ ಇದೆ." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "%s ಫೋಲ್ಡರಿನಲ್ಲಿ ನಿಮಗಾಗಿ ಯಾವುದೆ ಮೈಲ್ ಇಲ್ಲ." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "%s ಫೋಲ್ಡರಿನಲ್ಲಿ ನಿಮಗಾಗಿ ಹೊಸ ಮೈಲ್ ಇದೆ." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "%s ಫೋಲ್ಡರಿನಲ್ಲಿ ನಿಮಗಾಗಿ ಹಳೆ ಮೈಲ್ ಇದೆ." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "%s ಫೋಲ್ಡರಿನಲ್ಲಿ ನಿಮಗಾಗಿ ಮೈಲ್ ಇದೆ." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "ಕೋಶ '%s' ಅನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "ಕೋಶ '%s' ಅನ್ನು ರಚಿಸಲು ಹಾಗು ಆರಂಭಿಸಲು ಸಾಧ್ಯವಾಗಿಲ್ಲ." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "ಗುಪ್ತಪದವು ಈಗಾಗಲೆ ಬಳಸಲ್ಪಟ್ಟಿದೆ. ಬೇರೊಂದನ್ನು ಬಳಸಿ." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "ಗುಪ್ತಪದವನ್ನು ಈಗಾಗಲೆ ಬಳಸಲಾಗಿದೆ." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "ಡೀಫಾಲ್ಟ್‍ ಸುರಕ್ಷತಾ ಸನ್ನಿವೇಶ %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "ನೀವು ನೀವು ಬೇರೊಂದು ಪಾತ್ರ ಅಥವ ಮಟ್ಟವನ್ನು ದಾಖಲಿಸಲು ಇಚ್ಛಿಸುತ್ತೀರ?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ಪಾತ್ರ:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "%s ಪಾತ್ರಕ್ಕಾಗಿ ಯಾವುದೆ ಡೀಫಾಲ್ಟ್‍ ಬಗೆ ಇಲ್ಲ\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "ಮಟ್ಟ:" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "ಸಮಂಜಸವಾದ ಸುರಕ್ಷತಾ ಸನ್ನಿವೇಶ ಅಲ್ಲ" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "ಸುರಕ್ಷತಾ ಸನ್ನಿವೇಶ %s ವನ್ನು ನಿಯೋಜಿಸಲಾಗಿದೆ" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "ಕೀಲಿ ನಿರ್ಮಾಣ ಸನ್ನಿವೇಶ %s ವನ್ನು ನಿಯೋಜಿಸಲಾಗಿದೆ" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM ಅನ್ನು ಆರಂಭಿಸುವಲ್ಲಿನ ವಿಫಲತೆ\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() ಮಾಡುವಲ್ಲಿ ವಿಫಲತೆ\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "ಲಾಗಿನ್: ಫೋರ್ಕಿಂಗ್ ಮಾಡುವಲ್ಲಿ ವಿಫಲತೆ:%m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" +"ಅನುಮತಿಯನ್ನು ನೀಡಲಾಗಿದೆ (ಕೊನೆಯ ಬಾರಿಗೆ %ld ಸೆಕೆಂಡುಗಳ ಹಿಂದೆ ನಿಲುಕಿಸಿಕೊಳ್ಳಲಾಗಿತ್ತು)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "ನಿಮ್ಮ ಖಾತೆಯ ಅವಧಿ ಅಂತ್ಯಗೊಂಡಿದೆ; ದಯವಿಟ್ಟು ನಿಮ್ಮ ಗಣಕ ವ್ಯವಸ್ಥಾಪಕರನ್ನು ಸಂಪರ್ಕಿಸಿ" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "ನೀವು ಈ ಕೂಡಲೆ ನಿಮ್ಮ ಗುಪ್ತಪದವನ್ನು ಬದಲಿಸುವ ಅಗತ್ಯವಿದೆ (ಮೂಲದಿಂದ ಒತ್ತಾಯಿತವಾಗಿದೆ)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" +"ನೀವು ಈ ಕೂಡಲೆ ನಿಮ್ಮ ಗುಪ್ತಪದವನ್ನು ಬದಲಿಸುವ ಅಗತ್ಯವಿದೆ (ಗುಪ್ತಪದವು ಬಹಳ ಹಳೆಯದಾಗಿದೆ)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "ಎಚ್ಚರಿಕೆ: ನಿಮ್ಮ ಗುಪ್ತಪದದ ವಾಯಿದೆಯು %d ದಿನಗಳ ನಂತರ ತೀರಿಹೋಗುತ್ತದೆ" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "ಎಚ್ಚರಿಕೆ: %d ದಿನಗಳಲ್ಲಿ ನಿಮ್ಮ ಗುಪ್ತಪದದ ಅವಧಿ ಅಂತ್ಯಗೊಳ್ಳುತ್ತದೆ" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS ಗುಪ್ತಪದವನ್ನು ಬದಲಾಯಿಸಲಾಗುವುದಿಲ್ಲ್ಲ." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "ಯಾವುದೇ ಗುಪ್ತಪದ ನೀಡಲಾಗಿಲ್ಲ" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS ಗುಪ್ತಪದವನ್ನು ಬದಲಾಯಿಸಲಾಗುವುದಿಲ್ಲ್ಲ." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "ನೀವು ಕಡಿಮೆ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಆರಿಸಬೇಕು." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "ನೀವು ಒಂದು ಉದ್ದವಾದ ಗುಪ್ತಪದವನ್ನು ಆರಿಸಬೇಕು" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s ಗಾಗಿ ಗುಪ್ತಪದವನ್ನು ಬದಲಾಯಿಸಲಾಗುತ್ತಿದೆ." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "ನಿಮ್ಮ ಗುಪ್ತಪದವನ್ನು ಬದಲಾಯಿಸಲು ನೀವು ಬಹಳ ಸಮಯ ಕಾಯಬೇಕು" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "ನಿಮಗಾಗಿ ಹೊಸ ಮೈಲ್ ಇದೆ." + +#~ msgid "is the same as the old one" +#~ msgstr "ಇದು ಹಳೆಯದರ ಹಾಗೆಯೇ ಇದೆ" + +#~ msgid "memory allocation error" +#~ msgstr "ಮೆಮೊರಿ ನಿಯೋಜನಾ ದೋಷ" + +#~ msgid "is a palindrome" +#~ msgstr "ಇದು ಒಂದು ಸಮಾನ ಪೂರ್ವಾಪರವಾಗಿದೆ (palindrome)" + +#~ msgid "case changes only" +#~ msgstr "ಕೇವಲ ಕೇಸ್ ಗಳ ಬದಲಾವಣೆಯಾಗಿದೆ ಅಷ್ಟೆ" + +#~ msgid "is too similar to the old one" +#~ msgstr "ಇದು ಹಳೆಯದಕ್ಕೆ ಬಹಳಷ್ಟು ಹೋಲುತ್ತದೆ" + +#~ msgid "is too simple" +#~ msgstr "ಇದು ಬಹಳ ಸರಳವಾಗಿದೆ" + +#~ msgid "is rotated" +#~ msgstr "ಇದು ತಿರುಗಿಸಲಾಗಿದೆ" + +#~ msgid "not enough character classes" +#~ msgstr "ಸಾಕಷ್ಟು ಕ್ಯಾರೆಕ್ಟರ್ ವರ್ಗಗಳು ಇಲ್ಲ" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "ಇದು ಒಂದೇ ಬಗೆಯ ಬಹಳಷ್ಟು ಕ್ಯಾರೆಕ್ಟರುಗಳನ್ನು ಅನುಕ್ರಮವಾಗಿ ಹೊಂದಿದೆ" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "ಇದು ಒಂದೇ ಬಗೆಯ ಉದ್ದನೆಯ ಅಕ್ಷರಗಳ ಅನುಕ್ರಮವನ್ನು ಹೊಂದಿದೆ" + +#~ msgid "contains the user name in some form" +#~ msgstr "ಇದು ಯಾವುದೊ ಒಂದು ಬಗೆಯಲ್ಲಿ ಬಳಕೆದಾರ ಹೆಸರನ್ನು ಒಳಗೊಂಡಿದೆ" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ಕೆಟ್ಟ ಗುಪ್ತಪದ: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "ಖಾತೆಯನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ಲಾಕ್ ಮಾಡಲಾಗಿದೆ (%ld ಸೆಕೆಂಡುಗಳು ಉಳಿದಿವೆ)" + +#~ msgid "Authentication error" +#~ msgstr "ದೃಢೀಕರಣ ದೋಷ" + +#~ msgid "Service error" +#~ msgstr "ಸೇವಾ ದೋಷ" + +#~ msgid "Unknown user" +#~ msgstr "ಗೊತ್ತಿರದ ಬಳಕೆದಾರ" + +#~ msgid "Unknown error" +#~ msgstr "ಗೊತ್ತಿರದ ದೋಷ" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset= ಗೆ ಕೊಡಲಾದ ಕೆಟ್ಟ ಸಂಖ್ಯೆ\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: ಗುರುತಿಸಲಾಗದ ಆಯ್ಕೆ %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: ಎಲ್ಲಾ ಬಳಕೆದಾರರನ್ನು ಶೂನ್ಯವಲ್ಲದುದಕ್ಕೆ ಪುನರ್ ಸಂಯೋಜಿಸಲು ಆಗುವುದಿಲ್ಲ\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ko.po b/po/ko.po new file mode 100644 index 0000000..0f53cfc --- /dev/null +++ b/po/ko.po @@ -0,0 +1,580 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# eukim , 2013 +# eukim , 2007,2009 +# eukim , 2012 +# Tomáš Mráz , 2016. #zanata +# simmon , 2021. +# Seong-ho Cho , 2021, 2022, 2023, 2024. +# 김인수 , 2022, 2023, 2024. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2024-10-02 05:38+0000\n" +"Last-Translator: 김인수 \n" +"Language-Team: Korean \n" +"Language: ko\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.7.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "비밀번호: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "현재 %s 비밀번호: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "현재 비밀번호: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "신규 %s 비밀번호: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "신규 비밀번호: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "신규 %s 비밀번호 재입력: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "신규 비밀번호 재입력: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "죄송하지만, 비밀번호가 일치하지 않습니다." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s를 다시 입력하세요" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "비밀번호 변경이 중단되었습니다." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "로그인:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "성공" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "치명적인 오류 - 즉시 중지" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "모듈 불러오기 실패" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "기호를 찾을 수 없음" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "서비스 모듈에서 오류 발생" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "시스템 오류" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "메모리 버퍼 오류" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "권한 부여 거부" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "인증 실패" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "인증 자료 접근에 불충분한 인증 정보" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "인증 서비스에서 인증 정보를 가져올 수 없습니다" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "기본 인증 모듈에서 알 수 없는 사용자" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "서비스 재시도 최대 횟수를 넘었습니다" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "인증 토큰이 더 이상 유효하지 않습니다. 새로운 인증 토큰이 필요합니다" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "사용자 계정이 만료되었습니다" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "지정된 세션을 위한 항목을 생성/제거 할 수 없습니다" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "인증 서비스에서 사용자 인증 정보를 가져올 수 없습니다" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "사용자 인증 만료됨" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "사용자 인증 설정 실패" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "모듈에 해당하는 데이터가 없습니다" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item()에 잘못된 항목을 전달했습니다" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "정보교환 오류" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "인증 토큰 조작 오류" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "인증 정보를 복구할 수 없습니다" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "인증 토큰 잠금 사용 중" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "인증 토큰 기한이 비활성화됩니다" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "비밀번호 서비스에서 사전 검사 실패함" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "반환 값은 PAM 전송에 의해 무시됩니다" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "모듈을 알 수 없음" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "인증 토큰 유효 기간이 지남" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "정보교환을 위해 이벤트를 대기 중입니다" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "프로그램에서 libpam을 다시 불러와야 합니다" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "알 수 없는 PAM 오류" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...시간이 촉박합니다...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...미안합니다, 시간이 다 되었습니다!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "잘못된 정보교환 (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s 실패함: 종료 코드 %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s 실패함: 시그널 발생 %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s 실패함: 알 수 없는 상태 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"사용법: %s [--dir /path/to/tally-directory] [--user <사용자이름>] [--reset] " +"[--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "로그인 실패횟수 최근 실패 접근 호스트\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "로그인에 %u번 실패하여 계정이 잠겼습니다." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(잠금 해제까지 %d분 남았습니다)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(잠금 해제까지 %d분 남았습니다)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s에서" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s에" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "최근 로그인:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "신규 계정에 오신 것을 환영합니다!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "최근 실패한 로그인:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "최근 로그인 후 로그인 시도를 %d번 실패했습니다." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "최근 로그인 후 로그인 시도를 %d번 실패했습니다." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s' 계정에 너무 많은 로그인 시도가 있었습니다." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "신규 전자우편이 없습니다." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "신규 전자우편이 있습니다." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "오래된 전자우편이 있습니다." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "전자우편이 있습니다." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "%s 폴더에 전자우편이 없습니다." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "%s 폴더에 신규 전자우편이 있습니다." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "%s 폴더에 오래된 전자우편이 있습니다." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "%s 폴더에 전자우편이 있습니다." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "디렉토리 '%s'를 생성 중." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "디렉토리 '%s'를 생성하고 초기화 할 수 없음." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "이미 사용하고 있는 비밀번호입니다. 다른 것을 사용하세요." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "이미 사용하고 있는 비밀번호입니다." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "기본 보안 문맥은 %s 입니다." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "다른 역할 또는 수준을 입력하시겠습니까?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "역할:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "%s 역할의 기본값 형식이 없습니다." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "수준:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "이는 유효한 보안 문맥이 아닙니다." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "%s를 위한 유효한 문맥을 가져올 수 없습니다." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "보안 문맥 %s가 할당되었습니다." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "키 생성 문맥 %s가 할당되었습니다." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM 초기화에 실패함\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() 실패에 실패함\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "로그인: 포킹 실패: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "접근이 허용되었습니다 (최종 접근은 %ld 초 전 이었습니다)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "당신의 계정이 만료되었습니다; 시스템 관리자에게 연락하세요." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "당신의 비밀번호를 긴급히 변경해야 합니다 (관리자 강제 사항)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "당신의 비밀번호를 긴급히 변경해야 합니다. (비밀번호가 만료되었습니다)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "경고: 비밀번호는 %d일 후 유효 기간이 만료됩니다." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "경고: 당신의 비밀번호는 %d일 내에 유효 기간이 만료됩니다." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS 비밀번호는 변경 될 수 없습니다." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "어떤 비밀번호를 제공되지 않았습니다." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "비밀번호가 변경되지 않았습니다." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "더 짧은 비밀번호를 선택해야 합니다." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "더 긴 비밀번호를 선택해야 합니다." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s를 위한 비밀번호 변경 중." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "당신의 비밀번호를 변경하려면 조금 더 기다려야 합니다." + +#~ msgid "You have no mail." +#~ msgstr "전자메일이 없습니다." + +#~ msgid "is the same as the old one" +#~ msgstr "이전 암호와 같음" + +#~ msgid "memory allocation error" +#~ msgstr "메모리 할당 오류 " + +#~ msgid "is a palindrome" +#~ msgstr "앞뒤 어느쪽에서 읽어도 같은 문맥임" + +#~ msgid "case changes only" +#~ msgstr "대소문자만 변경" + +#~ msgid "is too similar to the old one" +#~ msgstr "이전 암호와 유사함" + +#~ msgid "is too simple" +#~ msgstr "너무 간단함" + +#~ msgid "is rotated" +#~ msgstr "교체됨" + +#~ msgid "not enough character classes" +#~ msgstr "문자 클래스가 부족합니다 " + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "너무 많은 동일한 문자가 연속적으로 포함되어있습니다 " + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "너무 길게 단순한 문자가 연속적으로 포함되어 있습니다 " + +#~ msgid "contains the user name in some form" +#~ msgstr "어떠한 형식으로 사용자 이름을 포함합니다. " + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "잘못된 암호: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "일시적으로 계정이 잠금되었습니다 (%ld 초 남음) " + +#~ msgid "Authentication error" +#~ msgstr "인증 오류" + +#~ msgid "Service error" +#~ msgstr "서비스 오류" + +#~ msgid "Unknown user" +#~ msgstr "알 수 없는 사용자" + +#~ msgid "Unknown error" +#~ msgstr "알 수 없는 오류" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: 잘못된 숫자가 --reset=에 설정됨\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: 알려지지 않은 옵션 %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: 모든 사용자를 영이 아닌 값으로 설정할 수 없음\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/kw_GB.po b/po/kw_GB.po new file mode 100644 index 0000000..81f76ff --- /dev/null +++ b/po/kw_GB.po @@ -0,0 +1,499 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the Linux-PAM package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM 1.2.1\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: Cornish (United Kingdom)\n" +"Language: kw_GB\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural= (n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : " +"3\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" +msgstr[3] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "" diff --git a/po/ky.po b/po/ky.po new file mode 100644 index 0000000..f8dc5ae --- /dev/null +++ b/po/ky.po @@ -0,0 +1,497 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Kirgyz (http://www.transifex.com/projects/p/fedora/language/" +"ky/)\n" +"Language: ky\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Сиз кыска сөздү тандоо керек." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Сиз кыска сөздү тандоо керек." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Сиз кыска сөздү тандоо керек." diff --git a/po/lt.po b/po/lt.po new file mode 100644 index 0000000..180f5b7 --- /dev/null +++ b/po/lt.po @@ -0,0 +1,504 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Lithuanian (http://www.transifex.com/projects/p/fedora/" +"language/lt/)\n" +"Language: lt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n" +"%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Turite pasirinkti trumpesnį slaptažodį." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Turite pasirinkti trumpesnį slaptažodį." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Turite pasirinkti trumpesnį slaptažodį." diff --git a/po/lv.po b/po/lv.po new file mode 100644 index 0000000..74178ef --- /dev/null +++ b/po/lv.po @@ -0,0 +1,504 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Latvian (http://www.transifex.com/projects/p/fedora/language/" +"lv/)\n" +"Language: lv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : " +"2);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Jums jāizvēlas īsāka parole." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Jums jāizvēlas īsāka parole." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Jums jāizvēlas īsāka parole." diff --git a/po/meson.build b/po/meson.build new file mode 100644 index 0000000..bfd0937 --- /dev/null +++ b/po/meson.build @@ -0,0 +1,15 @@ +if not find_program('gettext', required: get_option('i18n')).found() + subdir_done() +endif + +i18n = import('i18n') +i18n.gettext( + meson.project_name(), + preset: 'glib', + data_dirs: '.', + args: [ + '--copyright-holder=Linux-PAM Project', + '--msgid-bugs-address=https://github.com/linux-pam/linux-pam/issues', + '--package-version=1.7.0', + ], +) diff --git a/po/mk.po b/po/mk.po new file mode 100644 index 0000000..6837db7 --- /dev/null +++ b/po/mk.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Macedonian (http://www.transifex.com/projects/p/fedora/" +"language/mk/)\n" +"Language: mk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Мора да изберете пократка лозинка." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Мора да изберете пократка лозинка." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Мора да изберете пократка лозинка." diff --git a/po/ml.po b/po/ml.po new file mode 100644 index 0000000..61065eb --- /dev/null +++ b/po/ml.po @@ -0,0 +1,590 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Ani Peter , 2007,2013 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2013-04-15 01:55-0400\n" +"Last-Translator: Ani Peter \n" +"Language-Team: Malayalam \n" +"Language: ml\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "അടയാളവാക്ക്: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "പുതിയ %s അടയാളവാക്ക്: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "പുതിയ അടയാളവാക്ക്: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "പുതിയ %s അടയാളവാക്ക്: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "പുതിയ അടയാളവാക്ക്: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "വീണ്ടും %s അടയാളവാക്ക് ടൈപ്പ് ചെയ്യുക: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "വീണ്ടും അടയാളവാക്ക് ടൈപ്പ് ചെയ്യുക: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "ക്ഷമിക്കണം, അടയാളവാക്കുകള്‍ തമ്മില്‍ ചേരുന്നില്ല." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s വീണ്ടും ടൈപ്പ് ചെയ്യുക" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "അടയാളവാക്ക് മാറ്റം വരുത്തുന്നതു് നിര്‍ത്തിയിരിക്കുന്നു." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "ലോഗിന്‍:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "വിജയിച്ചു" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "ഗുരുതരമായ പിശക് - ഉടന്‍ അവസാനിപ്പിക്കുക" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "മൊഡ്യൂള്‍ ലഭ്യമാക്കുന്നതില്‍ പരാജയപ്പെട്ടു" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "ചിഹ്നം ലഭ്യമായില്ല" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "സര്‍വീസ് മൊഡ്യൂളില്‍ പിഴവ്" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "സിസ്റ്റമില്‍ പിഴവ്" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "മെമ്മറി ബഫറില്‍ പിശക്" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "പ്രവേശനം അനുവദിക്കുന്നതല്ല" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "ആധികാരികത ഉറപ്പാക്കുന്നതില്‍ പിശക്" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "ആധികാരികത ഉറപ്പാക്കുന്നതിന് മതിയായ ഡേറ്റാ ലഭ്യമാല്ല" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "ആധികാരികത ഉറപ്പാക്കുന്ന സര്‍വീസിന്, ആവശ്യമുളള വിവരങ്ങള്‍ ലഭ്യമാക്കുവാന്‍ സാധ്യമായില്ല" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "പറഞ്ഞിരിക്കുന്ന ഓഥന്റിക്കേഷന്‍ മൊഡ്യൂളില്‍ ഉപയോക്താവു് ലഭ്യമല്ല" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "സര്‍വീസ് ഇനി ലഭ്യമാക്കുവാന്‍ സാധ്യമാകുന്നതല്ല" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "ഓഥന്റിക്കേഷന്‍ ടോക്കന്റെ കാലാവധി കഴിഞ്ഞിരിക്കുന്നു; പുതിയത് നല്‍കുക" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "ഉപയോക്താവിന്റെ അക്കൌണ്ടിന്റെ കാലാവധി അവസാനിച്ചിരിക്കുന്നു" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "പറഞ്ഞിരിക്കുന്ന സെഷന് ആവശ്യമുളള എന്‍ട്രി ഉണ്ടാക്കുവാനോ നീക്കുവാനോ സാധ്യമാകുന്നല്ല" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "ഓഥന്റിക്കേഷന്‍ സര്‍വീസിന് ഉപയോക്താവിന്റെ വിവരങ്ങള്‍ ലഭ്യമാക്കുവാന്‍ സാധ്യമായില്ല" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "ഉപയോക്താവിന്റെ ആനുകൂല്യങ്ങളുടെ കാലാവധി അവസാനിച്ചിരിക്കുന്നു" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "ഉപയോക്താവിനുള്ള ആനുകൂല്യങ്ങള്‍ ക്രമികരിക്കുന്നതില്‍ പരാജയപ്പെട്ടു" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "മൊഡ്യൂള്‍ അനുസരിച്ചുളള ഡേറ്റ ലഭ്യമല്ല" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item()-ലേക്ക് തെറ്റായ വസ്തു നല്‍കിയിരിക്കുന്നു" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "സംവാദത്തിലുളള പിശക്" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "ഓഫന്റിക്കേഷന്‍ ടോക്കന്‍ കൈകാര്യം ചെയ്യുന്നതില്‍ പിശക്" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "ഓഥന്റിക്കേഷന്‍ വിവരം വീണ്ടെടുക്കുവാന്‍ സാധ്യമല്ല" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "ഓഥന്റിക്കേഷന്‍ ടോക്കന്‍ ലോക്ക് ഉപയോഗത്തിലാണ്" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "ഓഥന്റിക്കേഷന്‍ ടോക്കന്‍ ഏജിങ് പ്രവര്‍ത്തന രഹിതമാണ്" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "അടയാളവാക്ക് സര്‍വീസിന്റെ ആദ്യത്തെ പരിശോധന പരാജയപ്പെട്ടു" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "തിരികെ നല്‍കുന്ന മൂല്ല്യം PAM ഡിസ്പാച്ച് അവഗണിക്കേണ്ടതാകുന്നു" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "മൊഡ്യൂള്‍ ലഭ്യമല്ല" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "ഓഥന്റിക്കേഷന്‍ ടോക്കന്റെ കാലാവധി അവസാനിച്ചിരിക്കുന്നു" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "സംവാദം ഒരു ഇവന്റിനായി കാത്തിരിക്കുന്നു" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "പ്രയോഗങ്ങള്‍ക്ക് വീണ്ടും libpam ആവശ്യമുണ്ട്" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "അപരിചിതമായ PAM പിശക്" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...സമയപരിധി അവസാനിക്കുന്നു...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...ക്ഷമിക്കണം, നിങ്ങളുടെ സമയം കഴിഞ്ഞിരിക്കുന്നു!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "തെറ്റായ സംവാദം (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s പരാ‍ജയപ്പെട്ടു: %d എന്ന കോഡില്‍ നിന്നും പുറത്ത് കടക്കുക" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s പരാ‍ജയപ്പെട്ടു: %d%s സിഗ്നല്‍ ലഭ്യമായിരിക്കുന്നു" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s പരാ‍ജയപ്പെട്ടു: അപരിചിതമായ 0x%x നിലവാരം" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Login Failures Latest failure From\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u പരാജയപ്പെട്ട ലോഗിനുകള്‍ കാരണം അക്കൌണ്ട് താല്‍ക്കാലികമായി പൂട്ടിയിരിക്കുന്നു" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s-ല്‍ നിന്നും" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s-ല്‍" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "അവസാനം ലോഗിന്‍ ചെയ്തത്:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "നിങ്ങളുടെ പുതിയ അക്കൌണ്ടിലേക്ക് സ്വാഗതം!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "അവസാനം ലോഗിന്‍ ചെയ്തതു് പരാജയപ്പെട്ടു:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "ശരിയായി അവസാനം ലോഗിന്‍ ചെയ്ത ശേഷം %d തവണ ലോഗിന്‍ പരാജയപ്പെട്ടു." +msgstr[1] "ശരിയായി അവസാനം ലോഗിന്‍ ചെയ്ത ശേഷം %d തവണ ലോഗിന്‍ പരാജയപ്പെട്ടു." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "ശരിയായി അവസാനം ലോഗിന്‍ ചെയ്ത ശേഷം %d തവണ ലോഗിന്‍ പരാജയപ്പെട്ടു." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s'-ന് അനവധി ലോഗിനുകള്‍." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "നിങ്ങള്‍ക്ക് പുതിയ മെയില്‍ ഉണ്ട്." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "നിങ്ങള്‍ക്ക് പുതിയ മെയില്‍ ഉണ്ട്." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "നിങ്ങള്‍ക്ക് പഴയ മെയില്‍ ഉണ്ട്." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "നിങ്ങള്‍ക്ക് മെയില്‍ ഉണ്ട്." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "%s ഫോള്‍ഡറില്‍ നിങ്ങള്‍ക്ക് മെയില്‍ ഇല്ല." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "%s ഫോള്‍ഡറില്‍ നിങ്ങള്‍ക്ക് പുതിയ മെയില്‍ ഉണ്ട്." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "%s ഫോള്‍ഡറില്‍ നിങ്ങള്‍ക്ക് പഴയ മെയില്‍ ഉണ്ട്." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "%s ഫോള്‍ഡറില്‍ നിങ്ങള്‍ക്ക് മെയില്‍ ഉണ്ട്." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "'%s' ഡയറക്ടറി ഉണ്ടാക്കുന്നു." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "%s ഡയറക്ടറി ഉണ്ടാക്കുവാനും ആരംഭിക്കുവാനും സാധ്യമായില്ല." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "അടയാളവാക്ക് നിലവില്‍ ഉപയോഗിത്തിലുള്ളതാണ്. മറ്റൊന്ന് നല്‍കുക." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "രഹസ്യവാക്ക് നിലവില്‍ ഉപയോഗിച്ചിരിയ്ക്കുന്നു." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "സ്വതവേയുള്ള സെക്യൂരിറ്റി കോണ്‍ടെക്സ്റ്റ് %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "നിങ്ങള്‍ക്കു് മറ്റൊരു ജോലി അല്ലെങ്കില്‍ നില നല്‍കണമോ?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ജോലി:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "%s ജോലിയ്ക്കു് സ്വതവേയുള്ള തരം ലഭ്യമല്ല\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "നില: " + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "ശരിയായ സെക്യൂരിറ്റി കോണ്‍ടെക്സ്റ്റ് അല്ല" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "%s എന്ന സെക്യൂരിറ്റി കോണ്‍ടെക്സ്റ്റ് നല്‍കിയിരിക്കുന്നു" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "കീ ഉണ്ടാക്കുന്നതിനുള്ള കോണ്‍ടെക്സ്റ്റ് ആയ %s നല്‍കിയിരിക്കുന്നു" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM ആരംഭിക്കുന്നതില്‍ പരാജയം\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() ചെയ്യുന്നതില്‍ പരാജയം\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: ഫോര്‍ക്ക് ചെയ്യുന്നതില്‍ പരാജയം: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "അനുവാദം നല്‍കിയിരിക്കുന്നു (ഒടുവില്‍ പ്രവേശിച്ചതു് %ld സെക്കന്‍ഡുകള്‍ക്കു് മുമ്പാണു്)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"നിങ്ങളുടെ അക്കൌണ്ടിന്റെ കാലാവധി അവസാനിച്ചിരിക്കുന്നു; ദയവായി സിസ്റ്റം അഡ്മിനിസ്ട്രേറ്ററുമായി " +"ബന്ധപ്പെടുക" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "നിങ്ങളുടെ അടയാളവാക്ക് ഉടനെ മാറ്റേണ്ടതുണ്ട് (root enforced)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "നിങ്ങളുടെ അടയാളവാക്ക് ഉടനെ മാറ്റേണ്ടതുണ്ട് (അടയാളവാക്ക് മാറ്റുന്നതിന് സമയമായി)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "മുന്നറിയിപ്പ്: നിങ്ങളുടെ അടയാളവാക്കിന്റെ കാലാവധി %d ദിവസത്തിനുള്ളില്‍ അവസാനിക്കുന്നു" +msgstr[1] "മുന്നറിയിപ്പ്: നിങ്ങളുടെ അടയാളവാക്കിന്റെ കാലാവധി %d ദിവസത്തിനുള്ളില്‍ അവസാനിക്കുന്നു" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "മുന്നറിയിപ്പ്: നിങ്ങളുടെ അടയാളവാക്കിന്റെ കാലാവധി %d ദിവസത്തിനുള്ളില്‍ അവസാനിക്കുന്നു" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS അടയാളവാക്ക് മാറ്റുവാന്‍ സാധ്യമാകുന്നില്ല." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "അടയാളവാക്ക് നല്‍കിയിട്ടില്ല" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS അടയാളവാക്ക് മാറ്റുവാന്‍ സാധ്യമാകുന്നില്ല." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "നിങ്ങൾ ഒരു ഹ്രസ്വ പാസ്‌വേഡ് തിരഞ്ഞെടുക്കണം." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "ഇതിലും വലിയ അടയാളവാക്ക് തിരഞ്ഞെടുക്കുക" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s-നുളള അടയാളവാക്ക് മാറ്റുന്നു." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "നിങ്ങളുടെ അടയാളവാക്ക് മാറ്റുന്നതിനായി ഇനിയും കാത്തിരിക്കേണ്ടതാണ്." + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "നിങ്ങള്‍ക്ക് പുതിയ മെയില്‍ ഉണ്ട്." + +#~ msgid "is the same as the old one" +#~ msgstr "പഴയത് പോലെ തന്നെയാകുന്നതു്" + +#~ msgid "memory allocation error" +#~ msgstr "മെമ്മറി അനുവദിയ്ക്കുന്നതില്‍ പിശക്" + +#~ msgid "is a palindrome" +#~ msgstr "ഒരു പാലിന്‍ഡ്രോം ആണു്" + +#~ msgid "case changes only" +#~ msgstr "അക്ഷരങ്ങളുടെ വലിപ്പം മാത്രം മാറുന്നതു്" + +#~ msgid "is too similar to the old one" +#~ msgstr "പഴയതിന് സാമ്യമുള്ളതു്" + +#~ msgid "is too simple" +#~ msgstr "സാധാരണയുള്ളതു്" + +#~ msgid "is rotated" +#~ msgstr "is rotated" + +#~ msgid "not enough character classes" +#~ msgstr "മതിയായ ക്യാരക്ടര്‍ ക്ലാസ്സുകള്‍ ലഭ്യമല്ല" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "അടുത്തടുത്ത് ഒരേപോലുള്ള അനവധി അക്ഷരങ്ങളുണ്ടു്" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "വളരെയധികം ഒരേ തരത്തിലുള്ള അക്ഷരങ്ങളടങ്ങുന്നു" + +#~ msgid "contains the user name in some form" +#~ msgstr "ഉപയോക്താവിന്റെ നാമം ഏതെങ്കിലും ഒരു തരത്തിലുണ്ടു്" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "തെറ്റായ അടയാളവാക്ക്: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "അക്കൌണ്ട് താല്‍ക്കാലികമായി പൂട്ടിയിരിക്കുന്നു (%ld നിമിഷങ്ങള്‍ ബാക്കി)" + +#~ msgid "Authentication error" +#~ msgstr "ആധികാരികത ഉറപ്പാക്കുന്നതില്‍ പിശക്" + +#~ msgid "Service error" +#~ msgstr "സര്‍വീസ് പിശക്" + +#~ msgid "Unknown user" +#~ msgstr "അപരിചിതമായ ഉപയോക്താവു്" + +#~ msgid "Unknown error" +#~ msgstr "അപരിചിതമായ പിശക്" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s:നല്‍കിയിരിക്കുന്ന നംബര്‍ തെറ്റാണ്, --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Unrecognised ഉപാധി %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: എല്ലാ ഉപയോക്താക്കള്‍ക്കും പൂജ്യം അല്ലാതെ ക്രമികരിക്കുവാന്‍ സാധ്യമല്ല\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/mn.po b/po/mn.po new file mode 100644 index 0000000..12b0ea5 --- /dev/null +++ b/po/mn.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Mongolian (http://www.transifex.com/projects/p/fedora/" +"language/mn/)\n" +"Language: mn\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Та богино нууц үг сонгох ёстой." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Та богино нууц үг сонгох ёстой." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Та богино нууц үг сонгох ёстой." diff --git a/po/mr.po b/po/mr.po new file mode 100644 index 0000000..c982dfc --- /dev/null +++ b/po/mr.po @@ -0,0 +1,590 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Sandeep Shedmake , 2008-2009 +# Sandeep Shedmake , 2008-2009, 2013 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2013-05-03 03:46-0400\n" +"Last-Translator: sandeeps \n" +"Language-Team: Marathi (http://www.transifex.com/projects/p/fedora/language/" +"mr/)\n" +"Language: mr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "गुप्तशब्द: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "नवीन गुप्तशब्द %s: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "नवीन गुप्तशब्द: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "नवीन गुप्तशब्द %s: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "नवीन गुप्तशब्द: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "नवीन गुप्तशब्द %s पुन्हा टाइप करा: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "नवीन गुप्तशब्द पुन्हा टाइप करा: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "माफ करा, गुप्तशब्द जुळत नाही." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s पुन्हा प्रविष्ट करा" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "परवलीचा शब्द रद्द केले." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "दाखलन:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "यश" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "गंभीर त्रुटी - लगेच रद्द करा" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "विभाग दाखल करण्यास अपयशी" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "बोधचिन्ह आढळले नाही" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "सेवा विभाग अंतर्गत त्रुटी आढळली" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "प्रणाली त्रुटी" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "स्मृती बफर त्रुटी" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "परवानगी नाही" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "अधिप्रमाणन अपयश" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "अधिप्रमाणन माहिती करीता प्रवेशसाठी अपुरे श्रेय" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "अधिप्रमाणन सेवा अधिप्रमाणन माहिती प्राप्त करू शकले नाही" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "अंतर्भूतीय अधिप्रमाणन विभाग करीता वापरकर्त्याची ओळख पटली नाही" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "सेवा करीताचे कमाल पुन्हप्रारंभ संख्या संपले" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "अधिप्रमाणन टोकन यापुढे वैध नाही; नवीन आवश्यक आहे" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "वापरकर्ता खाते कालबाह्य झाले" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "निर्देशीत सत्र करीता नोंदणी बनवू/काढून टाकू शकत नाही" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "अधिप्रमाणन सेवा वापरकर्ता श्रेय प्राप्त करू शकत नाही" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "वापरकर्ता श्रेय कालबाह्य झाले" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "वापरकर्ता श्रेय स्थापीत करतेवेळी अपयशी झाले" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "विभाग निर्देशीत माहिती उपलब्ध नाही" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item() करीता अयोग्य घटक पाठविले गेले" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "संवाद त्रुटी" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "अधिप्रमाणन टोकन सदोष त्रुटी" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "अधिप्रमाणन माहिती पुन्हा प्राप्त केली जाऊ शकत नाही" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "अधिप्रमाणन टोकन कुलूप व्यस्थ आहे" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "अधिप्रमाणन टोकन कालबाह्यता अकार्यान्वीत केले गेले" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "गुप्तशब्द सेवा करीता प्राथमिक तपास अपयशी" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "PAM ने रिटर्न मुल्य करीता दुर्लक्ष केले पाहिजे" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "विभाग अपरिचीत आहे" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "अधिप्रमाणन टोकन कालबाह्य झाले" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "संवाद घटनाच्या प्रतिक्षेत आहे" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "अनुप्रयोगास libpam ची आवश्चकता आहे" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "अपरिचीत PAM त्रुटी" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...वेळ फारच कमी आहे...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...माफ करा, तुमची वेळ समाप्त झाली आहे!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "सदोषीत संवाद (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s अपयशी: एक्जीट कोड %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s अपयशी: संकेत %d%s प्राप्त झाले" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s अपयशी: अपरिचीत स्थिती 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file रूटेड-फाइलनाव] [--user वापरकर्त्याचे नाव] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "प्रवेश अपयशी अलिकडील अपयश पासून\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u अपयशी प्रवेश मुळे खाते कुलूपबंद केले" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s पासून" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s वरील" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "शेवटचे दाखलन:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "नवीन खात्यावर स्वागत आहे!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "शेवटचे अपयशी दाखलन:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "शेवटचे यशस्वी प्रवेश पासून %d अपयशी प्रवेश प्रयत्न आढळले." +msgstr[1] "शेवटचे यशस्वी प्रवेश पासून %d अपयशी प्रवेश प्रयत्न आढळले गेले." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "शेवटचे यशस्वी प्रवेश पासून %d अपयशी प्रवेश प्रयत्न आढळले." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s' करीता एकापेक्षा जास्त प्रवेश." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "नवीन मेल प्राप्त झाले." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "नवीन मेल प्राप्त झाले." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "जुणे मेल आढळले गेले." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "मेल आढळले गेले." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "संचयीका %s अंतर्गत मेल आढळले नाही." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "संचयीका %s अंतर्गत नवीन मेल आढळले." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "संचयीका %s अंतर्गत जुणे मेल आढळले." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "संचयीका %s अंतर्गत मेल आढळले गेले." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "संचयीका '%s' बनवित आहे." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "डिरेक्ट्री '%s' बनवण्यास व प्रारंभ करण्यास अशक्य." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "ह्या गुप्तशब्दचा आधीच वापर झाला आहे. दुसरा निवडा." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "पासवर्ड आधिपासूनच वापरले आहे." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "मुलभूत सुरक्षा संदर्भ %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "तुम्हाला अन्य भूमिका किंवा स्तर प्रविष्ट करायला आवडेल?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "भूमिका:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "भूमिका %s करीता मुलभूत प्रकार आढळले नाही\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "स्तर:" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "वैध सुरक्षा संदर्भ नाही" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "सुरक्षा संदर्भ %s लागू केले गेले" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "कि निर्माण संदर्भ %s लागू केले गेले" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM आरंभण्यात अपयशी\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() कार्यान्वीत करण्यास अपयशी\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "दाखलन: विभाजन अपयशी: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "प्रवेश स्वीकारले (शेवटचा प्रवेश %ld सेकंद पूर्वी आढळला)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "तुमचे खाते बंद झाले आहे, कृपया तुमच्या संगणक व्यवस्थापकाकडे जा" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "तुमचा गुप्तशब्द तत्काळ बदलण्याची आवश्यकता आहे (रूट वापरा)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "तुमचा गुप्तशब्द तत्काळ बदलण्याची आवश्यकता आहे (गुप्तशब्द जुना आहे)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "सावधानता: तुमचे गुप्तशब्द %d दिवस अंतर्गत कालबाह्य होईल" +msgstr[1] "सावधानता: तुमचे गुप्तशब्द %d दिवस अंतर्गत कालबाह्य होईल" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "सावधानता: तुमचे गुप्तशब्द %d दिवसात कालबाह्य होईल" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS गुप्तशब्द बदलविले जाऊ शकत नाही." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "गुप्तशब्द दिलेला नाही" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS गुप्तशब्द बदलविले जाऊ शकत नाही." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "आपण लहान संकेतशब्द निवडणे आवश्यक आहे." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "तुम्ही मोठा गुप्तशब्द निवडला पाहीजे" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s करीता गुप्तशब्द बदलवित आहे." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "तुमचा गुप्तशब्द बदलण्यासाठी तुम्हाला बराच वेळ वाट पहावी लागेल" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "नवीन मेल प्राप्त झाले." + +#~ msgid "is the same as the old one" +#~ msgstr "प्रविष्ट केलेले जुण्या प्रमाणेच आहे" + +#~ msgid "memory allocation error" +#~ msgstr "मेमरि वाटपवेळी त्रुटी" + +#~ msgid "is a palindrome" +#~ msgstr "पॅलींड्रोम आहे" + +#~ msgid "case changes only" +#~ msgstr "फक्त आकार बदलाव" + +#~ msgid "is too similar to the old one" +#~ msgstr "प्रविष्ट केलेले जुण्या नुरूपच आहे" + +#~ msgid "is too simple" +#~ msgstr "खूपच सोपे आहे" + +#~ msgid "is rotated" +#~ msgstr "स्तर बदलविले गेले" + +#~ msgid "not enough character classes" +#~ msgstr "अतिरिक्त अक्षर गट उपलब्ध नाही" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "पाठोपाठ खूप जास्त समान अक्षर आढळले" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "मोनोटोनिक अक्षर श्रृंखला खूपच लांब आहे" + +#~ msgid "contains the user name in some form" +#~ msgstr "कुठल्यातरी स्वरूपात वापरकर्ता नाव आढळले" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "अयोग्य गुप्तशब्द: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "खातं तात्पूर्ते कुलूपबंद केले (%ld सेकंद शिल्लक)" + +#~ msgid "Authentication error" +#~ msgstr "अधिप्रमाणन त्रुटी" + +#~ msgid "Service error" +#~ msgstr "सेवा त्रुटी" + +#~ msgid "Unknown user" +#~ msgstr "अपरिचीत वापरकर्ता" + +#~ msgid "Unknown error" +#~ msgstr "अपरिचित चूक" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset= करीता अयोग्य संख्या पुरविली गेली\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: अपरिचीत पर्याय %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file रूटेड-फाइलनाव] [--user वापरकर्त्याचे नाव] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: सर्व वापरकर्ता विना-शून्य असे पुन्हस्थापन करू शकत नाही\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ms.po b/po/ms.po new file mode 100644 index 0000000..a441b08 --- /dev/null +++ b/po/ms.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Sharuzzaman Ahmat Raslan , 2008. +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-29 07:01-0500\n" +"Last-Translator: Tomáš Mráz \n" +"Language-Team: LANGUAGE \n" +"Language: ms\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "(semasa) katalaluan UNIX:" + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "(semasa) katalaluan UNIX:" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, fuzzy, c-format +msgid "New %s password: " +msgstr "(semasa) katalaluan UNIX:" + +#: libpam/pam_get_authtok.c:46 +#, fuzzy +msgid "New password: " +msgstr "(semasa) katalaluan UNIX:" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, fuzzy, c-format +msgid "Retype new %s password: " +msgstr "(semasa) katalaluan UNIX:" + +#: libpam/pam_get_authtok.c:49 +#, fuzzy +msgid "Retype new password: " +msgstr "(semasa) katalaluan UNIX:" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Anda mesti memilih kata laluan yang lebih pendek." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Anda mesti memilih kata laluan yang lebih pendek." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Menukar katalaluan untuk %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Anda mesti memilih kata laluan yang lebih pendek." diff --git a/po/my.po b/po/my.po new file mode 100644 index 0000000..fba19ac --- /dev/null +++ b/po/my.po @@ -0,0 +1,497 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Burmese (http://www.transifex.com/projects/p/fedora/language/" +"my/)\n" +"Language: my\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "သင်တစ်ဦးပိုမိုတိုတောင်းသောစကားဝှက်ကိုရွေးချယ်ရပါမည်။" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "သင်တစ်ဦးပိုမိုတိုတောင်းသောစကားဝှက်ကိုရွေးချယ်ရပါမည်။" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "သင်တစ်ဦးပိုမိုတိုတောင်းသောစကားဝှက်ကိုရွေးချယ်ရပါမည်။" diff --git a/po/nb.po b/po/nb.po new file mode 100644 index 0000000..c3c04af --- /dev/null +++ b/po/nb.po @@ -0,0 +1,582 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Kjartan Maraas , 2008,2012-2013 +# Olav Pettershagen , 2005-2006 +# Tomáš Mráz , 2016. #zanata +# Allan Nordhøy , 2020. +# Dmitry V. Levin , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-05-17 18:48+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Norwegian Bokmål \n" +"Language: nb\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.0.4\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Passord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Nåværende %s-passord: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Nåværende passord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nytt %s-passord: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nytt passord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Bekreft nytt %s-passord: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Bekreft nytt passord: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Beklager, ikke samsvar mellom passord." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Skriv %s på nytt" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Bytte av passord avbrutt." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "logg inn:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Utført" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritisk feil - avbryter straks" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Klarte ikke å laste modul" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbol ikke funnet" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Feil i tjenestemodul" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Systemfeil" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Minnebufferfeil" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Tillatelse avvist" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Autentiseringsfeil" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Utilstrekkelige rettigheter for tilgang til autentiseringsinformasjon" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Autentiseringstjenesten kan ikke hente autentiseringsinformasjon" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Bruker ukjent for autentiseringsmodul" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Maksimalt antall forsøk er overskredet for tjenesten" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Autentiseringsjide ikke lenger gyldig; ny kode kreves" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Brukerkonto er utløpt" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Kan ikke opprette/fjerne en oppføring for den valgte økten" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Autentiseringstjenesten kan ikke hente brukerrettigheter" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Brukerrettigheter utløpt" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Feil under definisjon av brukerrettigheter" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Nei modulspesifikk informasjon finnes" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Ugyldig oppføring angitt for pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Dialogfeil" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Manipulasjonsfeil for autentiseringskode" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Autentiseringsinformasjon kan ikke gjenopprettes" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Låsing av autentiseringskode opptatt" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Tidsbegrensning av autentiseringskode deaktivert" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Passordtjenestens innledende kontroll mislyktes" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Resultatverdien bør ignoreres av PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modulen er ukjent" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Autentiseringskode utløpt" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Dialogen venter på hendelse" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Programmet må spørre libpam på nytt" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Ukjent PAM-feil" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Tiden er i ferd med utløpe..\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Beklager, tiden er utløpt!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "mislykket dialog (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s feilet: sluttkode %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s feilet: fikk signal %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s feilet: ukjent status 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "Bruk: %s [--dir /sti/til/tally-mappe] [--user brukernavn] [--reset]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Brukernavn Feil Siste feil Fra\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Konto låst som følge av %u mislykkede innlogginger." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, fuzzy, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minutter igjen til å låse opp)" +msgstr[1] "(%d minutter igjen til å låse opp)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minutter igjen til å låse opp)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " fra %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " på %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Siste innlogging:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Velkommen til din nye konto!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Siste feilede innlogging:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Det har vært %d feilet innloggingsforsøk siden forrige innlogging uten feil." +msgstr[1] "" +"Det har vært %d feilede innloggingsforsøk siden siste innlogging uten feil." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Det har vært %d feilede innloggingsforsøk siden siste innlogging uten feil." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "For mange innlogginger for «%s»." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Du har fått ny e-post." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Du har fått ny e-post." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Du har ulest e-post." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Du har fått e-post." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Du har ingen e-post i mappen %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Du har ny e-post i mappen %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Du har ulest e-post i mappen %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Du har e-post i mappen %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Oppretter katalog «%s»." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Kan ikke lage og initiere katalog «%s»." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Passordet er allerede benyttet. Velg et annet." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Passordet har allerede vært brukt." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Forvalgt sikkerhetskontekst er %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Vil du angi en annen rolle eller nivå?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rolle:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Ingen forvalgt type for rollen %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nivå:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Dette er ikke en gyldig sikkerhetskontekst." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Kunne ikke innhente gyldig kontekst for %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Sikkerhetskontekst «%s» tilordnet." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Kontekst «%s» for oppretting av nøkkel tilordnet." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "kunne ikke initialisere PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "kunne ikke pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: feil under forgrening: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Tilgang innvilget (siste tilgang var for %ld sekunder siden)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Din konto er utløpt; kontakt systemadministratoren din." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Du må straks endre passordet ditt (påkrevd av administrator)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Du må straks endre passordet ditt (passordet har utløpt)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Advarsel: Passordet ditt utløper om %d dag." +msgstr[1] "Advarsel: Passordet ditt utløper om %d dager." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Advarsel: Passordet ditt utløper om %d dager." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS-passord kunne ikke endres." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Inget passord har blitt angitt." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Passordet kunne ikke endres." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Du må velge et kortere passord." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Du må velge et lengre passord." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Endrer passord for %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Du må vente lenger før du kan endre passordet ditt." + +#~ msgid "You have no mail." +#~ msgstr "Du har ikke fått noen e-post." + +#~ msgid "is the same as the old one" +#~ msgstr "er det samme som det gamle" + +#~ msgid "memory allocation error" +#~ msgstr "feil med minneallokering" + +#~ msgid "is a palindrome" +#~ msgstr "er et palindrom" + +#~ msgid "case changes only" +#~ msgstr "kun endring av små/store bokstaver" + +#~ msgid "is too similar to the old one" +#~ msgstr "er for likt det gamle" + +#~ msgid "is too simple" +#~ msgstr "er for enkelt" + +#~ msgid "is rotated" +#~ msgstr "er rotert" + +#~ msgid "not enough character classes" +#~ msgstr "ikke nok tegnklasser" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "inneholder for mange like tegn etter hverandre" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "inneholder for lang monoton tegnsekvens" + +#~ msgid "contains the user name in some form" +#~ msgstr "inneholder brukernavnet i en eller annen form" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "SVAKT PASSORD: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Konto midlertidig låst (%ld sekunder igjen)." + +#~ msgid "Authentication error" +#~ msgstr "Autentiseringsfeil" + +#~ msgid "Service error" +#~ msgstr "Tjenestefeil" + +#~ msgid "Unknown user" +#~ msgstr "Ukjent bruker" + +#~ msgid "Unknown error" +#~ msgstr "Ukjent feil" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Ugyldig tall angitt for --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Ukjent valg %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filnavn] [--user brukernavn] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Kan ikke tilbakestille alle brukere til non-zero\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filnavn] [--file rooted-filnavn]\n" +#~ "[-u brukernavn] [--user brukernavn]\n" +#~ "[-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ne.po b/po/ne.po new file mode 100644 index 0000000..6804e28 --- /dev/null +++ b/po/ne.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Nepali (http://www.transifex.com/projects/p/fedora/language/" +"ne/)\n" +"Language: ne\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "तपाईंले छोटो पासवर्ड छान्नु पर्छ।" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "तपाईंले छोटो पासवर्ड छान्नु पर्छ।" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "तपाईंले छोटो पासवर्ड छान्नु पर्छ।" diff --git a/po/nl.po b/po/nl.po new file mode 100644 index 0000000..6d1e4f1 --- /dev/null +++ b/po/nl.po @@ -0,0 +1,591 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Geert Warrink , 2010,2012, 2020. +# hamaryns , 2013 +# Peter van Egdom , 2008 +# Reinout van Schouwen , 2013 +# Richard E. van der Luit , 2009 +# R.F. Pels , 2005 +# Rinse de Vries , 2005-2006 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020, 2021. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2021-07-22 00:54+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Dutch \n" +"Language: nl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.7.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Wachtwoord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Huidig %s wachtwoord: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Huidig wachtwoord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nieuw %s wachtwoord: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nieuw wachtwoord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Nieuw %s wachtwoord herhalen: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Nieuw wachtwoord herhalen: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Sorry, wachtwoorden komen niet overeen." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Voer %s opnieuw in" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Wachtwoord wijzigen is afgebroken." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "gebruikersnaam:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Succes" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritieke fout ­– onmiddellijk afbreken" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Laden van module mislukt" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbool niet gevonden" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Fout in servicemodule" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Systeemfout" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Fout in geheugenbuffer" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Toegang geweigerd" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Authenticatiefout" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Onvoldoende legitimatie voor toegang tot authenticatiedata" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Authenticatieservice kan geen authenticatie-informatie ophalen" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Gebruiker onbekend bij de onderliggende authenticatiemodule" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Maximum aantal herhaalpogingen voor service overschreden" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Authenticatie-token is niet langer geldig; nieuwe is vereist" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Gebruikersaccount is verlopen" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Kan een ingang niet aanmaken/verwijderen voor de opgegeven sessie" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"Authenticatieservice kan geen legitimatiegegevens van gebruiker ophalen" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Legitimatiegegevens van gebruiker verlopen" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Fout tijdens het instellen van legitimatiegegevens van gebruiker" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Geen modulespecifieke data aanwezig" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Onjuist item doorgegeven aan pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Conversatiefout" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Fout bij manipulatie van authenticatie-token" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Authenticatie-informatie kan niet worden hersteld" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Vergrendeling van authenticatie-token is bezig" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Veroudering van authenticatie-token is uitgeschakeld" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Voorlopige controle door de wachtwoordservice is mislukt" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "PAM-dispatch dient de teruggegeven waarde te negeren" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Module is onbekend" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Authenticatie-token is verlopen" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Conversatie wacht op een gebeurtenis" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Toepassing moet libpam nogmaals aanroepen" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Onbekende PAM-fout" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "…De tijd raakt op…\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "…Sorry, uw tijd is verlopen!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "foutieve conversatie (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s is mislukt: afsluitcode %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s is mislukt: signaal %d%s ontvangen" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s is mislukt: onbekende status 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Gebruik: %s [--dir /pad/naar/tally-directory] [--user gebruikersnaam] [--" +"reset]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Aanmelding Mislukte Laatst mislukte Van\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Het account is vergrendeld wegens %u mislukte aanmeldingen." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(nog %d minuut om te ontgrendelen)" +msgstr[1] "(nog %d minuten om te ontgrendelen)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(nog %d minuten om te ontgrendelen)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %e %b %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " van %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " op %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Laatste aanmelding:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Welkom bij je nieuwe account!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Laatste mislukte aanmeldpoging:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Er was %d mislukte aanmeldpoging sinds de laatste succesvolle aanmelding." +msgstr[1] "" +"Er waren %d mislukte aanmeldpogingen sinds de laatste succesvolle aanmelding." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Er waren %d mislukte aanmeldpogingen sinds de laatste succesvolle aanmelding." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Te veel aanmeldingen voor '%s'." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Je hebt nieuwe e-mail." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Je hebt nieuwe e-mail." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Je hebt oude e-mail." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Je hebt e-mail." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Je hebt geen e-mail in map %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Je hebt nieuwe e-mail in map %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Je hebt oude e-mail in map %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Je hebt e-mail in map %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Aanmaken van map '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Niet in staat om map '%s' aan te maken." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Wachtwoord is al eens gebruikt. Kies een ander wachtwoord." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Wachtwoord is al eens gebruikt." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "De standaard beveiligingscontext is %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Wil je een andere rol of een ander niveau invoeren?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rol:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Er is geen standaardtype voor rol %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "niveau:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Dit is geen geldige beveiligingscontext." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Een geldige context voor %s kon niet worden verkregen." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Beveiligingscontext %s is toegewezen." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Sleutelaanmaakcontext %s is toegewezen." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "initialiseren van PAM is mislukt\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() is mislukt\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "aanmelding: beginnen van nieuw proces mislukt: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Toegang is verleend (laatste toegang was %ld seconden geleden)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Je account is verlopen; neem contact op met je systeembeheerder." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Je moet onmiddellijk je wachtwoord wijzigen (op last van systeembeheerder)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Je moet onmiddellijk je wachtwoord wijzigen (wachtwoord is verouderd)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Waarschuwing: je wachtwoord zal binnen %d dag verlopen." +msgstr[1] "Waarschuwing: je wachtwoord zal binnen %d dagen verlopen." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Waarschuwing: je wachtwoord zal binnen %d dagen verlopen." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS-wachtwoord kon niet worden gewijzigd." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Geen wachtwoord opgegeven." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Het wachtwoord is niet gewijzigd." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Je moet een korter wachtwoord kiezen." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Je moet een langer wachtwoord kiezen." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Veranderen van wachtwoord voor %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Je moet langer wachten om je wachtwoord te wijzigen." + +#~ msgid "You have no mail." +#~ msgstr "Je hebt geen e-mail." + +#~ msgid "is the same as the old one" +#~ msgstr "is hetzelfde als het oude" + +#~ msgid "memory allocation error" +#~ msgstr "geheugentoekenningsfout" + +#~ msgid "is a palindrome" +#~ msgstr "is een palindroom" + +#~ msgid "case changes only" +#~ msgstr "alleen veranderingen in hoofd-/kleine letters" + +#~ msgid "is too similar to the old one" +#~ msgstr "lijkt te veel op het oude" + +#~ msgid "is too simple" +#~ msgstr "is te eenvoudig" + +#~ msgid "is rotated" +#~ msgstr "is omgedraaid" + +#~ msgid "not enough character classes" +#~ msgstr "onvoldoende tekensoorten" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "bevat teveel dezelfde opeenvolgende karakters" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "bevat een te lange monotone tekenreeks" + +#~ msgid "contains the user name in some form" +#~ msgstr "bevat de gebruikersnaam in een of andere vorm" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "SLECHT WACHTWOORD: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Het account is tijdelijk vergrendeld (%ld seconden resterend)." + +#~ msgid "Authentication error" +#~ msgstr "Authenticatiefout" + +#~ msgid "Service error" +#~ msgstr "Servicefout" + +#~ msgid "Unknown user" +#~ msgstr "Onbekende gebruiker" + +#~ msgid "Unknown error" +#~ msgstr "Onbekende fout" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Onjuist getal gegeven aan --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Onbekende optie %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s [--file rooted-bestandsnaam] [--user gebruikersnaam] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: kan niet alle gebruikers terugzetten naar non-zero\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-bestandsnaam] [--file rooted-bestandsnaam]\n" +#~ " [-u gebruikersnaam] [--user gebruikersnaam]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/nn.po b/po/nn.po new file mode 100644 index 0000000..c487189 --- /dev/null +++ b/po/nn.po @@ -0,0 +1,507 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +# Andreas-Johann Ø Ulvestad , 2021. +# Adaline Simonian , 2024. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2024-05-02 21:36+0000\n" +"Last-Translator: Adaline Simonian \n" +"Language-Team: Norwegian Nynorsk \n" +"Language: nn\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.5.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Passord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Noverande %s-passord: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Noverande passord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nytt %s-passord: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nytt passord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Stadfest nytt %s-passord: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Stadfest nytt passord: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Beklagar, passorda er ikkje like." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Skriv %s på nytt" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Skifte av passord vart avbrote." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "logg inn:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Utføyrd" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kristisk feil - avbryt med ein gong" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Klarte ikkje å laste modul" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Fann ikkje symbol" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Feil i tenestemodul" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Systemfeil" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Minnebufferfeil" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Nekta tilgang" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Autentiseringsfeil" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Utilstrekkelege rettar for tilgang til informasjon om autentisering" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Teneste for autentisering kan ikkje hente informasjon om autentisering" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Brukar udkjend for autentiseringsmodul" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Maks tal på forsøk er overskride for tenesta" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Autentiseringspollett er ikkje lenger gyldig; ny kode kravd" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Brukarkonto er utgått" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Kan ikkje opprette/fjerne oppføring for vald økt" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Autentiseringsteneste kan ikkje hente brukarrettar" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Brukarrettar er utløpt" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Feil under definering av brukarrettar" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Ingen modulspesifikk data finst" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Ugyldig oppføring spesifisert for pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Dialogfeil" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Manipulasjonsfeil for autentiseringspollett" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Autentiseringsinformasjon kan ikkje bli gjenoppretta" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Låsing av autentiseringskode er oppteken" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Tidsavgrensing for autentiseringspollett er deaktivert" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Posstordtenesta sin innledande kontroll feila" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Resultatverdien bør bli ignorert av PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modulen er ukjend" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Autentiseringspollett er utgått" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Dialogen ventar på hending" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Programmet må spørre libpam på ny" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Ukjend PAM-feil" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Tida held på å gå ut...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Beklagar, tida er gått ut\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "mislykka dialog (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s feila: sluttkode %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s feila: fekk signal %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s feila: ukjend status 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Bruk: %s [--dir /sti/til/tally-mappe] [--user brukarnamn] [--reset] [--" +"legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Konto er låst som følgje av %u mislukka innloggingar." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, fuzzy, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minutt står att for å låse opp)" +msgstr[1] "(%d minutt står att for å låse opp)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minutt står att for å låse opp)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " frå %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " på %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Siste innlogging:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Velkommen til din nye konto!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Siste feila innlogging:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Det har vore %d feila innloggingsforsøk utan feil sidan førre innlogging." +msgstr[1] "" +"Det har vore %d feila innloggingsforsøk utan feil sidan førre innlogging." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Det har vore %d feila innloggingsforsøk sidan sist innlogging utan feil." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "For mange innlogginga for «%s»." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Du har ikkje fått nokon ny e-post." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Du har fått ny e-post." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Du har ulest e-post." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Du har fått e-post." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Du har ingan e-post i mappen %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Du har ny e-post i mappa %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Du har ulest e-post i mappa %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Du har e-post i mappa %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Opprettar kataloge «%s»." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Kan ikkje lage og initiere katalog «%s»." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Passord er allereie nytta. Vel eit anna." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Passordet har allereie vore brukt." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Forehandvald sikkerheitskontekst er %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Vil du angje ei anna rolle eller nivå?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rolle:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Inga førehandvald type for rolla %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nivå:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Dette er ikkje gyldig sikkerheitskontekst." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Kunne ikkje innhente gyldig kontekst for %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Sikkerheitskontekst «%s» tilordna." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Kontekst «%s» for oppretting av nøkkel er tilordna." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "Kunne ikkje initiere PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "kunne ikkje pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: feil under forgreining: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Tilgang innvilga (siste tilgang var for %ld sekund sidan)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Kontoen din er utløpt; kontakt systemadministratoren din." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Du må straks endre passordet ditt (kravd av administrator)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Du må straks endre passordet ditt (passordet har utløpt)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Advarsel: Passordet ditt utløper om %d dag." +msgstr[1] "Advarsel: Passordet ditt utløper om %d dagar." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Varsel: Passordet ditt går ut om %d dagar." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS-passord kunne ikkje bli endra." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Inga passord er blitt angjeve." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Passordet kunne ikkje bli endra." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Du må velje eit kortare passord." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Du må velje eit kortare passord." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Skiftar passord for %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Du må vente lengre før du kan skifte passordet ditt." + +#~ msgid "You have no mail." +#~ msgstr "Du har ikkje fått nokon e-post." diff --git a/po/or.po b/po/or.po new file mode 100644 index 0000000..a1c8259 --- /dev/null +++ b/po/or.po @@ -0,0 +1,581 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Manoj Kumar Giri , 2008-2009,2012 +# Manoj Kumar Giri , 2013 +# Subhransu Behera , 2007 +# Tomáš Mráz , 2016. #zanata +# Ankit Behera , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-04-13 22:40+0000\n" +"Last-Translator: Ankit Behera \n" +"Language-Team: Odia \n" +"Language: or\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.11.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "ପ୍ରବେଶ ସଙ୍କେତ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "ସାମ୍ପ୍ରତିକ %s ପ୍ରବେଶ ସଙ୍କେତ: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "ସାମ୍ପ୍ରତିକ ପ୍ରବେଶ ସଙ୍କେତ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "ନୂତନ %s ପ୍ରବେଶ ସଙ୍କେତ: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "ନୂତନ ପ୍ରବେଶ ସଙ୍କେତ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "ନୂତନ %s ପ୍ରବେଶ ସଙ୍କେତକୁ ପୁନର୍ବାର ଟାଇପ କରନ୍ତୁ: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "ନୂତନ ପ୍ରବେଶ ସଙ୍କେତକୁ ପୁନର୍ବାର ଟାଇପ କରନ୍ତୁ: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "କ୍ଷମା କରିବେ, ପ୍ରବେଶ ସଙ୍କେତ ମିଶୁ ନାହିଁ।" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%sକୁ ପୁନର୍ବାର ଟାଇପ କରନ୍ତୁ" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "ପ୍ରବେଶ ସଙ୍କେତ ପରିବର୍ତ୍ତନକୁ ପ୍ରତ୍ୟାଖାନ କରାଯାଇଛି।" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "ଲଗଇନ:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "ସଫଳତା" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "ଗୁରୁତର ତୃଟି - ଏହା ବିଷୟରେ ଶୀଘ୍ର ଜଣାନ୍ତୁ" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "ଏକକାଂଶକୁ ଧାରଣ କରିବାରେ ବିଫଳ" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "ପ୍ରତୀକ ମିଳିଲା ନାହିଁ" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "ସେବା ଏକକାଂଶରେ ତୃଟି" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "ତନ୍ତ୍ର ତୃଟି" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "ସ୍ମୃତି ସଞ୍ଚୟ ତୃଟି" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "ଅନୁମତି ଅସ୍ବୀକୃତ" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "ବୈଧିକରଣ ବିଫଳ" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "ବୈଧିକରଣ ତଥ୍ଯକୁ ଅଭିଗମ କରିବା ପାଇଁ ଅଯେଥଷ୍ଟ ପ୍ରମାଣ ପତ୍ର" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "ବୈଧିକରଣ ସେବା ବୈଧିକରଣ ସୂଚନା ପୁନରୁଦ୍ଧାର କରିପାରିବ ନାହିଁ" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "ନିମ୍ନଲିଖିତ ବୈଧିକରଣ ଏକକାଂଶକୁ ଜଣା ନ ଥିବା ଚାଳକ" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "ସେବା ପାଇଁ ସର୍ବାଧିକ ସଂଖ୍ଯକ ପୁନଃପ୍ରଚେଷ୍ଟା ପୂର୍ଣ୍ଣ ମାତ୍ରାରେ ସମାପ୍ତ ହୋଇଯାଇଛି" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "ବୈଧିକରଣ ଟୋକନ ଆଉ ଏବେ ବୈଧ ନୁହେଁ; ଗୋଟିଏ ନୂତନ ଟୋକନ ଆବଶ୍ଯକ" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "ଚାଳକ ହିସାବ ଖାତା ଅଚଳ ହୋଇଯାଇଛି" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "ଉଲ୍ଲେଖିତ ଅଧିବେଶନ ପାଇଁ ଗୋଟିଏ ପ୍ରବିଷ୍ଟି ପ୍ରସ୍ତୁତ/ଅପସାରଣ କରିପାରିବ ନାହିଁ" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "ବୈଧିକରଣ ସେବା ଚାଳକ ପ୍ରମାଣ ପତ୍ର ମାନଙ୍କୁ ପୁନରୁଦ୍ଧାର କରିପାରିବ ନାହିଁ" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "ଚାଳକ ପ୍ରମାଣ ପତ୍ର ଅଚଳ ହୋଇଯାଇଛି" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "ଚାଳକ ପ୍ରମାଣ ପତ୍ର ବିନ୍ଯାସ କରିବାରେ ବିଫଳ" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "କୌଣସି ଏକକାଂଶ ନିର୍ଦ୍ଦିଷ୍ଟ ତଥ୍ଯ ନାହିଁ" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "ଖରାପ ବସ୍ତୁକୁ pam_*_item() କୁ ପଠାଯାଇଛି" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "କଥୋପକଥନ ତୃଟି" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "ବୈଧିକରଣ ଟୋକନ ବ୍ଯବହାର କୌଶଳ ସାଧନରେ ତୃଟି" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "ବୈଧିକରଣ ସୂଚନା ପୁନର୍ଲାଭ କରିପାରିବ ନାହିଁ" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "ବୈଧିକରଣ ଟୋକନ ତାଲା ବ୍ଯସ୍ତ ଅଛି" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "ବୈଧିକରଣ ଟୋକନ କାଳ ବର୍ଦ୍ଧନ ପ୍ରକ୍ରିୟାକୁ ନିଷ୍କ୍ରିୟ କରାଯାଇଛି" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "ପ୍ରବେଶ ସଙ୍କେତ ସେବା ଦ୍ବାରା ପ୍ରାଥମିକ ପରୀକ୍ଷଣ ବିଫଳ" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "ଫେରସ୍ତ ମୂଲ୍ଯକୁ PAM ପ୍ରେରଣ ଦ୍ବାରା ଆଗ୍ରହ୍ଯ କରାଯାଇଛି" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "ଏକକାଂଶଟି ଅଜଣା ଅଟେ" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "ବୈଧିକରଣ ଟୋକନ ଅଚଳ ହୋଇଯାଇଛି" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "କଥୋପକଥନ ଘଟଣା ପାଇଁ ଅପେକ୍ଷା କରୁଅଛି" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "ପ୍ରୟୋଗ libpam କୁ ପୁନର୍ବାର ଆହ୍ବାନ କରିବା ଦରକାର" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "ଅଜଣା PAM ତୃଟି" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...ସମୟ ସମାପ୍ତ ହେଉଛି...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...କ୍ଷମା କରିବେ, ଆପଣଙ୍କ ସମୟ ସମାପ୍ତ ହୋଇଯାଇଛି!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "ତୃଟିପୂର୍ଣ୍ଣ କଥୋପକଥନ (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s ବିଫଳ: %d ସଙ୍କେତରୁ ପ୍ରସ୍ଥାନ କରୁଅଛି" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s ବିଫଳ: %d%s ସଙ୍କେତ ପାଇଲା" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s ବିଫଳ: ଅଜଣା ଅବସ୍ଥିତି 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "ଲଗଇନ ବିଫଳତାର ନୂତନତମ ବିଫଳତା ରୁ\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u ବିଫଳତା ଲଗଇନ କାରଣରୁ ଖାତା ଅପରିବର୍ତ୍ତନଶୀଳ ହୋଇଯାଇଛି।" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s ରୁ" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s ରେ" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "ଅନ୍ତିମ ଲଗଇନ:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "ଆପଣଙ୍କ ନୂତନ ଖାତାରେ ଆପଣଙ୍କ ସ୍ବାଗତ!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "ଅନ୍ତିମ ବିଫଳ ଲଗଇନ:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "ଅନ୍ତିମ ସଫଳ ଲଗଇନ ପରଠାରୁ %d ଟି ବିଫଳ ଲଗଇନ ପ୍ରଚେଷ୍ଟା କରାଯାଇଛି।" +msgstr[1] "ଅନ୍ତିମ ସଫଳ ଲଗଇନ ପରଠାରୁ %d ଟି ବିଫଳ ଲଗଇନ ପ୍ରଚେଷ୍ଟା କରାଯାଇଛି।" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "ଅନ୍ତିମ ସଫଳ ଲଗଇନ ପରଠାରୁ %d ଟି ବିଫଳ ଲଗଇନ ପ୍ରଚେଷ୍ଟା କରାଯାଇଛି।" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s' ପାଇଁ ଅତ୍ଯଧିକ ସଂଖ୍ଯକ ଲଗଇନ ହୋଇଛି।" + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "ଆପଣଙ୍କ ପାଇଁ ଗୋଟିଏ ନୂଆ ଚିଠି ଆସିଛି।" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "ଆପଣଙ୍କ ପାଇଁ ଗୋଟିଏ ନୂଆ ଚିଠି ଆସିଛି।" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "ଆପଣଙ୍କ ନିକଟରେ ଗୋଟିଏ ପୁରୁଣା ଚିଠି ଅଛି।" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "ଆପଣଙ୍କ ନିକଟରେ ଚିଠି ଅଛି।" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "ଆପଣଙ୍କ ନିକଟରେ %s ଫୋଲଡରରେ କୌଣସି ଚିଠି ନାହିଁ।" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "ଆପଣଙ୍କ ନିକଟରେ %s ଫୋଲଡରରେ ନୂଆ ଚିଠି ଅଛି।" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "ଆପଣଙ୍କ ନିକଟରେ %s ଫୋଲଡରରେ ପୁରୁଣା ଚିଠି ଅଛି।" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "ଆପଣଙ୍କ ନିକଟରେ %s ଫୋଲଡରରେ ଚିଠି ଅଛି।" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "ଡ଼ିରେକ୍ଟୋରୀ '%s' ନିର୍ମାଣ କରୁଅଛି." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "ଡ଼ିରେକ୍ଟୋରୀ '%s'କୁ ନିର୍ମାଣ ଏବଂ ପ୍ରାରମ୍ଭ କରିବାରେ ଅସମର୍ଥ।" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "ପ୍ରବେଶ ସଙ୍କେତଟି ପୂର୍ବରୁ ବ୍ଯବହୃତ ହେଉଛି। ଅନ୍ଯ ଗୋଟିଏ ପ୍ରବେଶ ସଙ୍କେତ ଚୟନ କରନ୍ତୁ।" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "ପ୍ରବେଶ ସଙ୍କେତକୁ ପୂର୍ବରୁ ବ୍ୟବହାର କରାଯାଇଛି।" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "ପୂର୍ବନିର୍ଦ୍ଧାରିତ ସୁରକ୍ଷା ପ୍ରସଙ୍ଗ ହେଉଛି %s।" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "ଆପଣ ଭିନ୍ନ ଏକ ଭୂମିକା କିମ୍ବା ସ୍ତର ଭରଣ କରିବା ପାଇଁ ଚାହୁଁଛନ୍ତି କି?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ଭୂମିକା:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "ଭୂମିକା %s ପାଇଁ କୌଣସି ପୂର୍ବନିର୍ଦ୍ଧାରିତ ପ୍ରକାର ନାହିଁ।" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "ସ୍ତର:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "ଏହା ଗୋଟିଏ ବୈଧ ସୁରକ୍ଷା ପ୍ରସଙ୍ଗ ନୁହେଁ।" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "%s ପାଇଁ ଏକ ବୈଧ ପ୍ରସଙ୍ଗ ହାସଲ ହୋଇ ପାରିଲା ନାହିଁ।" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "%s ସୁରକ୍ଷା ପ୍ରସଙ୍ଗ ନ୍ଯସ୍ତ କରାଯାଇଛି।" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "କୀ ନିର୍ମାଣ ପ୍ରସଙ୍ଗ %s ନ୍ଯସ୍ତ କରାଯାଇଛି।" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM କୁ ପ୍ରାରମ୍ଭିକୃତ କରିବାରେ ବିଫଳ\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() କରିବାରେ ବିଫଳ\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "ଲଗଇନ: fork କରିବାରେ ବିଫଳ: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "ପ୍ରବେଶ ଗ୍ରହଣୀୟ ହୋଇଛି (ପୂର୍ବ ପ୍ରବେଶ ହୋଇଥିଲା %ld ସେକଣ୍ଡ ପୂର୍ବରୁ)।" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "ଆପଣଙ୍କର ଖାତା ଅଚଳ ହୋଇଯାଇଛି; ଦୟାକରି ଆପଣଙ୍କର ତନ୍ତ୍ର ପ୍ରଶାସକଙ୍କ ସହିତ ଯୋଗାଯୋଗ କରନ୍ତୁ।" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "ଆପଣ ଆପଣଙ୍କର ପ୍ରବେଶ ସଙ୍କେତକୁ ଯଥାଶୀଘ୍ର ବଦଳାଇବା ଆବଶ୍ଯକ (ରୁଟ୍ ହେବା ବାଧ୍ଯତାମୂଳକ)।" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" +"ଆପଣ ଆପଣଙ୍କର ପ୍ରବେଶ ସଙ୍କେତକୁ ଯଥାଶୀଘ୍ର ବଦଳାଇବା ଆବଶ୍ଯକ (ପ୍ରବେଶ ସଙ୍କେତର ବୈଧତା ସରି ଯାଇଛି)।" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "ଚେତାବନୀ: ଆପଣଙ୍କ ପ୍ରବେଶ ସଙ୍କେତ %d ଦିନରେ ଅକାମି ହୋଇଯିବ।" +msgstr[1] "ଚେତାବନୀ: ଆପଣଙ୍କ ପ୍ରବେଶ ସଙ୍କେତ %d ଦିନରେ ଅକାମି ହୋଇଯିବ।" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "ଚେତାବନୀ: ଆପଣଙ୍କ ପ୍ରବେଶ ସଙ୍କେତ %d ଦିନରେ ଅକାମି ହୋଇଯିବ।" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS ପ୍ରବେଶ ସଙ୍କେତକୁ ବଦଳାଇ ହେଲା ନାହିଁ।" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "କୌଣସି ପ୍ରବେଶ ସଙ୍କେତ ପ୍ରଦାନ କରାଯାଇ ନାହିଁ।" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "ପ୍ରବେଶ ସଙ୍କେତକୁ ପରିବର୍ତ୍ତନ କରା ଯାଇ ନାହିଁ।" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "ଆପଣ ଗୋଟିଏ ଛୋଟ ପ୍ରବେଶ ସଙ୍କେତ ଚୟନ କରିବା ଉଚିତ।" + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "ଆପଣ ଗୋଟିଏ ଲମ୍ବା ପ୍ରବେଶ ସଙ୍କେତ ଚୟନ କରିବା ଉଚିତ।" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s ପାଇଁ ପ୍ରବେଶ ସଙ୍କେତକୁ ବଦଳାଉଛି." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "ପ୍ରବେଶ ସଙ୍କେତକୁ ବଦଳାଇବା ପାଇଁ ଆପଣ ଅଧିକ ସମୟ ଅପେକ୍ଷା କରିବା ଉଚିତ।" + +#~ msgid "You have no mail." +#~ msgstr "ଆପଣଙ୍କର କୌଣସି ଚିଠି ନାହିଁ।" + +#~ msgid "is the same as the old one" +#~ msgstr "ପୁରୁଣା ପ୍ରବେଶ ସଙ୍କେତ ସହିତ ଏହା ସମାନ ଅଟେ" + +#~ msgid "memory allocation error" +#~ msgstr "ସ୍ମୃତିସ୍ଥାନ ବଣ୍ଟନ ତ୍ରୁଟି" + +#~ msgid "is a palindrome" +#~ msgstr "ପ୍ରବେଶ ସଙ୍କେତଟି ଗୋଟିଏ ପାଲିନଡ୍ରୋମ ଅଟେ" + +#~ msgid "case changes only" +#~ msgstr "କେବଳ ଅକ୍ଷର ପ୍ରକାର ପରିବର୍ତ୍ତିତ ହୋଇଥାଏ" + +#~ msgid "is too similar to the old one" +#~ msgstr "ଏହା ପୂର୍ବ ପ୍ରବେଶ ସଙ୍କେତ ସହିତ ବହୁତ ସମାନ ଅଟେ" + +#~ msgid "is too simple" +#~ msgstr "ଏହା ଅତି ସହଜ ଅଟେ" + +#~ msgid "is rotated" +#~ msgstr "ଏହା ଘୂର୍ଣ୍ଣୟମାନ ଅଟେ" + +#~ msgid "not enough character classes" +#~ msgstr "ଯଥେଷ୍ଟ ବର୍ଣ୍ଣ ଶ୍ରେଣୀ ନାହିଁ" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "ଅତ୍ୟଧିକ ସମାନ ଅକ୍ଷରକୁ ଲଗାତାର ଧାରଣ କରିଥାଏ" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "ନିରସ ବର୍ଣ୍ଣର ବଡ଼ କ୍ରମ ଧାରଣ କରିଅଛି" + +#~ msgid "contains the user name in some form" +#~ msgstr "ଚାଳକ ନାମକୁ କୌଣସି ଉପାୟରେ ଧାରଣ କରିଥାଏ" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ଖରାପ ପ୍ରବେଶ ସଙ୍କେତ: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "ଖାତା ଅସ୍ଥୟୀ ଭାବରେ ଅପରିବର୍ତ୍ତନଶୀଳ ହୋଇଯାଇଛି (%ld ସେକେଣ୍ଡସ୍ ବଳିଛି)।" + +#~ msgid "Authentication error" +#~ msgstr "ବୈଧିକରଣ ତୃଟି" + +#~ msgid "Service error" +#~ msgstr "ସେବା ତୃଟି" + +#~ msgid "Unknown user" +#~ msgstr "ଅଜଣା ଚାଳକ" + +#~ msgid "Unknown error" +#~ msgstr "ଅଜଣା ତୃଟି" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset= ପାଇଁ ଖରାପ ସଂଖ୍ଯା ଦିଆଯାଇଛି\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: ଅଚିହ୍ନିତ ବିକଳ୍ପ %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: ସମସ୍ତ ଚାଳକ ମାନଙ୍କୁ ଶୂନ୍ଯ ବିହୀନ ଭାବରେ ପୁନର୍ବାର ବିନ୍ଯାସ କରିପାରିବ ନାହିଁ\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/pa.po b/po/pa.po new file mode 100644 index 0000000..91c9c11 --- /dev/null +++ b/po/pa.po @@ -0,0 +1,580 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# asaini , 2013 +# Amanpreet Singh Alam[ਆਲਮ] , 2005 +# Jaswinder Singh , 2009,2012 +# Tomáš Mráz , 2016. #zanata +# A S Alam , 2020, 2022, 2023. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2023-03-14 02:20+0000\n" +"Last-Translator: A S Alam \n" +"Language-Team: Punjabi \n" +"Language: pa\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Weblate 4.15.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "ਪਾਸਵਰਡ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "ਮੌਜੂਦਾ %s ਪਾਸਵਰਡ: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "ਮੌਜੂਦਾ ਪਾਸਵਰਡ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "ਨਵਾਂ %s ਪਾਸਵਰਡ: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "ਨਵਾਂ ਪਾਸਵਰਡ: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "ਨਵਾਂ %s ਪਾਸਵਰਡ ਮੁੜ-ਲਿਖੋ: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "ਨਵਾਂ ਪਾਸਵਰਡ ਮੁੜ-ਲਿਖੋ: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "ਅਫ਼ਸੋਸ, ਪਰ ਪਾਸਵਰਡ ਆਪਸ ‘ਚ ਮਿਲਦੇ ਨਹੀਂ ਹਨ।" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "ਮੁੜ-ਲਿਖੋ %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "ਪਾਸਵਰਡ ਤਬਦੀਲੀ ਅਧੂਰੀ ਛੱਡੀ ਗਈ।" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "ਲਾਗਇਨ:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "ਸਫ਼ਲ" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "ਗੰਭੀਰ ਗਲਤੀ - ਫ਼ੌਰੀ ਅਧੂਰਾ ਛੱਡਿਆ" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "ਮੈਡਿਊਲ ਲੋਡ ਕਰਨ ਵਿੱਚ ਫੇਲ" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "ਨਿਸ਼ਾਨ ਨਹੀਂ ਮਿਲਿਆ" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "ਸੇਵਾ ਮੈਡੀਊਲ ਵਿੱਚ ਗਲਤੀ" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "ਸਿਸਟਮ ਗਲਤੀ" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "ਮੈਮੋਰੀ ਬਫ਼ਰ ਗਲਤੀ" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "ਅਧਿਕਾਰ ਪਾਬੰਦੀ" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "ਪਰਮਾਣਕਿਤਾ ਫੇਲ" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਡਾਟਾ ਵਰਤਣ ਲਈ ਲੋੜੀਂਦੇ ਅਧਿਕਾਰ ਨਹੀਂ ਹਨ" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਸਰਵਿਸ ਪ੍ਰਮਾਣਿਕਤਾ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕਰ ਸਕਦੀ" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਮੈਡਿਊਲ ਲਈ ਵਰਤੋਂਕਾਰ ਬਾਰੇ ਕੁਝ ਪਤਾ ਨਹੀਂ" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "ਸਰਵਿਸ ਲਈ ਵੱਧ-ਤੋਂ-ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ ਕਰਨ ਦੀ ਹੱਦ ਟੱਪੀ ਗਈ" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਟੋਕਨ ਦੀ ਮਿਆਦ ਪੁੱਗ ਚੁੱਕੀ ਹੈ, ਨਵੇਂ ਦੀ ਲੋੜ ਹੈ" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "ਵਰਤੋਂਕਾਰ ਖਾਤੇ ਦੀ ਮਿਆਦ ਪੁੱਗ ਚੁੱਕੀ ਹੈ" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "ਖਾਸ ਸ਼ੈਸ਼ਨ ਲਈ ਇੱਕ ਐਂਟਰੀ ਬਣਾ/ਹਟਾ ਨਹੀਂ ਸਕਦਾ" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਸਰਵਿਸ ਵਰਤੋਂਕਾਰ ਸਨਦਾਂ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕਰ ਸਕਦੀ ਹੈ" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "ਵਰਤੋਂਕਾਰ ਸਨਦਾਂ ਦੀ ਮਿਆਦ ਪੁੱਗੀ ਹੈ" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "ਵਰਤੋਂਕਾਰ ਸਨਦਾਂ ਸੈਟਿੰਗ ਅਸਫ਼ਲ ਹੈ" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "ਕੋਈ ਮੈਡਿਊਲ ਸੰਬੰਧੀ ਡਾਟਾ ਮੌਜੂਦ ਨਹੀਂ ਹੈ" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "ਗਲਤ ਇਕਾਈਆਂ pam_*_item() ਨੂੰ ਪਾਸ ਕੀਤੀਆਂ ਹਨ" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "ਤਬਦੀਲੀ ਗਲਤੀ" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਟੋਕਨ ਸੋਧ ਗਲਤੀ" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਜਾਣਕਾਰੀ ਰਿਕਵਰ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਟੋਕਨ ਲਾਕ ਬਿਜ਼ੀ ਹੈ" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "ਪ੍ਰਮਾਣਿਕਤਾ ਟੋਕਨ ਏਜਿੰਗ ਅਸਮਰੱਥ ਹੈ" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "ਪਾਸਵਰਡ ਸਰਵਿਸ ਵਲੋਂ ਮੁੱਢਲੀ ਜਾਂਚ ਫੇਲ੍ਹ ਹੋਈ" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "ਰਿਟਰਨ ਮੁੱਲ PAM ਡਿਸਪੈਚ ਦੁਆਰਾ ਅਣਡਿੱਠਾ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "ਮੈਡੀਊਲ ਅਣਜਾਣ ਹੈ" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "ਪਰਮਾਣਕਿਤਾ ਟੋਕਨ ਦੀ ਮਿਆਦ ਪੁੱਗ ਚੁੱਕੀ ਹੈ" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "ਗੱਲਬਾਤ ਕਿਸੇ ਘਟਨਾ ਦੀ ਉਡੀਕ ਵਿੱਚ ਹੈ" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "ਕਾਰਜ ਲਈ ਫਿਰ libpam ਨੂੰ ਕਾਲ ਕਰਨ ਦੀ ਲੋੜ ਹੈ" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "ਅਣਜਾਣ PAM ਗਲਤੀ" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...ਸਮਾਂ ਸਮਾਪਤ ਹੋ ਰਿਹਾ ਹੈ...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...ਅਫਸੋਸ, ਤੁਹਾਡਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋ ਗਿਆ ਹੈ!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "ਗਲਤ ਗੱਲ (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s ਫੇਲ ਹੋਇਆ: ਕੋਡ %d ਨਾਲ ਬੰਦ ਹੋ ਗਿਆ" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s ਫੇਲ ਹੋ ਗਿਆ: ਸਿਗਨਲ %d%s ਮਿਲਿਆ" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s ਫੇਲ ਹੋਇਆ: ਅਣਪਛਾਤੀ ਸਥਿਤੀ 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"ਵਰਤੋੰ: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "ਲਾਗਇਨ ਫੇਲ੍ਹ ਸਭ ਤੋੰ ਆਖਰੀ ਫੇਲ੍ਹ ਇਸ ਤੋੰ\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u ਫੇਲ੍ਹ ਹੋਏ ਲਾਗਇਨਾਂ ਕਰਕੇ ਖਾਤਾ ਲਾਕ ਕੀਤਾ ਹੈ।" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(ਅਣਲਾਕ ਕਰਨ ਲਈ %d ਮਿੰਟ ਬਚੇ ਹਨ)" +msgstr[1] "(ਅਣਲਾਕ ਕਰਨ ਲਈ %d ਮਿੰਟ ਬਚੇ ਹਨ)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(ਅਣਲਾਕ ਕਰਨ ਲਈ %d ਮਿੰਟ ਬਚੇ ਹਨ)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s ਤੋਂ" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s ਉੱਤੇ" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "ਪਿਛਲਾ ਲਾਗਇਨ:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "ਤੁਹਾਡੇ ਨਵੇਂ ਖਾਤੇ ਵਿੱਚ ਜੀ ਆਇਆਂ ਨੂੰ!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "ਆਖਰੀ ਫੇਲ ਹੋਇਆ ਲਾਗਇਨ:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "ਪਿਛਲੇ ਕਾਮਯਾਬ ਲਾਗਇਨ ਤੋਂ ਬਾਅਦ %d ਫੇਲ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ ਸੀ।" +msgstr[1] "ਪਿਛਲੇ ਕਾਮਯਾਬ ਲਾਗਇਨ ਤੋਂ ਬਾਅਦ %d ਫੇਲ੍ਹ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ਾਂ ਸਨ।" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "ਪਿਛਲੇ ਕਾਮਯਾਬ ਲਾਗਇਨ ਤੋਂ ਬਾਅਦ %d ਫੇਲ੍ਹ ਲਾਗਇਨ ਕੋਸ਼ਿਸ਼ਾਂ ਸਨ।" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s' ਲਈ ਬਹੁਤ ਸਾਰੇ ਲਾਗਇਨ ਕੀਤੇ ਗਏ ਸਨ।" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "ਤੁਹਾਡੇ ਲਈ ਕੋਈ ਨਵੀੰ ਈਮੇਲ ਨਹੀਂ ਹੈ।" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "ਤੁਹਾਡੀ ਨਵੀਂ ਮੇਲ ਹੈ।" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "ਤੁਹਾਡੀ ਪੁਰਾਣੀ ਮੇਲ ਹੈ।" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "ਤੁਹਾਡੀ ਮੇਲ ਹੈ।" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "ਫੋਲਡਰ %s ਵਿੱਚ ਤੁਹਾਡੀ ਕੋਈ ਮੇਲ ਨਹੀਂ ਹੈ।" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "ਫੋਲਡਰ %s ਵਿੱਚ ਤੁਹਾਡੀ ਨਵੀਂ ਮੇਲ ਹੈ।" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "ਫੋਲਡਰ %s ਵਿੱਚ ਤੁਹਾਡੀ ਪੁਰਾਣੀ ਮੇਲ ਹੈ।" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "ਫੇਲਡਰ %s ਵਿੱਚ ਤੁਹਾਡੀ ਮੇਲ ਹੈ।" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "ਡਾਇਰੈਕਟਰੀ '%s' ਬਣਾਈ ਜਾ ਰਹੀ ਹੈ।" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "ਡਾਇਰੈਕਟਰੀ '%s' ਨੂੰ ਬਣਾਉਣ ਅਤੇ ਸ਼ੁਰੂ ਕਰਨ ਵਿੱਚ ਅਸਮਰਥ।" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "ਪਾਸਵਰਡ ਪਹਿਲਾਂ ਵੀ ਵਰਤਿਆ ਗਿਆ ਹੈ। ਵੱਖਰਾ ਚੁਣੋ।" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "ਪਾਸਵਰਡ ਪਹਿਲਾਂ ਹੀ ਵਰਤਿਆ ਜਾ ਚੁੱਕਾ ਹੈ।" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "ਮੂਲ ਸੁਰੱਖਿਆ ਪਰਸੰਗ %s ਹੈ।" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "ਕੀ ਤੁਸੀਂ ਵੱਖਰਾ ਰੋਲ ਜਾਂ ਲੈਵਲ ਦੇਣਾ ਚਾਹੁੰਦੇ ਹੋ?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "ਰੋਲ:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "ਰੋਲ %s ਲਈ ਕੋਈ ਮੂਲ ਕਿਸਮ ਨਹੀਂ ਹੈ।" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "ਲੈਵਲ:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "ਇਹ ਠੀਕ ਸੁਰੱਖਿਆ ਪਰਸੰਗ ਨਹੀਂ ਹੈ।" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "%s ਲਈ ਢੁੱਕਵਾਂ ਪਰਸੰਗ ਪ੍ਰਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "ਸੁਰੱਖਿਆ ਪਰਸੰਗ %s ਦਿੱਤਾ ਨਹੀਂ ਜਾ ਸਕਿਆ।" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "ਕੁੰਜੀ ਬਣਾਉਣ ਪਰਸੰਗ %s ਦਿੱਤਾ ਗਿਆ ਹੈ।" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਫੇਲ\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() ਲਈ ਫੇਲ\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "ਲਾਗਇਨ: ਫੋਰਕਿੰਗ ਫੇਲ: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "ਪਹੁੰਚ ਲਈ ਇਜਾਜ਼ਤ ਦਿੱਤੀ ਗਈ (ਪਿਛਲੀ ਪਹੁੰਚ %ld ਸਕਿੰਟ ਪਹਿਲਾਂ ਸੀ)।" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "ਤੁਹਾਡੇ ਖਾਤੇ ਦੀ ਮਿਆਦ ਪੁੱਗ ਗਈ ਹੈ; ਆਪਣੇ ਸਿਸਟਮ ਪਰਸ਼ਾਸ਼ਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ ਜੀ।" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "ਤੁਹਾਨੂੰ ਆਪਣਾ ਪਾਸਵਰਡ ਫ਼ੌਰੀ ਤਬਦੀਲ ਕਰਨਾ ਪਵੇਗਾ (ਪਰਸ਼ਾਸ਼ਕ ਵੱਲੋਂ ਲਾਜ਼ਮੀ ਬਣਾਇਆ)।" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "ਤੁਹਾਨੂੰ ਆਪਣਾ ਪਾਸਵਰਡ ਫ਼ੌਰੀ ਤਬਦੀਲ ਕਰਨਾ ਪਵੇਗਾ (ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ ਪੁੱਗੀ ਹੈ)।" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "ਚੇਤਾਵਨੀ: ਤੁਹਾਡੇ ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ %d ਦਿਨ ‘ਚ ਪੁੱਗ ਜਾਵੇਗੀ।" +msgstr[1] "ਚੇਤਾਵਨੀ: ਤੁਹਾਡੇ ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ %d ਦਿਨਾਂ ‘ਚ ਪੁੱਗ ਜਾਵੇਗੀ।" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "ਚੇਤਾਵਨੀ: ਤੁਹਾਡੇ ਪਾਸਵਰਡ ਦੀ ਮਿਆਦ %d ਦਿਨਾਂ ‘ਚ ਪੁੱਗ ਜਾਵੇਗੀ।" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS ਪਾਸਵਰਡ ਤਬਦੀਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ ਹੈ।" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "ਕੋਈ ਪਾਸਵਰਡ ਨਹੀਂ ਦਿੱਤਾ ਗਿਆ।" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "ਪਾਸਵਰਡ ਤਬਦੀਲ ਨਹੀਂ ਕੀਤਾ ਗਿਆ।" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "ਤੁਹਾਨੂੰ ਇੱਕ ਛੋਟਾ ਪਾਸਵਰਡ ਚੁਣਨਾ ਚਾਹੀਦਾ ਹੈ." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "ਤੁਹਾਨੂੰ ਲੰਮਾ ਪਾਸਵਰਡ ਚੁਣਨਾ ਪਵੇਗਾ।" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s ਲਈ ਪਾਸਵਰਡ ਤਬਦੀਲ ਕਰ ਰਿਹਾ ਹੈ।" + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "ਤੁਹਾਨੂੰ ਆਪਣੇ ਪਾਸਵਰਡ ਨੂੰ ਬਦਲਣ ਲਈ ਲੰਮੀ ਉਡੀਕ ਕਰਨੀ ਪਵੇਗੀ।" + +#~ msgid "You have no mail." +#~ msgstr "ਤੁਹਾਡੇ ਲਈ ਕੋਈ ਮੇਲ ਨਹੀਂ ਹੈ।" + +#~ msgid "is the same as the old one" +#~ msgstr "ਪੁਰਾਣੇ ਵਰਗਾ ਹੈ" + +#~ msgid "memory allocation error" +#~ msgstr "ਮੈਮੋਰੀ ਨਿਰਧਾਰਨ ਗਲਤੀ" + +#~ msgid "is a palindrome" +#~ msgstr "ਘੁੰਮਾਉ ਸ਼ਬਦ ਹੈ" + +#~ msgid "case changes only" +#~ msgstr "ਸਿਰਫ ਅੱਖਰ ਤਬਦੀਲੀ" + +#~ msgid "is too similar to the old one" +#~ msgstr "ਪੁਰਾਣੇ ਨਾਲ ਬਹੁਤ ਮਿਲਦਾ-ਜੁਲਦਾ ਹੈ" + +#~ msgid "is too simple" +#~ msgstr "ਬਹੁਤ ਸਧਾਰਨ ਹੈ" + +#~ msgid "is rotated" +#~ msgstr "ਘੁੰਮਾਇਆ ਹੈ" + +#~ msgid "not enough character classes" +#~ msgstr "ਲੋੜੀਂਦੀਆਂ ਅੱਖਰ ਸ਼੍ਰੇਣੀਆਂ ਨਹੀਂ ਹਨ" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "ਲਗਾਤਾਰ ਬਹੁਤ ਸਾਰੇ ਮਿਲਦੇ-ਜੁਲਦੇ ਅੱਖਰ ਸ਼ਾਮਿਲ ਹਨ" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "ਇੱਕ ਬਹੁਤ ਜਿਆਦਾ ਲੰਬੀ ਇਕਸੁਰਤਾ ਵਾਲੀ ਅੱਖਰ ਲੜੀ ਸਮਾਈ ਹੋਈ ਹੈ" + +#~ msgid "contains the user name in some form" +#~ msgstr "ਕਿਸੇ ਢੰਗ ਨਾਲ ਵਰਤੋਂਕਾਰ ਨਾਂ ਮੌਜੂਦ ਹੈ" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ਗਲਤ ਪਾਸਵਰਡ: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "ਖਾਤਾ ਆਰਜ਼ੀ ਤੌਰ ‘ਤੇ ਲਾਕ ਕੀਤਾ ਹੈ (%ld ਸਕਿੰਟ ਬਾਕੀ ਹਨ)।" + +#~ msgid "Authentication error" +#~ msgstr "ਪਰਮਾਣਕਿਤਾ ਗਲਤੀ" + +#~ msgid "Service error" +#~ msgstr "ਸੇਵਾ ਗਲਤੀ" + +#~ msgid "Unknown user" +#~ msgstr "ਅਣਪਛਾਤਾ ਵਰਤੋਂਕਾਰ" + +#~ msgid "Unknown error" +#~ msgstr "ਅਣਜਾਣੀ ਗਲਤੀ" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset= ਲਈ ਗਲਤ ਨੰਬਰ ਦਿੱਤਾ ਗਿਆ\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: ਬੇਪਛਾਣ ਚੋਣ %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਗ਼ੈਰ-ਸਿਫ਼ਰ ਲਈ ਸੈੱਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u ਵਰਤੋਂਕਾਰ-ਨਾਂ] [--user ਵਰਤੋਂਕਾਰ-ਨਾਂ]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/pl.po b/po/pl.po new file mode 100644 index 0000000..914c6ff --- /dev/null +++ b/po/pl.po @@ -0,0 +1,590 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Piotr Drąg , 2008,2012, 2020, 2021, 2022. +# Stanisław Małolepszy , 2006 +# Wojciech Kapusta , 2006 +# Piotr Drąg , 2016. #zanata, 2020, 2021, 2022. +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-11-13 11:41+0000\n" +"Last-Translator: Piotr Drąg \n" +"Language-Team: Polish \n" +"Language: pl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.14.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Hasło: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Obecne hasło %s: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Obecne hasło: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nowe hasło %s: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nowe hasło: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Proszę ponownie wpisać nowe hasło %s: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Proszę ponownie wpisać nowe hasło: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Hasła się nie zgadzają." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Proszę ponownie wpisać %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Przerwano zmianę hasła." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "login:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Powodzenie" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Błąd krytyczny — natychmiastowe przerwanie" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Wczytanie modułu się nie powiodło" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Nie odnaleziono symbolu" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Błąd w module usługi" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Błąd systemu" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Błąd buforowania pamięci" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Brak dostępu" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Uwierzytelnienie się nie powiodło" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Niewystarczające uprawnienia do dostępu do danych uwierzytelniających" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Usługa uwierzytelniania nie może uzyskać informacji o uwierzytelnianiu" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Nieznany użytkownik w module uwierzytelniania niższego poziomu" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Wykorzystano maksymalną liczbę prób dla usługi" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Token uwierzytelniania nie jest już ważny; wymagany jest nowy" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Konto użytkownika wygasło" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Nie można utworzyć/usunąć wpisu dla podanej sesji" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Usługa uwierzytelniania nie może uzyskać uwierzytelnienia użytkownika" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Uwierzytelnienie użytkownika wygasło" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Ustawienie uwierzytelniania użytkownika się nie powiodło" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Brak danych specyficznych dla modułu" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Błędny element przekazany do pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Błąd rozmowy" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Błąd podczas modyfikowania tokenu uwierzytelniania" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Nie można odzyskać informacji uwierzytelniających" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Blokada tokenu uwierzytelniania jest zajęta" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Wygasanie tokenu uwierzytelniania jest wyłączone" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Wstępne sprawdzenie hasła przez usługę się nie powiodło" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Zwrócona wartość powinna zostać zignorowana przez PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Moduł jest nieznany" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Token uwierzytelniania wygasł" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Rozmowa oczekuje na zdarzenie" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Aplikacja musi jeszcze raz wywołać libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Nieznany błąd PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "…czas mija…\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "…czas minął.\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "błędna rozmowa (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s się nie powiodło: kod wyjścia %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s się nie powiodło: otrzymano sygnał %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s się nie powiodło: nieznany stan 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Użycie: %s [--dir /ścieżka/do/katalogu-tally] [--user nazwa-użytkownika] [--" +"reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Login Niepowodzenia Ostatnie niepowodzenie Z\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Konto zostało zablokowane z powodu %u nieudanych logowań." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(pozostała %d minuta do odblokowania)" +msgstr[1] "(pozostały %d minuty do odblokowania)" +msgstr[2] "(pozostało %d minut do odblokowania)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(pozostało %d min do odblokowania)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a, %-d %b %Y, %H∶%M∶%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " z %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " na %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Ostatnie logowanie:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Witaj na swoim nowym koncie!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Ostatnie nieudane logowanie:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Nastąpiła %d nieudana próba zalogowania od ostatniego udanego logowania." +msgstr[1] "" +"Nastąpiły %d nieudane próby zalogowania od ostatniego udanego logowania." +msgstr[2] "" +"Nastąpiło %d nieudanych prób zalogowania od ostatniego udanego logowania." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Nastąpiło %d nieudanych prób zalogowania od ostatniego udanego logowania." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Za dużo prób zalogowania na „%s”." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Nie ma nowych wiadomości." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Odebrano nowe wiadomości." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Skrzynka zawiera stare wiadomości." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Odebrano wiadomości." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Brak wiadomości w katalogu %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Nowe wiadomości w katalogu %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Stare wiadomości w katalogu %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Wiadomości w katalogu %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Tworzenie katalogu „%s”." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Nie można utworzyć i zainicjować katalogu „%s”." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Hasło było już używane. Należy wybrać inne." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Hasło było już używane." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Domyślny kontekst zabezpieczeń to %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Wprowadzić inną rolę lub poziom?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rola:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Brak domyślnego typu dla roli %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "poziom:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Nieprawidłowy kontekst zabezpieczeń." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Nie można uzyskać prawidłowego kontekstu dla %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Przypisano kontekst zabezpieczeń %s." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Przypisano kontekst tworzenia klucza %s." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "zainicjowanie PAM się nie powiodło\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "„pam_set_item()” się nie powiodło\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: rozdzielenie się nie powiodło: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Udzielono dostęp (ostatni dostęp %ld s temu)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Konto wygasło; proszę skontaktować się z administratorem komputera." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Wymagana jest natychmiastowa zmiana hasła (wymuszone przez administratora)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Wymagana jest natychmiastowa zmiana hasła (hasło wygasło)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Ostrzeżenie: hasło wygaśnie za %d dzień." +msgstr[1] "Ostrzeżenie: hasło wygaśnie za %d dni." +msgstr[2] "Ostrzeżenie: hasło wygaśnie za %d dni." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Ostrzeżenie: hasło wygaśnie za %d dni." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Nie można zmienić hasła NIS." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Nie podano hasła." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Hasło nie zostało zmienione." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Należy wybrać krótsze hasło." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Należy wybrać dłuższe hasło." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Zmienianie hasła dla %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Należy poczekać dłużej na zmianę hasła." + +#~ msgid "You have no mail." +#~ msgstr "Brak wiadomości." + +#~ msgid "is the same as the old one" +#~ msgstr "jest identyczne z poprzednim" + +#~ msgid "memory allocation error" +#~ msgstr "błąd przydziału pamięci" + +#~ msgid "is a palindrome" +#~ msgstr "jest palindromem" + +#~ msgid "case changes only" +#~ msgstr "ma zmienioną tylko wielkość znaków" + +#~ msgid "is too similar to the old one" +#~ msgstr "jest za bardzo podobne do poprzedniego" + +#~ msgid "is too simple" +#~ msgstr "jest za proste" + +#~ msgid "is rotated" +#~ msgstr "jest obrócone" + +#~ msgid "not enough character classes" +#~ msgstr "za mało rodzajów znaków" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "zawiera za dużo takich samych znaków po sobie" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "zawiera za dużo takich samych znaków po sobie" + +#~ msgid "contains the user name in some form" +#~ msgstr "zawiera nazwę użytkownika w jakiejś formie" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "BŁĘDNE HASŁO: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Konto zostało tymczasowo zablokowane (pozostało %ld s)." + +#~ msgid "Authentication error" +#~ msgstr "Błąd uwierzytelniania" + +#~ msgid "Service error" +#~ msgstr "Błąd usługi" + +#~ msgid "Unknown user" +#~ msgstr "Nieznany użytkownik" + +#~ msgid "Unknown error" +#~ msgstr "Nieznany błąd" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: podano błędny numer dla „--reset=”\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: nierozpoznana opcja %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file nazwa-pliku-root] [--user nazwa-użytkownika] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: nie można przywrócić wszystkich użytkowników\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f nazwa-pliku-root] [--file nazwa-pliku-root]\n" +#~ " [-u nazwa-użytkownika] [--user nazwa-użytkownika]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/pt.po b/po/pt.po new file mode 100644 index 0000000..0653dc2 --- /dev/null +++ b/po/pt.po @@ -0,0 +1,595 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Antonio Cardoso Martins , 2005-2006 +# Ricardo Pinto , 2013 +# Rui Gouveia , 2009 +# Rui Gouveia , 2012 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020, 2021. +# Manuela Silva , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2021-07-22 00:54+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Portuguese \n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Weblate 4.7.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Palavra-passe: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Palavra-passe %s atual: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Palavra-passe atual: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nova palavra-passe %s : " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nova palavra-passe: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Digite novamente a nova palavra-passe %s: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Digite novamente a nova palavra-passe: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Desculpe, as palavras-passe não coincidem." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Digite novamente %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "A alteração da palavra-passe foi abortada." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "credencial:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Sucesso" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Erro crítico - abortar imediatamente" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Não foi possível carregar o modulo" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Símbolo não encontrado" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Erro no módulo do serviço" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Erro de sistema" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Erro de ''buffer'' da memória" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Permissão negada" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Falha de autenticação" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Credenciais insuficientes para aceder aos dados de autenticação" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "O serviço de autenticação não pode obter a informação de autenticação" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Utilizador desconhecido para o módulo de autenticação subjacente" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Esgotou o número máximo de tentativas para o serviço" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "O código de autenticação já não é válido; é necessário um novo" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "A conta do utilizador expirou" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Não é possível criar/remover uma entrada para a sessão especificada" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"O serviço de autenticação não consegue obter as credenciais do utilizador" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "As credenciais do utilizador expiraram" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Falha na definição das credenciais do utilizador" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Não existe informação do módulo" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Item incorrecto passado para pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Erro de conversação" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Erro de manipulação do código de autenticação" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "A informação de autenticação não pode ser recuperada" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Bloqueio de código de autenticação ocupado" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Período do código de autenticação desativado" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "A validação preliminar pelo serviço de palavras-passe falhou" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "O valor de retorno deveria ser ignorado pelo PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "O módulo é desconhecido" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "O 'token' de autenticação expirou" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "A conversação está a aguardar um evento" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "A aplicação necessita de invocar o libpam novamente" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Erro PAM desconhecido" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...O tempo está a esgotar-se...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Desculpe, o seu tempo expirou!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "conversação errónea (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s falhou: código de saída %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s falhou: sinal capturado %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s falhou: estado desconhecido 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Utilização: %s [--dir / path/to/tally-directory] [--user nome de utilizador] " +"[--reset]]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Sessão Falhas Última falha De\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Conta bloqueada devido a %u inícios de sessão falhados." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minuto restante para desbloquear)" +msgstr[1] "(%d minutos restante para desbloquear)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(Minutos restantes para desbloquear: %d)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " a partir de %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " em %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Último início de sessão:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Bem vindo à sua nova conta!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Último início de sessão falhado:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Houve %d tentativa falhada de início de sessão desde o último início de " +"sessão com sucesso." +msgstr[1] "" +"Houve %d tentativas falhadas de início de sessão desde o último início de " +"sessão com sucesso." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Houve %d tentativas falhadas de início de sessão desde o último início de " +"sessão com sucesso." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Demasiados inícios de sessão para '%s'." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Tem correio electrónico novo." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Tem correio electrónico novo." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Tem correio electrónico antigo." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Tem correio electrónico." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Não tem correio electrónico na pasta %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Tem correio electrónico novo na pasta %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Tem correio electrónico antigo na pasta %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Tem correio electrónico na pasta %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "A criar directório '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Não foi possível criar e inicializar o directório '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "A senha já foi utilizada anteriormente. Escolha outra." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "A senha já foi utilizada anteriormente." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Contexto de Segurança por Omissão %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Pretende inserir um perfil ou nível diferente?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "Perfil:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Perfil sem tipo definido por omissão %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nível:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Não é um contexto de segurança válido." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Não foi possível obter um contexto de segurança válido para %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Contexto de Segurança %s Atribuído." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Contexto de Segurança de Chaves %s Atribuído." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "falha ao inicializar o PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "falha em pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "início de sessão: falha no 'forking': %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Acesso permitido (último acesso foi à %ld segundos atrás)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"A sua conta expirou; por favor contacte o seu administrador de sistema." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"É obrigatório que altere de imediato a sua senha (politica do sistema)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" +"É obrigatório que altere de imediato a sua senha (antiguidade da password)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Aviso: a sua senha expira em %d dia." +msgstr[1] "Aviso: a sua senha expira em %d dias." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Aviso: a sua palavra passe expira em %d dias." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "A senha NIS não pode ser alterada." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Não foi fornecida uma senha." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "A senha não foi alterada." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Você deve escolher uma senha mais curta." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Deve escolher uma senha mais longa." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "A alterar senha para %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Tem de esperar mais antes de poder alterar a sua senha." + +#~ msgid "You have no mail." +#~ msgstr "Não tem correio electrónico." + +#~ msgid "is the same as the old one" +#~ msgstr "é igual à anterior" + +#~ msgid "memory allocation error" +#~ msgstr "erro de alocação de memória" + +#~ msgid "is a palindrome" +#~ msgstr "é um palíndromo" + +#~ msgid "case changes only" +#~ msgstr "apenas muda a capitulação" + +#~ msgid "is too similar to the old one" +#~ msgstr "é muito semelhante à anterior" + +#~ msgid "is too simple" +#~ msgstr "é demasiado simples" + +#~ msgid "is rotated" +#~ msgstr "é rodada" + +#~ msgid "not enough character classes" +#~ msgstr "não tem classes de caracteres suficientes" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "contém demasiados caracteres iguais consecutivos" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "contém demasiados caracteres iguais consecutivos" + +#~ msgid "contains the user name in some form" +#~ msgstr "contém, de alguma forma, o nome do utilizador" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "MÁ SENHA: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Conta temporariamente bloqueada (faltam %ld segundos)." + +#~ msgid "Authentication error" +#~ msgstr "Erro de autenticação" + +#~ msgid "Service error" +#~ msgstr "Erro de serviço" + +#~ msgid "Unknown user" +#~ msgstr "Utilizador desconhecido" + +#~ msgid "Unknown error" +#~ msgstr "Erro desconhecido" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Número errado fornecido a --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Opção não reconhecida %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file ficheiro-raiz] [--user nome-utilizador] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "" +#~ "%s: Não foi possível reiniciar todos os utilizadores para não zero\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u nome-de-utilizador] [--user nome-de-utilizador]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/pt_BR.po b/po/pt_BR.po new file mode 100644 index 0000000..c6645a3 --- /dev/null +++ b/po/pt_BR.po @@ -0,0 +1,585 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Diego Búrigo Zacarão , 2008 +# Elder Marco , 2012 +# Glaucia Freitas , 2007 +# Glaucia Freitas , 2007 +# Marcelo Barbosa , 2013 +# Taylon Silmer , 2008-2009 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020, 2021. +# Rafael Fontenelle , 2023. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2023-09-18 01:35+0000\n" +"Last-Translator: Rafael Fontenelle \n" +"Language-Team: Portuguese (Brazil) \n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.0.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Senha: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Atual %s senha: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Atual senha: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nova %s senha: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nova senha: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Redigite a nova %s senha: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Redigite a nova senha: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "As senhas não são iguais." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Redigite %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "A alteração de senha foi abortada." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "login:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Sucesso" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Erro crítico - abortar imediatamente" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Falha ao carregar módulo" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Símbolo não encontrado" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Erro no módulo de serviço" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Erro do sistema" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Erro do buffer de memória" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Permissão negada" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Falha de autenticação" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Credenciais insuficientes para acessar dados de autenticação" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "O serviço de autenticação não recuperou informações de autenticação" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Usuário desconhecido para o módulo de autenticação subjacente" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Esgotado o número máximo de tentativas para serviço" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "O token de autenticação não é mais válido; é necessario um novo token" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "A conta do usuário expirou" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Impossível fazer/remover uma entrada para a sessão específica" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "O serviço de autenticação não recuperou as credenciais do usuário" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Credenciais do usuário expiradas" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Falha ao definir credenciais do usuário" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Não há nenhum dado específico para o módulo" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Ítem incorreto passado para pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Erro de conversação" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Erro de manipulação de token de autenticação" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Impossível recuperar informações de autenticação" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Bloqueio de token de autenticação ocupado" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Validade do token de autenticação desabilitado" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Falha na verificação preliminar por serviço de senha" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "O valor de retorno deve ser ignorado pelo despacho PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Módulo desconhecido" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Token de autenticação expirado" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Conversação aguardando por evento" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "O aplicativo precisa chamar libpam novamente" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Erro desconhecido no PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...O tempo está acabando...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Desculpe, seu tempo está aumentando!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "conversação errônea (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s falhou: código de saída %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s falhou: detectou sinal %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s falhou: status desconhecido 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Uso: %s [--dir /caminho/para/diretório-tally] [--user usuário] [--reset] [--" +"legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Login Falhas Última falha De\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Conta bloqueada devido a %u falhas de login." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minuto restante para desbloquear)" +msgstr[1] "(%d minutos restante para desbloquear)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(minutos restantes para desbloquear: %d)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " de %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " em %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Último login:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Bem-vindo à sua nova conta!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Falha no último login:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Houve %d falhas de login desde o último login bem sucedido." +msgstr[1] "Houveram %d falhas de login desde o último login bem sucedido." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Houveram %d falhas de login desde o último login bem sucedido." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Há logins demais para '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Você não tem novas mensagens." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Há novas mensagens." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Há mensagens antigas." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Há mensagens." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Não há mensagens na pasta %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Há novas mensagens na pasta %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Há mensagens antigas na pasta %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Há mensagens na pasta %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Criando o diretório '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Impossível criar e inicializar o diretório \"%s\"." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "A senha já foi usada. Escolha outra." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "A senha já foi usada." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Contexto de Segurança Padrão %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Deseja digitar uma função ou nível diferente?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "função:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Não existe tipo padrão para a função %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nível:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Não é um contexto de segurança válido." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Impossível obter um contexto válido para %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Contexto de segurança %s atribuído." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Contexto de criação de chave %s atribuído." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "falha ao inicializar PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "falha em pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: falha na bifurcação: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Acesso concedido (o último acesso foi a %ld segundos atrás)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Sua conta expirou; entre em contato com o administrador do sistema." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Mude sua senha imediatamente (aplicado pela raiz)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Mude sua senha imediatamente (senha expirada)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Aviso: sua senha irá expirar em %d dia." +msgstr[1] "Aviso: sua senha irá expirar em %d dias." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Aviso: sua senha irá expirar em %d dias." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "A senha NIS não pôde ser mudada." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Nenhuma senha informada." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Senha inalterada." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Você deve escolher uma senha mais curta." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Escolha uma senha mais longa." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Mudando senha para %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Aguarde mais tempo para mudar a senha." + +#~ msgid "You have no mail." +#~ msgstr "Não há mensagens." + +#~ msgid "is the same as the old one" +#~ msgstr "é igual à antiga senha" + +#~ msgid "memory allocation error" +#~ msgstr "Erro de alocação de memória" + +#~ msgid "is a palindrome" +#~ msgstr "é um palíndromo" + +#~ msgid "case changes only" +#~ msgstr "mudou apenas maiúsculas/minúsculas" + +#~ msgid "is too similar to the old one" +#~ msgstr "é muito semelhante à antiga" + +#~ msgid "is too simple" +#~ msgstr "é simples demais" + +#~ msgid "is rotated" +#~ msgstr "foi invertida" + +#~ msgid "not enough character classes" +#~ msgstr "classes de caractere insuficientes" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "contém muitos caracteres igual consecutivamente" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "contém muito longo de uma seqüência de caracteres monotônica" + +#~ msgid "contains the user name in some form" +#~ msgstr "contém o nome de usuário em algum formulário" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "SENHA INCORRETA: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Conta temporariamente bloqueada (restam %ld segundos)." + +#~ msgid "Authentication error" +#~ msgstr "Erro de autenticação" + +#~ msgid "Service error" +#~ msgstr "Erro de serviço" + +#~ msgid "Unknown user" +#~ msgstr "Usuário desconhecido" + +#~ msgid "Unknown error" +#~ msgstr "Erro desconhecido" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Número insuficiente fornecido para --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Opção não reconhecida %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file ficheiro-raiz [--user nome-utilizador] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Impossível redefinir todos os usuários para não-zero\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f ficheiro-raiz] [--file ficheiro-raiz]\n" +#~ " [-u nome-utilizador] [--user nome-utilizador]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ro.po b/po/ro.po new file mode 100644 index 0000000..3335a94 --- /dev/null +++ b/po/ro.po @@ -0,0 +1,519 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +# Vlad , 2020. +# Remus-Gabriel Chelu , 2023. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2023-04-09 22:51+0000\n" +"Last-Translator: Remus-Gabriel Chelu \n" +"Language-Team: Romanian \n" +"Language: ro\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " +"20)) ? 1 : 2;\n" +"X-Generator: Weblate 4.15.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Parola: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Parolă curentă %s: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Parolă curentă: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Parolă nouă %s: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Parolă nouă: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Rescrieți parola nouă %s: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Rescrieți parola nouă: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Parolele nu se potrivesc." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Rescrieți %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Schimbarea parolei a fost anulată." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "autentificare:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Succes" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Eroare critică - abandonare imediată" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Încărcarea modulului a eșuat" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Simbolul nu a fost găsit" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Eroare în modulul de serviciu" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Eroare de sistem" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Eroare în memoria tampon" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Acces refuzat" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Autentificare eșuată" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Acreditări insuficiente pentru a accesa datele de autentificare" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"Serviciul de autentificare nu poate obține informațiile de autentificare" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Utilizator necunoscut de modulul de autentificare subiacent" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "S-a atins numărul maxim de încercări pentru serviciu" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" +"Autentificarea nu mai este valabilă; o nouă autentificare este necesară" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Contul utilizatorului a expirat" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Nu se poate crea/șterge o intrare pentru sesiunea specificată" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Serviciul de autentificare nu poate obține acreditările utilizatorului" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Acreditările utilizatorului au expirat" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Eșec la definirea acreditărilor pentru utilizator" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Nu există date specifice pentru modul" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "A fost trimis un element incorect către pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Eroare de conversație" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Eroare la manipularea informației de autentificare" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Informația de autentificare nu poate fi recuperată" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Blocarea informației de autentificare este rezervată" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Perioada de validitate a informației de autentificare este dezactivată" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Eroare la verificarea preliminară făcută de către serviciul de parole" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Valoarea de returnare trebuie să fie ignorată de către expeditorul PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modul necunoscut" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Informația de autentificare a expirat" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Conversația așteaptă după un eveniment" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Aplicația trebuie să cheme din nou libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Eroare PAM necunoscută" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Timpul se scurge...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Timpul a expirat!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "conversație eronată (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s a eșuat: codul de ieșire %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s a eșuat: semnalul captat %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s a eșuat: stare necunoscută 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Utilizare: %s [--dir /ruta/la/directorul-tally] [--user nume_utilizator] [--" +"reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Autentificare Eșuări Ultima eșuare De la\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Contul este blocat din cauza a %u autentificări eșuate." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(un minut rămas până la deblocare)" +msgstr[1] "(%d minute rămase până la deblocare)" +msgstr[2] "(%d de minute rămase până la deblocare)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minute rămase până la deblocare)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %A %e %B %Y la %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " de la %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " pe %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Ultima autentificare:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Bun venit în noul cont!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Ultima autentificare eșuată:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"A avut loc %d încercare nereușită de autentificare de la ultima " +"autentificare reușită." +msgstr[1] "" +"Au avut loc %d încercări nereușite de autentificare de la ultima " +"autentificare reușită." +msgstr[2] "" +"Au avut loc %d de încercări nereușite de autentificare de la ultima " +"autentificare reușită." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Au avut loc %d încercări nereușite de autentificare de la ultima " +"autentificare reușită." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Sunt prea multe autentificări pentru „%s”." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Nu aveți niciun mesaj nou." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Aveți un mesaj nou." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Aveți un mesaj vechi." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Aveți un mesaj." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Nu aveți mesaje în directorul %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Aveți un mesaj nou în directorul %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Aveți un mesaj vechi în directorul %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Aveți un mesaj în directorul %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Se creează directorul „%s”." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Nu se poate crea și inițializa directorul „%s”." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Parola a fost deja utilizată. Alegeți alta." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Parola a fost deja utilizată." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Contextul implicit de securitate este %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Vreți să introduceți un rol sau nivel diferit?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rol:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Nu există un tip implicit pentru rolul %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nivel:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Nu există un context valid de securitate." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Nu s-a putut obține un context valid pentru %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Contextul de securitate %s a fost asignat." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Cheia pentru crearea contextului %s a fost asignată." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "eroare la inițializarea PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "eroare la pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "autentificare: eroare la bifurcare %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Accesul a fost permis (ultimul acces a fost acum %ld secunde)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Contul dvs. a expirat; contactați administratorul de sistem." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Vi se cere să vă schimbați parola imediat (cerință impusă de administrator)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Vi se cere să vă schimbați parola imediat (parola a expirat)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Atenție: parola dvs. va expira în %d zi." +msgstr[1] "Atenție: parola dvs. va expira în %d zile." +msgstr[2] "Atenție: parola dvs. va expira în %d de zile." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Atenție: parola dvs. va expira în %d zile." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Parola pentru NIS nu poate fi schimbată." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Nu a fost introdusă nicio parolă." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Parola nu a fost schimbată." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Trebuie să alegeți o parolă mai scurtă." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Trebuie să alegeți o parolă mai lungă." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Se schimbă parola pentru %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Trebuie să așteptați mai mult până veți putea schimba parola." + +#~ msgid "You have no mail." +#~ msgstr "Nu ai mesaje." diff --git a/po/ru.po b/po/ru.po new file mode 100644 index 0000000..2277575 --- /dev/null +++ b/po/ru.po @@ -0,0 +1,590 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Aleksandr Brezhnev , 2012 +# Andrew Martynov , 2008 +# Yulia , 2007,2009 +# Yulia , 2013 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020, 2021. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2021-07-21 13:19+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Russian \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.7.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Пароль: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Текущий пароль %s: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Текущий пароль: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Новый пароль %s: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Новый пароль: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Повторите ввод нового пароля %s: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Повторите ввод нового пароля: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Извините, но пароли не совпадают." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Повторите ввод %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Изменение пароля отменено." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "учетная запись:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Успех" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Критическая ошибка -- незамедлительное прерывание операции" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Не удалось загрузить модуль" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Символ не найден" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Ошибка в модуле службы" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Системная ошибка" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Ошибка буфера памяти" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Доступ запрещен" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Сбой при проверке подлинности" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Недостаточно учетных данных для доступа к данным проверки подлинности" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"Службе проверки подлинности не удается загрузить сведения аутентификации" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Пользователь не известен базовому модулю проверки подлинности" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Использовано максимальное число попыток, заданное для службы" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Маркер проверки подлинности больше недействителен; требуется новый" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Срок действия учетной записи пользователя истек" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Не удалось создать/удалить запись для указанного сеанса" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"Службе проверки подлинности не удается загрузить учетные данные пользователя" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Срок действия учетных данных пользователя истек" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Сбой при настройке учетных данных пользователя" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Отсутствуют данные, специфичные для модуля" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "В pam_*_item() передан неверный элемент" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Ошибка диалога" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Ошибка при операциях с маркером проверки подлинности" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Не удается восстановить сведения аутентификации" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Блокировка маркера проверки подлинности занята" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Ограничение срока действия маркера проверки подлинности отключено" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Службе паролей не удалось выполнить предварительную проверку" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Возвращенное значение не должно учитываться при передаче в PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Неизвестный модуль" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Срок действия маркера проверки подлинности истек" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Процесс диалога ожидает событие" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Приложение должно повторно вызвать libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Неизвестная ошибка PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Время истекает...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Извините, ваше время истекло!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "ошибочный диалог (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "Сбой %s. Код выхода: %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "Сбой %s. Получен сигнал %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "Сбой %s. Неизвестный статус 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Использование: %s: [--dir /путь/к/каталогу-tally] [--user имя_пользователя] " +"[--reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Учетная запись Сбои Последний сбой С\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" +"Учетная запись заблокирована как следствие неудачных попыток входа (всего -- " +"%u)." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(осталась %d минута до разблокировки)" +msgstr[1] "(осталось %d минуты до разблокировки)" +msgstr[2] "(осталось %d минут до разблокировки)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(осталось %d мин. до разблокировки)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " с %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " на %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Последний вход в систему:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Добро пожаловать в новую учетную запись!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Последняя неудачная попытка входа в систему:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Со времени последнего входа была %d неудачная попытка." +msgstr[1] "Число неудачных попыток со времени последнего входа: %d." +msgstr[2] "Число неудачных попыток со времени последнего входа: %d." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Число неудачных попыток со времени последнего входа: %d." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Слишком много регистраций в системе для «%s»." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Почты нет." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Есть новая почта." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Есть старая почта." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Есть почта." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Нет почты в папке %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Есть новая почта в папке %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Есть старая почта в папке %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Есть почта в папке %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Создание каталога %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Не удалось создать и инициализировать каталог %s." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Этот пароль уже был использован. Выберите другой." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Этот пароль уже использовался." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Контекст безопасности по умолчанию %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Хотите ввести другую роль или уровень?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "роль:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Для роли %s нет типа по умолчанию." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "уровень:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Неверный контекст безопасности." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Не удалось получить корректный контекст для %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Контекст безопасности %s назначен." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Контекст %s, используемый при создании ключей, назначен." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "не удалось инициировать PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "не удалось выполнить pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "регистрация: сбой при создании нового процесса: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Доступ предоставлен (последнее обращение было %ld сек. назад)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"Срок действия учетной записи истек; обратитесь к системному администратору." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Вам необходимо немедленно сменить пароль (по требованию администратора)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Вам необходимо немедленно сменить пароль (пароль устарел)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Предупреждение: срок действия пароля истекает через %d день." +msgstr[1] "Предупреждение: срок действия пароля истекает через %d дня." +msgstr[2] "Предупреждение: срок действия пароля истекает через %d дней." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Предупреждение: срок действия пароля истекает через %d дн(я)(ей)." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Пароль NIS изменить нельзя." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Пароль не указан." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Пароль не изменен." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Вы должны выбрать более короткий пароль." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Выберите пароль большей длины." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Смена пароля для %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "До смены пароля должно пройти больше времени." + +#~ msgid "is the same as the old one" +#~ msgstr "совпадает со старым" + +#~ msgid "memory allocation error" +#~ msgstr "ошибка выделения памяти" + +#~ msgid "is a palindrome" +#~ msgstr "является палиндромом" + +#~ msgid "case changes only" +#~ msgstr "изменения только в регистре" + +#~ msgid "is too similar to the old one" +#~ msgstr "слишком похож на старый" + +#~ msgid "is too simple" +#~ msgstr "слишком простой" + +#~ msgid "is rotated" +#~ msgstr "является результатом чередования" + +#~ msgid "not enough character classes" +#~ msgstr "слишком мало символов различных типов" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "содержит слишком длинную последовательность одинаковых символов" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "содержит слишком много повторяющихся символов" + +#~ msgid "contains the user name in some form" +#~ msgstr "содержит имя пользователя" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "НЕУДАЧНЫЙ ПАРОЛЬ: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Учетная запись временно заблокирована (осталось %ld сек.)." + +#~ msgid "Authentication error" +#~ msgstr "Ошибка при проверке подлинности" + +#~ msgid "Service error" +#~ msgstr "Ошибка службы" + +#~ msgid "Unknown user" +#~ msgstr "Неизвестный пользователь" + +#~ msgid "Unknown error" +#~ msgstr "Неизвестная ошибка" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: указано неверное число для --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: неопознанный параметр %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file имя_корневого_файла] [--user имя_пользователя] [--reset[=n]] " +#~ "[--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "" +#~ "%s: не удается выполнить сброс всех пользователей в ненулевое значение\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f имя_корневого_файла] [--file имя_корневого_файла]\n" +#~ " [-u имя_пользователя] [--user имя_пользователя]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/si.po b/po/si.po new file mode 100644 index 0000000..273976a --- /dev/null +++ b/po/si.po @@ -0,0 +1,563 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Danishka Navin , 2007. +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-03-06 23:59+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Sinhala \n" +"Language: si\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Weblate 3.11.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "රහස්පදය: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "නව %s රහස්පදය: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "නව රහස්පදය: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "නව %s රහස්පදය: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "නව රහස්පදය: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "නව %s රහස්පදය නැවත ඇතුළත් කරන්න: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "නව රහස්පදය නැවත ඇතුළත් කරන්න: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "සමාවෙන්න, රහස්පද ගැලපෙන්නේ නැත." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "රහස්පදය දැනටමත් භාවිතා වේ. වෙනත් එකක් තෝරාගන්න." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "පිවිසීම:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "සාර්ථකයි" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "අවදානම් දෝෂය- ක්‍ෂණික ඉවත්වීම" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "අංගය පූරණය අසමත් විය" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "සංකේතය හමුවූයේ නැත" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "සේවා අංගයේ දෝෂයකි" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "පද්ධති දෝෂය" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "මතක බෆරයේ දෝෂය" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "අවසර වලකා ඇත" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "තහවුරු කරගැනීම අසමත් විය" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "අවසර දත්ත සඳහා ප්‍රවේශයට ප්‍රමාණවත් සහතිකත්වයක් නැත" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "තහවුරු කරගැනීමේ සේවාව මඟින් තහවුරු කරගැනීමේ තොරතුරු ලබාගත නොහැක" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "පාදකවු තහවුරු කරගැනීමේ අංගය සඳහා පරිශිලකයා නොදනි" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "සේවාව සඳහා උපරිම උත්සාහ කිරිම් ඉක්මවා ඇත" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "තහවුරු කරගැනීමේ ටෝකනය තවදුරටත් වලංගු නැත; අලුත් එකක් අවශ්‍යවේ" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "පරිශීලක ගිණුම කල්ඉකුත් විය" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "දක්වා ඇති සැසිය සඳහා ඇතුළත් කිරීමක් එකක් කිරීම/ඉවත් කිරීම කළ නොහැක" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "තහවුරු කරගැනීමේ සේවාව මඟින් පරිශීලක සහතික ලබාගත නොහැක" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "පරිශීලක සහතික කල්ඉකුත් වී ඇත" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "පරිශීලක සහතික සැකසීම අසමත් විය" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "අංගවිශේෂිත දත්ත නොමැත" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "දෝෂ සහිත අයිතමයක් pam_*_item() වෙත පිරිනැමුනී" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "පරිවර්තන දෝෂය" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "තහවුරු කරගැනීමේ ටෝකනය සැකසිමේදි දෝෂය" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "තහවුරු කරගැනීමේ තොරතුරු නැවත ලබාගත නොහැක" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "තහවුරු කරගැනීමේ ටෝකන අගුලුව කර්‍යය බහුලයි" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "පැරණි තහවුරු කරගැනීමේ ටෝකනය අක්‍රීයව ඇත" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "රහස්පද සේවයේ ප්‍රාථමික පරික්‍ෂණය අසමත්විය" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "PAM පිටත්කර හැරීම මඟින් ආපසු ලැබෙන අගය නොසලකා හැරිය යුතුය" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "නොදන්නා අංගයකි" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "තහවුරු කරගැනීමේ ටෝකනය කල්ඉකුතවි ඇත" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "පරිවත්තනය අවස්තාවක් සඳහා නැවතී ඇත" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "යෙදුමට පැරණි libpam ඇමතීමට අවශ්‍යව ඇත" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "නොදන්නා PAM දෝෂය" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...කාල සීමාව ඉක්මවා ඇත...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...සමාවන්න, ොබගේ කාලය ඉක්ම විය!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "වැරදි සගත පරිවර්තනයක්(%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s අසමත් විය: ඉවතිවීමෙ කේතය %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s අසමත් විය: සංඥාව අල්ලා ගන්නා ලදි%d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s අසමත් විය: නොදන්නා තත්වය 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s වෙතින්" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s වෙනිදා" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "අවසාන පිවිසුම:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "ඔබගේ නව ගිණුමට සාදරයෙන් පිළිගනිමු!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s' සඳහා බොහෝ පිවිසුම් ගණනක් ඇත." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "ඔබට අලුත් තැපැල් ඇත." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "ඔබට අලුත් තැපැල් ඇත." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "ඔබට පරණ තැපැල් ඇත." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "ඔබට තැපැල් ඇත." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "%s බහලුම තුළ ඔබට තැපැල් නැත." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "%s බහලුම තුළ ඔබට අලුත් තැපැල් ඇත." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "%s බහලුම තුළ ඔබට පරණ තැපැල් ඇත." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "%s බහලුම තුළ ඔබට තැපැල් ඇත." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "රහස්පදය දැනටමත් භාවිතා වේ. වෙනත් එකක් තෝරාගන්න." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +#, fuzzy +msgid "Password has been already used." +msgstr "රහස්පදය දැනටමත් භාවිතා වේ. වෙනත් එකක් තෝරාගන්න." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "නිරවද්‍ය ආරක්‍ෂක ප්‍රකරණයක් නොවේ" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "නිරවද්‍ය ආරක්‍ෂක ප්‍රකරණයක් නොවේ" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "%s ආරක්‍ෂක ප්‍රකරණය යොදවා ඇත" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "%s ආරක්‍ෂක ප්‍රකරණය යොදවා ඇත" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM ඇරඹිම අසමත් විය\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() අසමත් විය\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "පිවිසුම: ෆොර්කින් බිදවැටීමක්: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "ඔබගේ ගිණුම කල්ඉකුත් වී ඇත; කරුණාකර ඔබගේ පද්ධති කළමණාකරු හමුවන්න" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "ඔබගේ රහස්පදය හැකි ඉක්මනින් වෙනස් කළ යුතුව ඇත (root බලකර සිටී)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "ඔබගේ රහස්පදය හැකි ඉක්මනින් වෙනස් කළ යුතුව ඇත (රහස්පදය පැරණියි)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "අවවාදයි: ඔබගේ රහස්පදය දින %d කින් කල්ඉකුත් වේ" +msgstr[1] "අවවාදයි: ඔබගේ රහස්පදය දින %d කින් කල්ඉකුත් වේ" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "අවවාදයි: ඔබගේ රහස්පදය දින %d කින් කල්ඉකුත් වේ" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS රහස්පදය වෙනස් කළ නොහැක." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "රහස්පදය සපයා නැත" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS රහස්පදය වෙනස් කළ නොහැක." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "ඔබ කෙටි මුරපදයක් තෝරා ගත යුතුය." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "ඔබ විසින් දිගු රහස්පදයක් තෝරාගත යුතුම වේ" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "ඔබගේ රහස්පදය වෙනස් කිරීමට බොහෝ වෙලාවක් රැදී සිටීය යුතුම වේ" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "ඔබට අලුත් තැපැල් ඇත." + +#~ msgid "is the same as the old one" +#~ msgstr "එය පැරණි රහස්පදය හා සමාන වේ" + +#~ msgid "is a palindrome" +#~ msgstr "එය පැලින්ඩ්‍රොමයකි" + +#~ msgid "case changes only" +#~ msgstr "කැපිටල් සිම්පල් වෙනස්කම් පමණි" + +#~ msgid "is too similar to the old one" +#~ msgstr "එය පැරණි රහස්පදය බොගොදුරට සමාන වේ" + +#~ msgid "is too simple" +#~ msgstr "එය සරළ වැඩි වේ" + +#~ msgid "is rotated" +#~ msgstr "භ්‍රමණය වි ඇත" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "BAD PASSWORD: %s" + +#~ msgid "Authentication error" +#~ msgstr "තහවුරු කරගැනීමේ දෝෂය" + +#~ msgid "Service error" +#~ msgstr "සේවා දෝෂය" + +#~ msgid "Unknown user" +#~ msgstr "නොදන්නා පරිශීලකයෙක්" + +#~ msgid "Unknown error" +#~ msgstr "නොදන්නා දෝෂයක්" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: සාවද්‍ය අංකයක් ලබා දී ඇත --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: %s හදුනා නොගත් විකල්පයකි\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: ශුන්‍ය නොවන අගයට සියළුම පරිශීලකයින් නැවත සැකසිය නොහැක\n" diff --git a/po/sk.po b/po/sk.po new file mode 100644 index 0000000..590a723 --- /dev/null +++ b/po/sk.po @@ -0,0 +1,595 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# feonsu , 2008 +# feonsu , 2014 +# feonsu , 2008 +# Pavol Šimo , 2009 +# Tomáš Mráz , 2016. #zanata +# Ondrej Sulek , 2020, 2021, 2023. +# Dmitry V. Levin , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2023-05-16 10:21+0000\n" +"Last-Translator: Ondrej Sulek \n" +"Language-Team: Slovak \n" +"Language: sk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"X-Generator: Weblate 4.15.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Heslo: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Aktuálne %s heslo: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Aktuálne heslo: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nové %s heslo: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nové heslo: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Opakujte nové %s heslo: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Opakujte nové heslo: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Prepáčte, heslá sa nezhodujú." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Opakujte %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Zmena hesla zrušená." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "login:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Úspech" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritická chyba - okamžité zrušenie" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Nepodarilo sa načítať modul" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbol nenájdený" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Chyba v module služby" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Chyba systému" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Chyba vyrovnávacej pamäte" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Prístup odmietnutý" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Zlyhanie overenia" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Nedostatočné oprávnenia pre prístup k údajom overenia" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Overovacia služba nemôže získať informácie pre overenie" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Používateľ nie je známy pre podriadený overovací modul" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Vyčerpaný maximálny počet pokusov pre službu" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Overovací token už nie je platný; požadovaný je nový" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Platnosť používateľského účtu vypršala" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Pre zadanú reláciu nie je možné vytvoriť/odstrániť záznam" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Overovacia služba nemôže získať oprávnenia používateľa" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Vypršala platnosť používateľského oprávnenia" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Chyba pri nastavení oprávnení používateľa" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Nie je možné nájsť dáta pre modul" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Funkcii pam_*_item() bola poslaná zlá položka" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Chyba konverzácie" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Chyba pri manipulácii s overovacím tokenom" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Overovaciu informáciu nie je možné obnoviť" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Overovací token je uzamknutý" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Starnutie overovacieho tokenu zakázané" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Zlyhanie predbežnej kontroly v službe hesla" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Návratová hodnota by mala byť ignorovaná mechanizmom PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Neznámy modul" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Vypršala platnosť overovacieho tokenu" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Konverzácia čaká na udalosť" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Aplikácia musí znovu zavolať libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Neznáme chyba PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Odpočet bol spustený...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Prepáčte, váš čas vypršal!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "chybná konverzácia (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s zlyhalo: výstupný kód %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s zlyhalo: dostal signál %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s zlyhalo: neznámy stav 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Použitie: %s [--dir /cesta/k/adresaru] [--user pouzivatelske_meno] [--legacy-" +"output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Login Zlyhaní Posledné zlyhanie Od\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Účet uzamknutý z dôvodu %u neúspešných prihlásení." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(na odomknutie zostáva %d minúta)" +msgstr[1] "(na odomknutie zostáva %d minúty)" +msgstr[2] "(na odomknutie zostáva %d minút)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(odomknutie zostáva %d minút)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %d.%m.%Y %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " z %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " na %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Posledné prihlásenie:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Vitajte vo vašom novom účte!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Posledné neúspešné prihlásenie:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Od posledného úspešného prihlásenia došlo k %d neúspešnému pokusu o " +"prihlásenie." +msgstr[1] "" +"Od posledného úspešného prihlásenia došlo k %d neúspešným pokusom o " +"prihlásenie." +msgstr[2] "" +"Od posledného úspešného prihlásenia došlo k %d neúspešným pokusom o " +"prihlásenie." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Od posledného úspešného prihlásenia došlo k %d neúspešným pokusom o " +"prihlásenie." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Príliš veľa prihlásení pre '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Nemáte žiadnu novú poštu." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Máte novú poštu." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Máte starú poštu." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Máte poštu." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Nemáte žiadnu poštu v priečinku %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Máte novú poštu v priečinku %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Máte starú poštu v priečinku %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Máte poštu v priečinku %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Vytváranie priečinka '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Nedá sa vytvoriť a inicializovať priečinok '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Heslo už bolo použité. Zvoľte si iné." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Heslo už bolo použité." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Predvolený kontext zabezpečenia %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Želáte si zadať inú rolu alebo úroveň?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rola:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Chýba predvolený typ pre rolu %s." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "úroveň:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Neplatný kontext zabezpečenia." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Nie je možné získať platný kontext pre %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Kontext zabezpečenia %s pridelený." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Kontext zabezpečenia pre vytváranie kľúčov %s pridelený." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "chyba pri inicializácii PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "chyba pri pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: chyba forku: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Prístup povolený (posledný prístup pred %ld sekundami)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"Platnosť vášho účtu vypršala; kontaktujte prosím svojho správcu systému." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Je vyžadovaná okamžitá zmena vašeho hesla (vynútené správcom)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Je vyžadovaná okamžitá zmena vašeho hesla (heslo vypršalo)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Upozornenie: vaše heslo vyprší za %d deň." +msgstr[1] "Upozornenie: vaše heslo vyprší za %d dni." +msgstr[2] "Upozornenie: vaše heslo vyprší za %d dní." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Upozornenie: vaše heslo vyprší za %d dní." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Nie je možné zmeniť NIS heslo." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Heslo nezadané." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Heslo nebolo zmenené." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Musíte zvoliť kratšie heslo." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Musíte si zvoliť dlhšie heslo." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Zmena hesla pre %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Na zmenu svojho hesla musíte počkať dlhšie." + +#~ msgid "You have no mail." +#~ msgstr "Nemáte žiadnu poštu." + +#~ msgid "is the same as the old one" +#~ msgstr "je rovnaké ako predchádzajúce" + +#~ msgid "memory allocation error" +#~ msgstr "chyba pridelenia pamäte" + +#~ msgid "is a palindrome" +#~ msgstr "je palindróm" + +#~ msgid "case changes only" +#~ msgstr "len zmena veľkosti" + +#~ msgid "is too similar to the old one" +#~ msgstr "je príliš podobné predchádzajúcemu" + +#~ msgid "is too simple" +#~ msgstr "je príliš jednoduché" + +#~ msgid "is rotated" +#~ msgstr "je otočené" + +#~ msgid "not enough character classes" +#~ msgstr "nedostatok rôznych druhov znakov" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "obsahuje príliš veľa rovnakých znakov za sebou" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "obsahuje príliš jednotvárnu postupnosť znakov" + +#~ msgid "contains the user name in some form" +#~ msgstr "obsahuje v nejakej forme používateľské meno" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "NESPRÁVNE HESLO: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Účet dočasne uzamknutý (zostáva %ld sekúnd)." + +#~ msgid "Authentication error" +#~ msgstr "Chyba overenia" + +#~ msgid "Service error" +#~ msgstr "Chyba služby" + +#~ msgid "Unknown user" +#~ msgstr "Neznámy používateľ" + +#~ msgid "Unknown error" +#~ msgstr "Neznáma chyba" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Zadané zlé číslo pre --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Neznáma voľba %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file meno_suboru] [--user pouzivatelske_meno] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Nedá sa resetovať všetkých používateľov nenulovo\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f meno_suboru] [--file meno_suboru]\n" +#~ " [-u pouzivatelske_meno] [--user pouzivatelske_meno]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/sl.po b/po/sl.po new file mode 100644 index 0000000..5b9764d --- /dev/null +++ b/po/sl.po @@ -0,0 +1,510 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +# Martin Srebotnjak , 2023, 2024. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2024-08-17 17:38+0000\n" +"Last-Translator: Martin Srebotnjak \n" +"Language-Team: Slovenian \n" +"Language: sl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n" +"%100==4 ? 2 : 3;\n" +"X-Generator: Weblate 5.6.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Geslo: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Trenutno geslo %s: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Trenutno geslo: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Novo geslo %s: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Novo geslo: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Ponovno vnesite novo geslo %s: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Ponovno vnesite novo geslo: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Žal se gesli ne ujemata." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Ponovno vnesite %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Sprememba gesla je bila prekinjena." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "prijava:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Uspelo je" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritična napaka - sledi takojšna prekinitev" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Modula ni bilo možno naložiti" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Simbola ni mogoče najti" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Napaka v servisnem modulu" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Sistemska napaka" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Napaka medpomnilnika" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Ni ustreznih dovoljenj" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Napaka overjanja" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Nezadostne poverilnice za dostop do podatkov za preverjanje pristnosti" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" +"Storitev za preverjanje pristnosti ne more pridobiti podatkov za preverjanje " +"pristnosti" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Uporabnik ni znan osnovnemu modulu za preverjanje pristnosti" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Izčrpali so največje število ponovnih poskusov za storitev" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Žeton za preverjanje pristnosti ni več veljaven; potreben je nov" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Uporabniški račun je potekel" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Ni mogoče narediti/odstraniti vnosa za določeno sejo" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" +"Storitev preverjanja pristnosti ne more pridobiti uporabniških poverilnic" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Poverilnice uporabnika so potekle" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Neuspešna nastavitev uporabniških poverilnic" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Ni podatkov, specifičnih za modul" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Slab element posredovan v pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Napaka pogovora" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Napaka pri manipulaciji žetona za preverjanje pristnosti" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Podatkov za preverjanje pristnosti ni mogoče obnoviti" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Zaklepanje žetona za preverjanje pristnosti je zasedeno" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Staranje žetona za preverjanje pristnosti je onemogočeno" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Neuspešno predhodno preverjanje s storitvijo gesla" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Vrnjeno vrednost mora PAM prezreti" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modul ni znan" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Žeton za preverjanje pristnosti je potekel" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Pogovor čaka na dogodek" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Program mora znova poklicati libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Neznana napaka PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "... Čas se izteka ...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "... Žal se je vaš čas iztekel!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "napačen pogovor (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s ni uspel: izhodna koda %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s ni uspel: ujeti signal %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s ni uspel: neznano stanje 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Uporaba: %s [--dir /pot/do/imenika-zabeležke] [--user uporabniškoime] [--" +"reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Prijava Neuspele Zadnja neuspela Iz\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Račun je zaklenjen zaradi %u neuspelih prijav." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d preostala minuta za odklepanje)" +msgstr[1] "(%d preostali minuti za odklepanje)" +msgstr[2] "(%d preostale minute za odklepanje)" +msgstr[3] "(%d preostalih minut za odklepanje)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d preostalih minut za odklepanje)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %d.%m.%Y %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " iz %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " na %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Zadnja prijava:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Dobrodošli v svojem novem računu!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Zadnja neuspešna prijava:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Od zadnje uspešne prijave je bilo %d neuspel poskus prijave." +msgstr[1] "Od zadnje uspešne prijave sta bila %d neuspela poskusa prijave." +msgstr[2] "Od zadnje uspešne prijave so bili %d neuspeli poskusi prijave." +msgstr[3] "Od zadnje uspešne prijave je bilo %d neuspelih poskusov prijave." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Od zadnje uspešne prijave je bilo %d neuspelih poskusov prijave." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Bilo je preveč prijav za »%s«." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Nimate nove pošte." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Imate novo pošto." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Imate staro pošto." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Imate pošto." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "V mapi %s nimate pošte." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "V mapi %s imate novo pošto." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "V mapi %s imate staro pošto." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Imate pošto v mapi %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Ustvarjanje mape %s ..." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Ni mogoče ustvariti in inicializirati mape »%s«." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Geslo je že bilo uporabljeno. Izberite drugo." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Geslo je že bilo uporabljeno." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Privzeti varnostni kontekst je %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Bi radi vstopili v drugo vlogo ali raven?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "vloga:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Privzeta vrsta za vlogo %s ni na voljo." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "raven:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "To ni veljaven varnostni kontekst." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Veljavnega konteksta za %s ni bilo mogoče pridobiti." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Dodeljen je bil varnostni kontekst %s." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Ključni kontekst ustvarjanja %s je bil dodeljen." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "Inicializacija PAM ni uspela\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "neuspešen pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "Prijava: neuspelo vejanje: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Dostop je bil odobren (zadnji dostop je bil pred %ld s)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Vaš račun je potekel; Obrnite se na skrbnika sistema." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Geslo morate takoj spremeniti (zahteva skrbnika)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Geslo morate takoj spremeniti (geslo je poteklo)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Opozorilo: vaše geslo bo poteklo čez %d dan." +msgstr[1] "Opozorilo: vaše geslo bo poteklo čez %d dni." +msgstr[2] "Opozorilo: vaše geslo bo poteklo čez %d dni." +msgstr[3] "Opozorilo: vaše geslo bo poteklo čez %d dni." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Opozorilo: vaše geslo bo poteklo v %d dneh." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Gesla NIS ni bilo možno spremeniti." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Geslo ni bilo podano." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Geslo ni bilo spremenjeno." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Izbrati morate krajše geslo." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Izbrati morate daljše geslo." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Spreminjanje gesla za %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Za spremembo gesla morate počakati dlje." diff --git a/po/sq.po b/po/sq.po new file mode 100644 index 0000000..35af2e2 --- /dev/null +++ b/po/sq.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Albanian (http://www.transifex.com/projects/p/fedora/language/" +"sq/)\n" +"Language: sq\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Ju duhet të zgjidhni një fjalëkalim më të shkurtër." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Ju duhet të zgjidhni një fjalëkalim më të shkurtër." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Ju duhet të zgjidhni një fjalëkalim më të shkurtër." diff --git a/po/sr.po b/po/sr.po new file mode 100644 index 0000000..e1be616 --- /dev/null +++ b/po/sr.po @@ -0,0 +1,599 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Marko Uskokovic , 2007-2008 +# Miloš Komarčević , 2008 +# Momcilo Medic , 2014 +# Nikola Pajtić , 2008 +# Sandra Gucul-Milojevic , 2008 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2014-11-16 06:51-0500\n" +"Last-Translator: Momcilo Medic \n" +"Language-Team: Serbian (http://www.transifex.com/projects/p/linux-pam/" +"language/sr/)\n" +"Language: sr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Лозинка: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "Нова %s лозинка: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "Нова лозинка: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Нова %s лозинка: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Нова лозинка: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Поново унесите нову %s лозинку: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Поново унесите нову лозинку: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Извините, лозинке се не подударају." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Поново унесите %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "Промена лозинке је прекинута." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "пријава:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Успешно" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Критична грешка - прекидам одмах" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Неуспешно учитавање модула" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Симбол није пронађен" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Грешка у услужном модулу" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Системска грешка" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Грешка меморијског бафера" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Дозвола је одбијена" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Неуспешна аутентификација" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Недовољно уверења за приступ подацима аутентификације" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Услуга аутентификације не може да добави информације аутентификације" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Корисник није познат основном модулу аутентификације" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Искоришћен је максимални број покушаја услуге" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Жетон аутентификације више није исправан; неопходан је нови" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Кориснички налог је истекао" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Не могу да направим/уклоним ставку наведене сесије" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Услуга аутентификације не може да добави корисничка уверења" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Корисничка уверења су истекла" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Неуспешно постављање корисничких уверења" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Нису присутни специфични подаци модула" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Лош објекат је прослеђен pam_*_item() функцији" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Грешка у разговору" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Грешка при манипулацији жетоном аутентификације" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Информације о аутентификацији не могу бити повраћене" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Закључавање жетона аутентификације је заузето" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Застаревање жетона аутентификације је искључено" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Неуспешна прелиминарна провера услугом лозинке" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "PAM диспечер треба да игнорише повратну вредност" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Модул је непознат" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Истекао је жетон аутентификације" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Разговор очекује догађај" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Програм мора поново да позове libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Непозната PAM грешка" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Истиче време...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Извините, време вам је истекло!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "разговор пун грешака (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s неуспех: излазни код %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s неуспех: ухваћен сигнал %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s неуспех: непознат статус 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file коренски-називдатотеке] [--user корисничкоиме] [--reset[=n]] [--" +"quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Пријава Неуспеси Последњи неуспех Са\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Налог је закључан због %u неуспелих пријава" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %e. %b %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " са %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " на %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Последња пријава:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Добро дошли на ваш нови налог!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Последња неуспешна пријава:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Био је %d неуспео покушај пријаве од последње успешне пријаве." +msgstr[1] "Било је %d неуспела покушаја пријаве од последње успешне пријаве." +msgstr[2] "Било је %d неуспелих покушаја пријаве од последње успешне пријаве." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Било је %d неуспелих покушаја пријаве од последње успешне пријаве." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "Превише пријава за „%s“." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Имате нову пошту." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Имате нову пошту." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Имате стару пошту." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Имате пошту." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Немате пошту у фасцикли %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Имате нову пошту у фасцикли %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Имате стару пошту у фасцикли %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Имате пошту у фасцикли %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Правим директоријум „%s“." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Не могу да направим директоријум „%s“." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Лозинка је већ у употреби. Изаберите другу." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Шифра је већ била у употреби." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "Подразумевани безбедносни контекст %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Да ли желите да унесете другу улогу или ниво?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "улога:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "Нема подразумеване врсте за улогу %s\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "ниво:" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "Неисправан безбедносни контекст" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "Безбедносни контекст %s је додељен" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "Контекст прављења кључа %s је додељен" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "неуспешна иницијализација PAM-а\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "неуспешно извршавање функције pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "пријава: грешка при гранању: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Приступ је одобрен (последњи приступ је био пре %ld секунди)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "Ваш налог је истекао; обратите се администратору система" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Морате одмах да промените вашу лозинку (наметнуо root)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Морате одмах да промените вашу лозинку (застарела је)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Упозорење: ваша лозинка ће истећи кроз %d дан" +msgstr[1] "Упозорење: ваша лозинка ће истећи кроз %d дана" +msgstr[2] "Упозорење: ваша лозинка ће истећи кроз %d дана" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Упозорење: ваша лозинка ће истећи кроз %d дана" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS лозинка не може бити промењена." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "Није понуђена лозинка" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS лозинка не може бити промењена." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Морате одабрати краћу лозинку." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Морате изабрати дужу лозинку" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Мењам лозинку за %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Морате дуже сачекати на промену лозинке" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "Имате нову пошту." + +#~ msgid "is the same as the old one" +#~ msgstr "иста је као и стара" + +#~ msgid "memory allocation error" +#~ msgstr "грешка у резервисању меморије" + +#~ msgid "is a palindrome" +#~ msgstr "палиндром је" + +#~ msgid "case changes only" +#~ msgstr "само промене величине слова" + +#~ msgid "is too similar to the old one" +#~ msgstr "сувише је слична претходној" + +#~ msgid "is too simple" +#~ msgstr "сувише је једноставна" + +#~ msgid "is rotated" +#~ msgstr "изокренута је" + +#~ msgid "not enough character classes" +#~ msgstr "нема довољно класа знакова" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "садржи превише истих знакова узастопно" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "садржи предугачак низ монотоних карактера" + +#~ msgid "contains the user name in some form" +#~ msgstr "садржи корисничко име у неком облику" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ЛОША ЛОЗИНКА: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Налог је привремено закључан (остало је %ld секунди)" + +#~ msgid "Authentication error" +#~ msgstr "Грешка при аутентификацији" + +#~ msgid "Service error" +#~ msgstr "Грешка услуге" + +#~ msgid "Unknown user" +#~ msgstr "Непознат корисник" + +#~ msgid "Unknown error" +#~ msgstr "Непозната грешка" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: задат је лош број за --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: није препозната опција %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file коренски-називдатотеке] [--user корисничкоиме] [--reset[=n]] " +#~ "[--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: не могу да повратим све кориснике на број различит од нуле\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [- коренски-називдатотеке] [--file коренски-називдатотеке]\n" +#~ " [-u корисничкоиме] [--user корисничкоиме]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/sr@latin.po b/po/sr@latin.po new file mode 100644 index 0000000..4d230f9 --- /dev/null +++ b/po/sr@latin.po @@ -0,0 +1,597 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Marko Uskokovic , 2007, 2008. +# Miloš Komarčević , 2008. +# Nikola Pajtić , 2008. +# Sandra Gucul-Milojevic , 2008. +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:59-0500\n" +"Last-Translator: Tomáš Mráz \n" +"Language-Team: LANGUAGE \n" +"Language: sr@latin\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Lozinka: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "Nova %s lozinka: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "Nova lozinka: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nova %s lozinka: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nova lozinka: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Ponovo unesite novu %s lozinku: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Ponovo unesite novu lozinku: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Izvinite, lozinke se ne podudaraju." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Ponovo unesite %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "Promena lozinke je prekinuta." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "prijava:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Uspešno" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritična greška - prekidam odmah" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Neuspešno učitavanje modula" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Simbol nije pronađen" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Greška u uslužnom modulu" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Sistemska greška" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Greška memorijskog bafera" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Dozvola je odbijena" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Neuspešna autentifikacija" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Nedovoljno uverenja za pristup podacima autentifikacije" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Usluga autentifikacije ne može da dobavi informacije autentifikacije" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Korisnik nije poznat osnovnom modulu autentifikacije" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Iskorišćen je maksimalni broj pokušaja usluge" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Žeton autentifikacije više nije ispravan; neophodan je novi" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Korisnički nalog je istekao" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Ne mogu da napravim/uklonim stavku navedene sesije" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Usluga autentifikacije ne može da dobavi korisnička uverenja" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Korisnička uverenja su istekla" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Neuspešno postavljanje korisničkih uverenja" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Nisu prisutni specifični podaci modula" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Loš objekat je prosleđen pam_*_item() funkciji" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Greška u razgovoru" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Greška pri manipulaciji žetonom autentifikacije" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Informacije o autentifikaciji ne mogu biti povraćene" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Zaključavanje žetona autentifikacije je zauzeto" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Zastarevanje žetona autentifikacije je isključeno" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Neuspešna preliminarna provera uslugom lozinke" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "PAM dispečer treba da ignoriše povratnu vrednost" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modul je nepoznat" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Istekao je žeton autentifikacije" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Razgovor očekuje događaj" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Program mora ponovo da pozove libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Nepoznata PAM greška" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Ističe vreme...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Izvinite, vreme vam je isteklo!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "razgovor pun grešaka (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s neuspeh: izlazni kod %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s neuspeh: uhvaćen signal %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s neuspeh: nepoznat status 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file korenski-nazivdatoteke] [--user korisničkoime] [--reset[=n]] [--" +"quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Prijava Neuspesi Poslednji neuspeh Sa\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Nalog je zaključan zbog %u neuspelih prijava" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" +msgstr[2] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %e. %b %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " sa %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " na %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Poslednja prijava:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Dobro došli na vaš novi nalog!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Poslednja neuspešna prijava:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Bio je %d neuspeo pokušaj prijave od poslednje uspešne prijave." +msgstr[1] "Bilo je %d neuspela pokušaja prijave od poslednje uspešne prijave." +msgstr[2] "Bilo je %d neuspelih pokušaja prijave od poslednje uspešne prijave." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "Bilo je %d neuspelih pokušaja prijave od poslednje uspešne prijave." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "Previše prijava za „%s“." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Imate novu poštu." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Imate novu poštu." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Imate staru poštu." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Imate poštu." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Nemate poštu u fascikli %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Imate novu poštu u fascikli %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Imate staru poštu u fascikli %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Imate poštu u fascikli %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Pravim direktorijum „%s“." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Ne mogu da napravim direktorijum „%s“." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Lozinka je već u upotrebi. Izaberite drugu." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +#, fuzzy +msgid "Password has been already used." +msgstr "Lozinka je već u upotrebi. Izaberite drugu." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "Podrazumevani bezbednosni kontekst %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Da li želite da unesete drugu ulogu ili nivo?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "uloga:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "Nema podrazumevane vrste za ulogu %s\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nivo:" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "Neispravan bezbednosni kontekst" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "Bezbednosni kontekst %s je dodeljen" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "Kontekst pravljenja ključa %s je dodeljen" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "neuspešna inicijalizacija PAM-a\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "neuspešno izvršavanje funkcije pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "prijava: greška pri grananju: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Pristup je odobren (poslednji pristup je bio pre %ld sekundi)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "Vaš nalog je istekao; obratite se administratoru sistema" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Morate odmah da promenite vašu lozinku (nametnuo root)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Morate odmah da promenite vašu lozinku (zastarela je)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Upozorenje: vaša lozinka će isteći kroz %d dan" +msgstr[1] "Upozorenje: vaša lozinka će isteći kroz %d dana" +msgstr[2] "Upozorenje: vaša lozinka će isteći kroz %d dana" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Upozorenje: vaša lozinka će isteći kroz %d dana" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS lozinka ne može biti promenjena." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "Nije ponuđena lozinka" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS lozinka ne može biti promenjena." + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Morate izabrati dužu lozinku" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Morate izabrati dužu lozinku" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Menjam lozinku za %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Morate duže sačekati na promenu lozinke" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "Imate novu poštu." + +#~ msgid "is the same as the old one" +#~ msgstr "ista je kao i stara" + +#~ msgid "is a palindrome" +#~ msgstr "palindrom je" + +#~ msgid "case changes only" +#~ msgstr "samo promene veličine slova" + +#~ msgid "is too similar to the old one" +#~ msgstr "suviše je slična prethodnoj" + +#~ msgid "is too simple" +#~ msgstr "suviše je jednostavna" + +#~ msgid "is rotated" +#~ msgstr "izokrenuta je" + +#~ msgid "not enough character classes" +#~ msgstr "nema dovoljno klasa znakova" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "sadrži previše istih znakova uzastopno" + +#, fuzzy +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "sadrži previše istih znakova uzastopno" + +#~ msgid "contains the user name in some form" +#~ msgstr "sadrži korisničko ime u nekom obliku" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "LOŠA LOZINKA: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Nalog je privremeno zaključan (ostalo je %ld sekundi)" + +#~ msgid "Authentication error" +#~ msgstr "Greška pri autentifikaciji" + +#~ msgid "Service error" +#~ msgstr "Greška usluge" + +#~ msgid "Unknown user" +#~ msgstr "Nepoznat korisnik" + +#~ msgid "Unknown error" +#~ msgstr "Nepoznata greška" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: zadat je loš broj za --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: nije prepoznata opcija %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file korenski-nazivdatoteke] [--user korisničkoime] [--reset[=n]] " +#~ "[--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: ne mogu da povratim sve korisnike na broj različit od nule\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [- korenski-nazivdatoteke] [--file korenski-nazivdatoteke]\n" +#~ " [-u korisničkoime] [--user korisničkoime]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/sv.po b/po/sv.po new file mode 100644 index 0000000..945b8cc --- /dev/null +++ b/po/sv.po @@ -0,0 +1,589 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Christer Andersson , 2007 +# Daniel Nylander , 2009-2010 +# Göran Uddeborg , 2012-2013 +# Tomáš Mráz , 2016. #zanata +# Andreas Henriksson , 2020. +# Dmitry V. Levin , 2021. +# Luna Jernberg , 2022. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-11-15 18:19+0000\n" +"Last-Translator: Luna Jernberg \n" +"Language-Team: Swedish \n" +"Language: sv\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.14.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Lösenord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Nuvarande %s lösenord: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Nuvarande lösenord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Nytt %s lösenord: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Nytt lösenord: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Ange nytt %s lösenord igen: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Ange nytt lösenord igen: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Tyvärr, lösenorden stämmer inte överens." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Ange %s igen" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Ändring av lösenordet avbröts." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "inloggning:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Lyckades" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritiskt fel - avbryter omedelbart" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Misslyckades med att läsa in modulen" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Symbolen hittades inte" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Fel i tjänstemodul" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Systemfel" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Minnesbuffertfel" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Åtkomst nekad" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Autentiseringsfel" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Otillräckliga inloggingsuppgifter för åtkomst av autentiseringsdata" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Autentiseringstjänsten kan inte hämta autentiseringsinformation" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Användaren är inte känd för underliggande autentiseringsmodul" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Maximalt antal försök har gjorts för denna tjänst" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Autentiseringselement är inte längre giltigt. Ett nytt behövs" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Användarkontot har förfallit" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Kan inte skapa/ta bort en post för angiven session" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Autentiseringstjänst kan inte hämta användarreferenser" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Användarreferenser har förfallit" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Misslyckades med att ange användarreferenser" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Ingen modulspecifik data finns" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Ogiltigt objekt skickat till pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Konversationsfel" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Manipuleringsfel för autentiseringselement" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Autentiseringsinformation kan inte återställas" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Autentiseringselementlås upptaget" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Åldrande av autentiseringselement inaktiverat" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Lösenordstjänstens preliminära kontroll misslyckades" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Returvärdet borde ignoreras vid PAM-avsändande" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modulen är okänd" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Autentiseringselement har förfallit" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Konversation väntar på händelse" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Programmet behöver anropa libpam igen" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Okänt PAM-fel" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Tiden håller på att ta slut...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Tyvärr, din tid är ute!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "felaktig konversation (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s misslyckades: slutstatus %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s misslyckades: fångade signalen %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s misslyckades: okänd status 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Användning: %s [--dir /sökväg/till/tally-katalog] [--user användarnamn] [--" +"reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Inloggning Misslyck Senaste fel Från\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Kontot är låst på grund av %u misslyckade inloggningar." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d minut kvar till upplåsning)" +msgstr[1] "(%d minuter kvar till upplåsning)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d minuter kvar till upplåsning)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %e %b %Y %H.%M.%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " från %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " på %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Senaste inloggning:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Välkommen till ditt nya konto!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Senaste misslyckade inloggning:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Det har skett %d misslyckat inloggningsförsök sedan senaste korrekta " +"inloggning." +msgstr[1] "" +"Det har skett %d misslyckade inloggningsförsök sedan senaste korrekta " +"inloggning." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Det har skett %d misslyckade inloggningsförsök sedan senaste korrekta " +"inloggning." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "För många inloggningar för '%s'." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Du har inga nya brev." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Du har nya brev." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Du har gamla brev." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Du har brev." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Du har inga brev i katalogen %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Du har nya brev i katalogen %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Du har gamla brev i katalogen %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Du har brev i katalogen %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Skapar katalogen '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Kunde inte skapa och initiera katalogen '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Lösenordet har redan används. Välj ett annat." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Lösenordet har redan använts." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Standardsäkerhetskontexten är %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Vill du ange en annan roll eller nivå?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "roll:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Ingen standardttyp för %s-roll." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "nivå:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Inte en giltig säkerhetskontext." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "En giltig kontext för %s kunde inte erhållas." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Säkerhetskontext %s tilldelad." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Nyckelskapandekontext %s tilldelad." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "misslyckades med att initiera PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() misslyckades\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "inloggning: fel vid grening: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Åtkomst godkänd (senaste åtkomst var %ld sekunder sedan)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Ditt konto har förfallit. Vänligen kontakta din systemadministratör." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Du måste ändra ditt lösenord omedelbart (påtvingat av administratör)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Du måste ändra ditt lösenord omedelbart (lösenord för gammalt)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Varning: ditt lösenord går ut om %d dag." +msgstr[1] "Varning: ditt lösenord går ut om %d dagar." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Varning: ditt lösenord går ut om %d dagar." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS-lösenord kunde inte ändras." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Inget lösenord angavs." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Lösenordet har inte ändrats." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Du måste välja ett kortare lösenord." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Du måste välja ett längre lösenord." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Ändrar lösenord för %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Du måste vänta längre innan du kan ändra lösenord." + +#~ msgid "You have no mail." +#~ msgstr "Du har inga brev." + +#~ msgid "is the same as the old one" +#~ msgstr "är samma som det gamla" + +#~ msgid "memory allocation error" +#~ msgstr "minnesallokeringsfel" + +#~ msgid "is a palindrome" +#~ msgstr "är ett palindrom" + +#~ msgid "case changes only" +#~ msgstr "endast ändringar i gemener och versaler" + +#~ msgid "is too similar to the old one" +#~ msgstr "är för likt det gamla" + +#~ msgid "is too simple" +#~ msgstr "är för enkelt" + +#~ msgid "is rotated" +#~ msgstr "är roterat" + +#~ msgid "not enough character classes" +#~ msgstr "för få teckenklasser" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "innehåller för många tecken av samma sort i följd" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "innehåller en för lång monoton teckensekvens" + +#~ msgid "contains the user name in some form" +#~ msgstr "innehåller användarnamnet i någon form" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "DÅLIGT LÖSENORD: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Kontot är temporärt låst (%ld sekunder kvar)." + +#~ msgid "Authentication error" +#~ msgstr "Autentiseringsfel" + +#~ msgid "Service error" +#~ msgstr "Tjänstefel" + +#~ msgid "Unknown user" +#~ msgstr "Okänd användare" + +#~ msgid "Unknown error" +#~ msgstr "Okänt fel" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Felaktigt nummer till --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Okänd flagga %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file absolut-filnamn] [--user användarnamn] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Kan inte ställa om alla användare till nollskilt värde\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f absolut-filnamn] [--file absolut-filnamn]\n" +#~ " [-u användarnamn] [--user användarnamn]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ta.po b/po/ta.po new file mode 100644 index 0000000..8ad7d82 --- /dev/null +++ b/po/ta.po @@ -0,0 +1,593 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Arun Prakash , 2012 +# I felix , 2007 +# I. Felix , 2009 +# shkumar , 2013 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-03-06 23:59+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Tamil \n" +"Language: ta\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.11.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "கடவுச்சொல்:" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "புதிய %s password: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "புதிய password: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "புதிய %s password: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "புதிய password: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "புதிய %s password மீண்டும் உள்ளிடவும்: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "புதிய password மீண்டும் உள்ளிடவும்: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "கடவுச்சொல் பொருந்தவில்லை." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%sஐ மறு தட்டச்சு செய்" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "கடவுச்சொல் மாற்றம் கைவிடப்பட்டது." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "புகுபதிவு:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "வெற்றி" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "சிக்கலான பிழை - உடனடியாக நிறுத்தப்பட்டது" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "தொகுதியை ஏற்ற முடியவில்லை" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "சிக்னல் கிடைக்கவில்லை" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "சேவை தொகுதியில் பிழை" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "கணினி பிழை" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "நினைவக இடையக பிழை" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "அனுமதி இல்லை" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "உரிமம் செயலிழக்கப்பட்டது" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "உரிம தரவினை அணுக போதிய உரிமங்கள் இல்லை" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "உரிம சேவை உரிம தகவலை எடுக்க முடியவில்லை" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "உரிம தொகுதியை எடுக்க பயனருக்கு தெரியவில்லை" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "சேவைக்கு அதிகபட்ச எண்ணிக்கையான முயற்சி கொண்டது" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "உரிம டோக்கன் தவறானது; புதிய ஒன்று தேவைப்படுகிறது" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "பயனர் கணக்கு முடிவுற்றது" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "குறிப்பிட்ட அமர்வுக்கு உள்ளீட்டை உருவாக்க/நீக்க முடியவில்லை" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "உரிம சேவை பயனர் சன்மானத்தை எடுக்க முடியவில்லை" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "பயனர் சன்மானம் முடிவுற்றது" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "செயலிழக்கப்பட்ட அமைவு பயனர் சன்மானங்கள்" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "தொகுதி குறிப்பிட்ட தகவல் இல்லை" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item() க்கு தவறான உருப்படி அனுப்பப்பட்டது" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "உரையாடல் பிழை" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "உரிம டோக்கன் கணக்கீடு பிழை" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "உரிம தகவலை எடுக்க முடியவில்லை" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "உரிம டோக்கன் பூட்டு செயலில் உள்ளது" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "உரிம டோக்கன் செயலிழக்கப்பட்டது" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "கடவுச்சொல் சேவையால் முதல் சோதனை செய்ய முடியவில்லை" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "கொடுக்கப்பட்ட மதிப்பு PAM dispatch ஆல் தவிர்க்கப்பட்ட வேண்டும்" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "தொகுதியை தெரியவில்லை" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "உரிம டோக்கற் முடிவுற்றது" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "உரையாடல் நிகழ்வுக்காக காத்திருக்கிறது" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "பயன்பாடு libpam ஐ மீண்டும் அழைக்க வேண்டும்" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "தெரியாத PAM பிழை" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "... நேரம் போய் கொண்டிருக்கிறது...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "... உங்கள் நேரம் முடிந்தது!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "பிழையான உரையாடல் (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s செயலிழக்கப்பட்டது: வெளியேறும் குறியீடு %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s செயலிழக்கப்பட்டது: சிக்னல் %d%s பிடிக்கப்பட்டது" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s செயலிழக்கப்பட்டது: தெரியாத நிலை 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "புகுபதிவு கடைசி தோல்வி தோல்வியடைந்தது இங்கிருந்து\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u தோல்வி புகுபதிவுகளால் கணக்கு பூட்டப்பட்டுள்ளது" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s இலிருந்து" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s இல்" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "கடைசி புகுபதிவு:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "உங்கள் புதிய கணக்கு வரவேற்கப்படுகிறீர்கள்!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "கடைசி தோல்வியடைந்த புகுபதிவு:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "கடைசி புகுபதிவிலிருந்து %d புகுபதிவு முயற்சி தோல்வியடைந்தது." +msgstr[1] "கடைசி புகுபதிவிலிருந்து %d புகுபதிவு முயற்சிகள் தோல்வியடைந்தன." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "கடைசி புகுபதிவிலிருந்து %d புகுபதிவு முயற்சி தோல்வியடைந்தன." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s'க்கு பல புகுபதிவுகள் உள்ளன." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "உங்களுக்கு புதிய அஞ்சல் உள்ளது." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "உங்களுக்கு புதிய அஞ்சல் உள்ளது." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "உங்களுக்கு பழைய அஞ்சல் உள்ளது." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "உங்களுக்கு அஞ்சல் உள்ளது." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "உங்களுக்கு %s அடைவில் அஞ்சல் இல்லை." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "உங்களுக்கு %s அடைவில் புதிய அஞ்சல் உள்ளது." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "உங்களுக்கு %s அடைவில் பழைய அஞ்சல் உள்ளது." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "உங்களுக்கு %s அடைவில் அஞ்சல் உள்ளது." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "அடைவு '%s'ஐ உருவாக்குகிறது." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "அடைவு '%s'ஐ உருவாக்க மற்றும் துவக்க முடியவில்லை." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "கடவுச்சொல் ஏற்கனவே பயன்படுத்தப்பட்டது. வேறொன்றை பயன்படுத்தவும்." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "கடவுச்சொல் ஏற்கனவே பயன்படுத்தப்பட்டுள்ளது." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "முன்னிருப்பு பாதுகாப்பு சூழல் %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "நீங்கள் வேறு பங்கு அல்லது நிலையை உள்ளிட வேண்டுமா?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "பங்கு:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "பங்கு %sக்கு முன்னிருப்பு வகை இல்லை\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "நிலை:" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "இது சரியான பாதுகாப்பு சூழல் இல்லை" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "பாதுகாப்பு சூழல் %s ஒதுக்கப்பட்டது" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "விசை உருவாக்க சூழல் %s ஒதுக்கப்பட்டுள்ளது" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAMஐ ஆரம்பிக்க முடியவில்லை\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() செயலிழக்கப்பட்டது\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: failure forking: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "அணுகல் வழங்கப்பட்டது (கடைசி அணுகல் %ld விநாடிகளுக்கு முன்)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "உங்கள் கணக்கு முடிவுற்றது, உங்கள் கணினி நிர்வாகியை அணுகவும்" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "நீங்கள் உங்கள் கடவுச்சொல்லை உடனடியாக மாற்ற வேண்டும் (ரூட் வலியுறுத்துகிறது)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "நீங்கள் உங்கள் கடவுச்சொல்லை உடனடியாக மாற்ற வேண்டும் (கடவுச்சொல் மூப்பாகிவிட்டது)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "எச்சரிக்கை: கடவுச்சொல் %d நாட்களில் முடிவுறும்" +msgstr[1] "எச்சரிக்கை: கடவுச்சொல் %d நாட்களில் முடிவுறும்" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "எச்சரிக்கை: கடவுச்சொல் %d நாட்களில் முடிவுறும்" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS கடவுச்சொல்லை மாற்ற முடியாது." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "கடவுச்சொல் கொடுக்கப்படவில்லை" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS கடவுச்சொல்லை மாற்ற முடியாது." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "குறுகிய கடவுச்சொல்லை நீங்கள் தேர்வு செய்ய வேண்டும்." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "நீங்கள் நீண்ட கடவுச்சொல்லை தேர்ந்தெடுக்க வேண்டும்" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%sக்கு கடவுச்சொல்லை மாற்றுகிறது." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "உங்கள் கடவுச்சொல்லை மாற்ற சிறிது காத்திருக்க வேண்டும்" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "உங்களுக்கு புதிய அஞ்சல் உள்ளது." + +#~ msgid "is the same as the old one" +#~ msgstr "இது பழையதைப் போல உள்ளது" + +#~ msgid "memory allocation error" +#~ msgstr "நினைவக ஒதுக்கீட்டில் பிழை" + +#~ msgid "is a palindrome" +#~ msgstr "இது ஒரு palindrome" + +#~ msgid "case changes only" +#~ msgstr "எழுத்து வகை மாற்றங்கள் மட்டும்" + +#~ msgid "is too similar to the old one" +#~ msgstr "இது பழையதை ஒத்தே உள்ளது" + +#~ msgid "is too simple" +#~ msgstr "இது மிகவும் எளிதாக உள்ளது" + +#~ msgid "is rotated" +#~ msgstr "இது சுழலக்கூடியது" + +#~ msgid "not enough character classes" +#~ msgstr "போதிய எழுத்து வகுப்புகள் இல்லை" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "நிறைய அதே எழுத்துக்கள் தொடர்ந்து கொண்டுள்ளது" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "நீளமான ஒரு மோனோடோனிக் எழுத்துத் தொடரைக் கொண்டுள்ளது" + +#~ msgid "contains the user name in some form" +#~ msgstr "சில வடிவல் பயனர் பெயரை கொண்டுள்ளது" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "தவறான கடவுச்சொல்: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "கணக்கு தற்காலிகமாக பூட்டப்பட்டுள்ளது (%ld விநாடிகள் உள்ளன)" + +#~ msgid "Authentication error" +#~ msgstr "உரிம பிழை" + +#~ msgid "Service error" +#~ msgstr "சேவை பிழை" + +#~ msgid "Unknown user" +#~ msgstr "தெரியாத பயனர்" + +#~ msgid "Unknown error" +#~ msgstr "தெரியாத பிழை" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: தவறான எண் --reset= க்கு கொடுக்கப்பட்டுள்ளது\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: அங்கீகரிக்கப்படாத விருப்பம் %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: பூஜ்ஜியமில்லாததற்கு அனைத்து பயனர்களையும் மறு அமைக்க முடியவில்லை\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/te.po b/po/te.po new file mode 100644 index 0000000..3da0c3c --- /dev/null +++ b/po/te.po @@ -0,0 +1,590 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Krishnababu Krothapalli , 2008-2009 +# sudheesh001 , 2013 +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2013-04-30 07:58-0400\n" +"Last-Translator: sudheesh001 \n" +"Language-Team: Telugu (http://www.transifex.com/projects/p/fedora/language/" +"te/)\n" +"Language: te\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "సంకేతపదము: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "కొత్త %s సంకేతపదము: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "కొత్త సంకేతపదము: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "కొత్త %s సంకేతపదము: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "కొత్త సంకేతపదము: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "కొత్త %s సంకేతపదమును మరలాటైపుచేయుము: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "కొత్త సంకేతపదమును మరలాటైపుచేయుము: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "క్షమించాలి, సంకేతపదము సరిపోలలేదు." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "తిరిగిటైపుచేయి %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "సంకేతపదము మార్పు తప్పించబడింది" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "లాగిన్:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "సఫలం" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "సంక్లిష్ట దోషము - తక్షణ యెబార్టు" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "మాడ్యూల్ లోడుచేయుటకు విఫలమైంది" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "చిహ్నము కనబడలేదు" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "సేవా మాడ్యూల్‌నందు దోషము" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "సిస్టమ్ దోషము" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "మెమొరీ బఫర్ దోషము" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "అనుమతి నిరాకరించబడింది" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "దృవీకరణ వైఫల్యము" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "దృవీకరణ డాటాను యాక్సిస్ చేయుటకు సరిపోని ఆనవాళ్ళు" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "దృవీకరణ సేవ దృవీకరణ సమాచారమును వెలికితీయలేక పోయింది" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "క్రిందనవున్న దృవీకరణ మాడ్యూల్‌కు వినియోగదారి తెలియదు" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "సేవకొరకు గరిష్ట సంఖ్యలో పునఃప్రయత్నాలను కలిగివుంది" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "దృవీకరణ టోకెన్ అవసరములేదు, కొత్తది అవసరము" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "వినియోగదారి ఖాతా కాలముతీరినది" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "తెలుపబడి సెషన్‌కు యెటువంటి ప్రవేశమును చెయలేవు/తొలగించలేవు" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "దృవీకరణ సేవ వినియోగదారి ఆనవాళ్ళను వెలికితీయలేదు" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "వినియోగదారి ఆనవాళ్ళు కాలముతీరినవి" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "వినియోగదారి ఆనవాళ్ళను అమర్చుటలో విఫలము" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "మాడ్యూల్ ప్రత్యేకమైన డాటాలేదు" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item() ద్వారా చెడ్డఅంశము వెళ్ళింది" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "సంభాషణా దోషము" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "దృవీకరణ టోకెన్ మానిప్యులేషన్ దోషము" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "దృవీకరణ సమాచారము తిరిగిపొందబడదు" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "దృవీకరణ టోకెన్ లాక్ బ్యుజీగావుంది" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "దృవీకరణ టోకెన్ యేజింగ్ అచేతనమైంది" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "సంకేతపదము సేవద్వారా ప్రాధమిక పరిశీలన విఫలమైంది" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "తిరిగియిచ్చు విలువ PAM పంపిణీచేత వదిలివేయబడాలి" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "మాడ్యూల్ తెలియనిది" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "దృవీకరణ టోకెన్ కాలముతీరినది" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "సంభాషణ ఘటనకొరకు వేచివుంది" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "libpamను అనువర్తనము మరలా కాల్‌చేయవలసివుంది" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "తెలియని PAM దోషము" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...సమయం అయిపోతోంది...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...క్షమించాలి, మీ సమయం అయిపోయింది!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "తప్పుడు సంభాషణలు (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s విఫలమైంది: బహిష్కరణ కోడ్ %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s విఫలమైంది: సంకేతము %d%s పొదింది" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s విఫలమైంది: తెలియని స్థితి 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "లాగిన్ విఫలమైంది సరికొత్త వైఫల్యం దీనినుండి\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u లాగిన్‌ల వైఫల్యం కారణంగా ఖాతా లాక్అయింది" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s నుండి" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s పైన" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "చివరి లాగిన్:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "మీ కొత్త ఖాతాకు స్వాగతము!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "చివరిగా విఫలమైన లాగిన్:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "చివరి సమర్ధవంతపు లాగిన్‌నుండి ఆక్కడ %d విఫల లాగిన్ ప్రయత్నం వుంది." +msgstr[1] "చివరి సమర్ధవంతపు లాగిన్‌నుండి ఆక్కడ %d విఫల లాగిన్ ప్రయత్నాలు వున్నాయి." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "చివరి సమర్ధవంతపు లాగిన్‌నుండి ఆక్కడ %d విఫల లాగిన్ ప్రయత్నాలు వున్నాయి." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "'%s' కొరకు మరీయెక్కువ లాగిన్‌లు" + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "మీరు కొత్త మెయిల్ కలిగివున్నారు." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "మీరు కొత్త మెయిల్ కలిగివున్నారు." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "మీరు పాత మెయిల్ కలిగివున్నారు." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "మీరు మెయిల్ కలిగివున్నారు." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "మీరు ఫోల్డరు %sనందు యెటువంటి మెయిల్ కలిగిలేరు." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "మీరు ఫోల్డరు %sనందు కొత్త మెయిల్‌ను కలిగివున్నారు." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "మీరు ఫోల్డరు %sనందు పాతమెయిల్‌ను కలిగివున్నారు." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "మీరు ఫోల్డరు %sనందు మెయిల్‌ను కలిగివున్నారు." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "డెరెక్టరీ '%s' సృష్టించుట." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "డైరెక్టరీ %sను సృష్టించలేక పోయింది మరియు సిద్దీకరించలేక పోయింది." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "సంకేతపదము యిప్పటికే వుపయోగించబడింది. మరియొకదానిని యెంచుకొనుము." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "సంకేతపదము యిప్పటికే వుపయోగించబడింది." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "అప్రమేయ రక్షణ సందర్భము %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "మీరు విభిన్న పాత్రను లేదా స్థాయిని ప్రవేశపెడదామని అనుకుంటున్నారా?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "పాత్ర:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "పాత్ర %sకొరకు యెటువంటి అప్రమేయ రకములేదు\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "స్థాయి:" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "విలువైన రక్షణ సందర్భముకాదు" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "రక్షణ సందర్భము %s అప్పగించబడింది" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "కీ సృష్టీకరణ సందర్భము %s అప్పగించబడింది" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM సిద్దముచేయుటలో విఫలమైంది\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() విఫలమైంది\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "login: failure forking: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "యాక్సిస్ యివ్వబడింది (చివరిగా యాక్సిస్ చేసినది %ld సెకనుల క్రితం)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "మీ ఖాతా కాలముతీరినది; దయచేసి మీ సిస్టమ్ నిర్వాహకుడిని సంప్రదించండి" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "మీరు మీ సంకేతపదమును తక్షణమే మార్చవలసివుంది (root enforced)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "మీరు మీ సంకేతపదమును తక్షణమే మార్చవలసివుంది (password aged)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "హెచ్చరిక: మీ సంకేతపదము %d రోజులో కాలముతీరుతుంది" +msgstr[1] "హెచ్చరిక: మీ సంకేతపదము %d రోజులలో కాలముతీరుతుంది" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "హెచ్చరిక: మీ సంకేతపదము %d రోజులలో కాలముతీరుతుంది" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS సంకేతపదము మార్చబడ లేకపోయింది." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "ఎటువంటి సంకేతపదము యివ్వలేదు" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS సంకేతపదము మార్చబడ లేకపోయింది." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "మీరు తప్పనిసరిగా తక్కువ పాస్‌వర్డ్‌ను ఎంచుకోవాలి." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "మీరు తప్పక పొడవాటి సంకేతపదమును యెంచుకొనవలెను." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s కొరకు సంకేతపదమును మార్చుతోంది" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "మీ సంకేతపదమును మార్చుటకు మీరు ఎక్కువసేపు వేచివుండాలి" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "మీరు కొత్త మెయిల్ కలిగివున్నారు." + +#~ msgid "is the same as the old one" +#~ msgstr "ఇది పాతదేనా" + +#~ msgid "memory allocation error" +#~ msgstr "సంభాషణా దోషము" + +#~ msgid "is a palindrome" +#~ msgstr "పాలిండ్రోమా" + +#~ msgid "case changes only" +#~ msgstr "కేస్ మార్పులు మాత్రమే" + +#~ msgid "is too similar to the old one" +#~ msgstr "పాతదానికి మరీ దగ్గరపోలికగావుంది" + +#~ msgid "is too simple" +#~ msgstr "మరీ సరళంగావుంది" + +#~ msgid "is rotated" +#~ msgstr "ఇది పర్యాయంగానా" + +#~ msgid "not enough character classes" +#~ msgstr "సరిపోవునంత కారెక్టర్ క్లాసెస్ లేవు" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "ఒకదానితర్వాత వొకటి అదే అక్షరాలు చాలావున్నాయి" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "ఒకదానితర్వాత వొకటి అదే అక్షరాలు చాలావున్నాయి" + +#~ msgid "contains the user name in some form" +#~ msgstr "ఒకరకంగా వినియోగదారి నామమును కలిగివుంది" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "చెడ్డ సంకేతపదము: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "ఖాతా తాత్కాలికంగా లాక్‌చేయబడింది (%ld సెకనులు మిగిలినవి)" + +#~ msgid "Authentication error" +#~ msgstr "దృవీకరణం దోషము" + +#~ msgid "Service error" +#~ msgstr "సేవ దోషము" + +#~ msgid "Unknown user" +#~ msgstr "తెలియని వినియోగదారి" + +#~ msgid "Unknown error" +#~ msgstr "తెలియని దోషము" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s:చెడ్డ సంఖ్య యివ్వబడింది --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: గుర్తించని ఐచ్చికము %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: వినియోగదారులనందరిని సున్నా-కానిదానికి తిరిగివుంచలేము\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/tg.po b/po/tg.po new file mode 100644 index 0000000..46d634d --- /dev/null +++ b/po/tg.po @@ -0,0 +1,500 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Tajik (http://www.transifex.com/projects/p/fedora/language/" +"tg/)\n" +"Language: tg\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "Шумо бояд гузарвожаи кӯтоҳтарро интихоб кунед." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Шумо бояд гузарвожаи кӯтоҳтарро интихоб кунед." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Шумо бояд гузарвожаи кӯтоҳтарро интихоб кунед." diff --git a/po/th.po b/po/th.po new file mode 100644 index 0000000..e8162c7 --- /dev/null +++ b/po/th.po @@ -0,0 +1,497 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Thai (http://www.transifex.com/projects/p/fedora/language/" +"th/)\n" +"Language: th\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "คุณต้องเลือกรหัสผ่านที่สั้นกว่า" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "คุณต้องเลือกรหัสผ่านที่สั้นกว่า" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "คุณต้องเลือกรหัสผ่านที่สั้นกว่า" diff --git a/po/tr.po b/po/tr.po new file mode 100644 index 0000000..57efd9e --- /dev/null +++ b/po/tr.po @@ -0,0 +1,587 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Bahadır Kandemir , 2010 +# ismail yenigül , 2013 +# Koray Löker , 2006 +# Onuralp SEZER , 2012 +# Selim Şumlu , 2012 +# Tomáš Mráz , 2016. #zanata +# Oğuz Ersen , 2020, 2021. +# Oğuz Ersen , 2022. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-11-13 09:19+0000\n" +"Last-Translator: Oğuz Ersen \n" +"Language-Team: Turkish \n" +"Language: tr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n>1);\n" +"X-Generator: Weblate 4.14.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Parola: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Mevcut %s parolası: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Mevcut parola: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Yeni %s parolası: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Yeni parola: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Yeni %s parolasını tekrar girin: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Yeni parolayı tekrar girin: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Üzgünüm, parolalar birbirine uymuyor." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "%s'i tekrar girin" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Parola değişikliği iptal edildi." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "giriş:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Başarılı" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Kritik hata - şimdi çıkılıyor" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Modül yüklenemedi" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Sembol bulunamadı" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Hizmet modülünde hata" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Sistem hatası" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Tampon bellek hatası" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "İzin verilmedi" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Yetkilendirme hatası" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Kimlik bilgisine ulaşmak için yetersiz yetki" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Yetkilendirme hizmeti, yetkilendirme bilgisini alamıyor" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Kullanıcı, yetkilendirme modülü altyapısında tanımlı değil" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Hizmet için azami deneme sayısı tükendi" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Yetkilendirme anahtarı artık geçerli değil; yenisi gerekiyor" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Kullanıcı hesabının süresi doldu" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Belirtilen oturum için girdi yapılamıyor/silinemiyor" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Yetkilendirme hizmeti kullanıcı kimliğini getiremiyor" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Kullanıcı kimliğinin süresi doldu" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Kullanıcı kimlik bilgileri ayarlanamadı" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Modüle özgü veri yok" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "pam_*_item() fonksiyonuna yanlış öğe geçildi" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Etkileşim hatası" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Yetkilendirme anahtarı manipülasyon hatası" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Yetkilendirme bilgisi kurtarılamadı" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Yetkilendirme anahtarının kilidi meşgul" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Yetkilendirme anahtarı vadesi pasifleştirildi" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Parola hizmeti ön denetimi başarısız oldu" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Dönüş değeri PAM dispatch tarafından gözardı edilmeli" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Modül bilinmiyor" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Yetkilendirme anahtarının süresi doldu" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Etkileşim için olay bekleniyor" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Uygulamanın libpam kütüphanesini yeniden çağırması gerekiyor" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Bilinmeyen PAM hatası" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Zaman geçiyor...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Üzgünüm, süreniz doldu!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "hatalı etkileşim (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s başarısız: çıkış kodu %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s başarısız: %d%s sinyali yakalandı" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s başarısız: bilinmeyen durum 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Kullanım: %s [--dir KayıtlarınTutulduğuDizininYolu] [--user KullanıcıAdı] [--" +"reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Giriş Hatalar Son hata Kim\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "%u başarısız oturum açma nedeniyle hesap kilitlendi." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(kilidi açmak için %d dakika kaldı)" +msgstr[1] "(kilidi açmak için %d dakika kaldı)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(kilidi açmak için %d dakika kaldı)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %e %b %Y %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " %.*s makinesinden" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " %.*s üzerinde" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Son giriş:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Yeni hesabınıza hoş geldiniz!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Son başarısız giriş:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "Son başarılı girişten bu yana %d başarısız giriş denemesi yapıldı." +msgstr[1] "Son başarılı girişten bu yana %d başarısız giriş denemesi yapıldı." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Son başarılı girişten itibaren %d başarısız kimlik doğrulama girişimi oldu." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "%s için çok fazla giriş var." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "Yeni iletiniz yok." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Yeni iletiniz var." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Eski/okunmuş iletiniz var." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "İletiniz var." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "%s dizininde iletiniz yok." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "%s dizininde yeni iletiniz var." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "%s dizininde eski/okunmuş iletiniz var." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "%s dizininde iletiniz var." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "%s dizini oluşturuluyor." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "%s dizini oluşturulamadı." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Parola kullanımda. Lütfen başka bir parola seçin." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Parola önceden kullanılmıştır." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Öntanımlı güvenlik bağlamı %s'dir." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Farklı bir rol ya da seviye girmek ister misiniz?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "rol:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "%s rolü için öntanımlı bir tür yok." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "seviye:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Bu geçerli bir güvenlik bağlamı değil." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "%s için geçerli bir bağlam alınamadı." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "%s güvenlik bağlamı atandı." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "%s anahtar oluşturma bağlamı atandı." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "PAM başlatılamadı\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() çalıştırılamadı\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "giriş: çatallama yapılamadı: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Erişim izni verildi (son erişim %ld saniye önce)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "Hesabınızın süresi doldu; lütfen sistem yöneticinize başvurun." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Parolanızı hemen değiştirmeniz gerekmektedir (yönetici tarafından zorunlu " +"kılındı)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Parolanızı hemen değiştirmeniz gerekmektedir (parolanın süresi doldu)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Uyarı: parolanızın süresi %d gün içinde dolacak." +msgstr[1] "Uyarı: parolanızın süresi %d gün içinde dolacak." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Uyarı: parolanızın süresi %d gün içinde dolacak." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS parolası değiştirilemedi." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Parola belirtilmedi." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Parola değiştirilmedi." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Daha kısa bir şifre seçmelisiniz." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Daha uzun bir parola seçmelisiniz." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "%s kullanıcısının parolası değiştiriliyor." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Parolanızı değiştirmek için daha uzun süre beklemelisiniz." + +#~ msgid "You have no mail." +#~ msgstr "E-postanız yok." + +#~ msgid "is the same as the old one" +#~ msgstr "eskisi ile aynı" + +#~ msgid "memory allocation error" +#~ msgstr "bellek ayırma hatası" + +#~ msgid "is a palindrome" +#~ msgstr "bir palindrom (iki yönden aynı şekilde okunuyor)" + +#~ msgid "case changes only" +#~ msgstr "sadece büyük-küçük harf değişimi" + +#~ msgid "is too similar to the old one" +#~ msgstr "eskisine çok benziyor" + +#~ msgid "is too simple" +#~ msgstr "çok basit" + +#~ msgid "is rotated" +#~ msgstr "değiştirilmiş" + +#~ msgid "not enough character classes" +#~ msgstr "yetersiz karakter sınıfı" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "aynı karakterleri arka arkaya içeriyor" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "çok uzun tekdüze karakter sırası içeriyor" + +#~ msgid "contains the user name in some form" +#~ msgstr "kullanıcı adını bir biçimde içeriyor" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "KÖTÜ PAROLA: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Hesap geçici olarak kilitlendi (%ld saniye kaldı)." + +#~ msgid "Authentication error" +#~ msgstr "Yetkilendirme hatası" + +#~ msgid "Service error" +#~ msgstr "Hizmet hatası" + +#~ msgid "Unknown user" +#~ msgstr "Bilinmeyen kullanıcı" + +#~ msgid "Unknown error" +#~ msgstr "Bilinmeyen hata" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: --reset argümanına geçersiz sayı verildi\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Tanınmayan seçenek %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file DosyanınTamYolu] [--user KullanıcıAdı] [--reset[=n]] [--" +#~ "quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Tüm kullanıcılara sıfır olmayan bir değer atanamadı\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f kök-dosya-adı] [--file kök-dosya-adı]\n" +#~ " [-u kullanıcı-adı] [--user kullanıcı-adı]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/uk.po b/po/uk.po new file mode 100644 index 0000000..dc69868 --- /dev/null +++ b/po/uk.po @@ -0,0 +1,597 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Ivan Petrouchtchak , 2006 +# Yuri Chornoivan , 2010,2012, 2020, 2021, 2022. +# Tomáš Mráz , 2016. #zanata +# Yuri Chornoivan , 2016. #zanata, 2020, 2021, 2022. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2022-11-13 09:19+0000\n" +"Last-Translator: Yuri Chornoivan \n" +"Language-Team: Ukrainian \n" +"Language: uk\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.14.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Пароль: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "Поточний пароль %s: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "Поточний пароль: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Новий пароль %s: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Новий пароль: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Повторіть новий пароль %s: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Повторіть новий пароль: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Вибачте, паролі не збігаються." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Повторне введення %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "Зміну пароля перервано." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "користувач:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Успіх" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Критична помилка - негайна зупинка" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Не вдалося завантажити модуль" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Символ не знайдено" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Помилка в модулі служби" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Системна помилка" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Помилка буфера пам'яті" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Доступ заборонено" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Помилка розпізнавання" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Недостатньо прав для доступу до даних розпізнавання" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Службі розпізнавання не вдалося отримати інформацію щодо розпізнавання" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Невідомий користувач для модуля розпізнавання" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Використано максимальну кількість дозволених спроб для служби" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Лексема розпізнавання вже не чинна; потрібна нова" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Обліковий запис користувача вже застарів" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Не вдається створити / вилучити запис для вказаного сеансу" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Служба розпізнавання не може отримати права користувача" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Права користувача застаріли" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Помилка встановлення прав користувача" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Немає даних для певних модулів" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Неправильний елемент передано до pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Помилка обміну даними" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Помилка маніпуляції лексемою розпізнавання" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Не вдається відновити інформацію розпізнавання" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Лексему розпізнавання заблоковано, зайнято" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Застарівання лексеми розпізнавання вимкнено" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Невдача попередньої перевірки службою паролів" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Повернуте значення має бути проігноровано засобом розподілу PAM" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Невідомий модуль" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Застаріла лексема розпізнавання" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Обмін даними очікує на подію" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Програмі потрібно знов викликати libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Невідома помилка PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Час закінчується...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Вибачте, ваш час закінчився!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "помилкова розмова (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "Помилка %s: коди виходу %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "Помилка %s: отримано сигнал %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "Помилка %s: невідомий стан 0x%x" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"Користування: %s [--dir /шлях/до/каталогу/tally] [--user ім'я користувача] " +"[--reset] [--legacy-output]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Користувач Помилок Остання помилка З\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Обліковий запис заблоковано через %u помилок під час спроби входу." + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(лишилося %d хвилина до розблокування)" +msgstr[1] "(лишилося %d хвилини до розблокування)" +msgstr[2] "(лишилося %d хвилин до розблокування)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(лишилося %d хвилин до розблокування)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " з %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " на %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Останній вхід: %s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Ласкаво просимо до вашого нового запису!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Останній невдалий вхід: %s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Після останнього успішного входу було виконано %d спробу входу, яка " +"завершилася помилкою." +msgstr[1] "" +"Після останнього успішного входу було виконано %d спроби входу, які " +"завершилися помилками." +msgstr[2] "" +"Після останнього успішного входу було виконано %d спроб входу, які " +"завершилися помилками." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Після останнього успішного входу було виконано %d спроби входу, які " +"завершилися помилками." + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "Забагато входів в для «%s»." + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "У вас немає нової пошти." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Надійшла нова пошта." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Ви маєте стару пошту." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Надійшла пошта." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "У теці %s немає поштових повідомлень." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "У теці %s є нові поштові повідомлення." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "У теці %s є старі поштові повідомлення." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "У теці %s є поштові повідомлення." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Створення каталогу «%s»." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Не вдалося створити і ініціалізувати каталог «%s»." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Цей пароль вже використано. Виберіть інший." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "Пароль вже використовувався." + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "Типовий контекст безпеки — %s." + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Бажаєте ввести іншу роль або рівень?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "роль:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "Для ролі %s не визначено типового типу." + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "рівень:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "Цей контекст безпеки є некоректним." + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "Не вдалося отримати коректний контекст для %s." + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "Призначено контекст безпеки %s." + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "Призначено контекст створення ключів %s." + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "не вдалося ініціалізувати PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "помилка pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "вхід: помилка розгалуження: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Надано доступ (остання спроба доступу сталася %ld секунд тому)." + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"Строк дії вашого облікового запису вичерпано — будь ласка, зверніться до " +"вашого системного адміністратора." + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Вам слід негайно змінити пароль (вимога адміністратора)." + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" +"Вам необхідно негайно змінити пароль (строк дії поточного пароля вичерпано)." + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Попередження: строк дії вашого пароля буде вичерпано за %d день." +msgstr[1] "Попередження: строк дії вашого пароля буде вичерпано за %d дні." +msgstr[2] "Попередження: строк дії вашого пароля буде вичерпано за %d днів." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Попередження: строк дії вашого пароля буде вичерпано за %d днів." + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Не вдалося змінити пароль NIS." + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "Не було надано жодного пароля." + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "Пароль не було змінено." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Необхідно вибрати коротший пароль." + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "Вам слід вибрати довший пароль." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Зміна пароля %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "Ви слід ще трохи зачекати, щоб змінити ваш пароль." + +#~ msgid "You have no mail." +#~ msgstr "Нових повідомлень немає." + +#~ msgid "is the same as the old one" +#~ msgstr "такий самий, як і старий" + +#~ msgid "memory allocation error" +#~ msgstr "помилка під час спроби отримання області у пам’яті" + +#~ msgid "is a palindrome" +#~ msgstr "— це паліндром" + +#~ msgid "case changes only" +#~ msgstr "тільки зміни в регістрі" + +#~ msgid "is too similar to the old one" +#~ msgstr "занадто подібний до старого" + +#~ msgid "is too simple" +#~ msgstr "занадто простий" + +#~ msgid "is rotated" +#~ msgstr "чергується" + +#~ msgid "not enough character classes" +#~ msgstr "недостатнє використання класів символів" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "містить забагато послідовних однакових символів" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "містить надто довгу монотонну послідовність символів" + +#~ msgid "contains the user name in some form" +#~ msgstr "містить ім’я користувача з форми" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "ПОГАНИЙ ПАРОЛЬ: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Обліковий запис тимчасово заблоковано (ще на %ld секунд)." + +#~ msgid "Authentication error" +#~ msgstr "Помилка розпізнавання" + +#~ msgid "Service error" +#~ msgstr "Помилка служби" + +#~ msgid "Unknown user" +#~ msgstr "Невідомий користувач" + +#~ msgid "Unknown error" +#~ msgstr "Невідома помилка" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: вказано помилкове число --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: нерозпізнано параметр %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file назва-файла-користувача-root] [--user ім'я користувача] [--" +#~ "reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "" +#~ "%s: не вдається відновити ненульове значення для всіх користувачів\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f назва-файла-користувача-root] [--file назва-файла-користувача-" +#~ "root]\n" +#~ " [-u користувач] [--user користувач]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/ur.po b/po/ur.po new file mode 100644 index 0000000..eb366d0 --- /dev/null +++ b/po/ur.po @@ -0,0 +1,499 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2011-11-30 06:56-0500\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Urdu \n" +"Language: ur\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "آپ کو ایک چھوٹا پاس ورڈ منتخب کرنا ہوگا." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "آپ کو ایک چھوٹا پاس ورڈ منتخب کرنا ہوگا." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "آپ کو ایک چھوٹا پاس ورڈ منتخب کرنا ہوگا." diff --git a/po/vi.po b/po/vi.po new file mode 100644 index 0000000..c617990 --- /dev/null +++ b/po/vi.po @@ -0,0 +1,593 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# , 2012. +# Tomáš Mráz , 2016. #zanata +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2012-11-30 06:03-0500\n" +"Last-Translator: mattheu_9x \n" +"Language-Team: Vietnamese (http://www.transifex.com/projects/p/fedora/" +"language/vi/)\n" +"Language: vi\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Mật khẩu : " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "Mật khẩu %s mới: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "Mật khẩu mới: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "Mật khẩu %s mới: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "Mật khẩu mới: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Nhập lại mật khẩu %s mới: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Nhập lại mật khẩu mới: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Xin lỗi, mật khẩu không khớp." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "Nhập lại %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "Hủy bỏ việc thay đổi mật khẩu." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "Đăng nhập:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Thành công" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Lỗi nghiêm trọng - hủy bỏ ngay" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Không thể nạp mô-đun" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Không tìm thấy biểu tượng" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Lỗi trong mô-đun dịch vụ" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Lỗi hệ thống" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Lỗi bộ nhớ đệm" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Không có đủ quyền thực hiện" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Xác thực thất bại" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Không đủ quyền uỷ nhiệm để truy cập dữ liệu xác thực" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Dịch vụ chứng thực không thể lấy các thông tin xác thực" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Người dùng chưa biết đến mô-đun xác thực tiềm ẩn" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Đã hết số lần thử lại tối đa cho dịch vụ" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Dấu hiệu xác thực không còn hiệu lực; yêu cầu dấu hiệu xác thực mới" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "Tài khoản người dùng đã hết hiệu lực" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Không thể thực hiện hoặc loại bỏ một mục nhập cho phiên định" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Dịch vụ chứng thực không thể lấy các chứng chỉ người dùng" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Hết hạn ủy nhiệm người dùng" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Cài đặt ủy nhiệm người dùng thất bại" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Hiện tại không có mô-đun dữ liệu" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Tình trạng hàng được thông qua để pam_ * _item ()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Lỗi giao tiếp" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Thao tác xác thực token lỗi" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Xác thực thông tin không thể được phục hồi" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "authentication token lock busy" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "xác thực token già tàn tật" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Không thành công sơ bộ kiểm tra bằng mật khẩu dịch vụ" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "Giá trị trả về phải được bỏ qua bởi PAM công văn" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "chưa biết mô-đun" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Hết hạn xác thực" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Giao tiếp đang chờ kết quả" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Ứng dụng cần gọi libpam lần nữa" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Không biết lỗi PAM" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Thời gian đang dần hết...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Xin lỗi, đã hết thời gian!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "hội thoại sai (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s thất bại: lối ra mã %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s thất bại: bắt tín hiệu %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s thất bại: không rõ tình trạng 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [- tập bắt rễ-filename] [- người sử dụng tên người dùng] [- đặt lại [= " +"n]] [- yên tĩnh]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Đang nhập Thất bại Thất bại cuốie Từ \n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, fuzzy, c-format +msgid "The account is locked due to %u failed logins." +msgstr "Tài khoản bị khóa do đăng nhập %u không thành công" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " từ %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " trên %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Lần đăng nhập:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Chào mừng bạn đến tài khoản mới của bạn!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "Lần đăng nhập thất bại trước:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +"Đã có %d lần đăng nhập thất bại kể từ lần đăng nhập thành công trước đó." + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" +"Có %d lần đăng nhập không thành công kể từ lần đăng nhập thành công trước." + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "Quá nhiều lần đăng nhập cho '%s'." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Bạn có thư mới." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Bạn có thư mới." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Bạn có thư cũ." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Bạn có thư." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Bạn không có thư trong thư mục %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Bạn có thư mới trong thư mục %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Bạn có thư cũ trong thư mục %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Bạn có thư trong thư mục %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "Tạo thư mục '%s'." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "Không thể khởi tạo thư mục '%s'." + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Mật khẩu đã được dùng. Hãy chọn mật khẩu khác." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +#, fuzzy +msgid "Password has been already used." +msgstr "Mật khẩu đã được dùng. Hãy chọn mật khẩu khác." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "Bối cảnh an ninh mặc định %s\n" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "Bạn có muốn nhập một vai trò khác nhau hoặc cấp?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "Vai trò:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, fuzzy, c-format +msgid "There is no default type for role %s." +msgstr "Không có loại mặc định cho vai trò %s\n" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "trình độ:" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "Không phải là một bối cảnh an ninh hợp lệ" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "Bối cảnh an ninh %s Giao" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "Sáng tạo Context phím %s Giao" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "không thể khởi tạo PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "không pam_set_item ()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "đăng nhập: thất bại forking: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, fuzzy, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "Được phép truy cập (lần truy cập trước %ld giây trước)" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"Tài khoản của bạn đã hết hạn dùng: hãy liên lạc với nhà quản trị hệ thống" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "Bạn phải thay đổi ngay mật khẩu (người chủ ép buộc)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "Bạn phải thay đổi ngay mật khẩu (mật khẩu quá cũ)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "Cảnh báo: mật khẩu của bạn sẽ hết hạn trong %d ngày" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, fuzzy, c-format +msgid "Warning: your password will expire in %d days." +msgstr "Cảnh báo: mật khẩu của bạn sẽ hết hạn trong %d ngày" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "NIS mật khẩu không thể được thay đổi." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "Không có mật khẩu được cung cấp" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "NIS mật khẩu không thể được thay đổi." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Bạn phải chọn một mật khẩu ngắn hơn." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Bạn phải chọn mật khẩu dài hơn" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "Thay đổi mật khẩu cho %s." + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Bạn phải đợi thêm nữa, để thay đổi mật khẩu" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "Bạn có thư mới." + +#~ msgid "is the same as the old one" +#~ msgstr "là giống như cũ" + +#~ msgid "memory allocation error" +#~ msgstr "Lỗi cấp phát bộ nhớ" + +#~ msgid "is a palindrome" +#~ msgstr "là một xâu palindrome" + +#~ msgid "case changes only" +#~ msgstr "chỉ thay đổi chữ thường/hoa" + +#~ msgid "is too similar to the old one" +#~ msgstr "quá giống cái cũ" + +#~ msgid "is too simple" +#~ msgstr "quá đơn giản" + +#~ msgid "is rotated" +#~ msgstr "được sử dụng lại" + +#~ msgid "not enough character classes" +#~ msgstr "không đủ các lớp nhân vật" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "chứa quá nhiều kí tự giống nhau liên tiếp" + +#, fuzzy +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "chứa quá nhiều kí tự giống nhau liên tiếp" + +#~ msgid "contains the user name in some form" +#~ msgstr "chứa tên user trong một số biểu mẫu" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "Mật khẩu không an toàn: %s" + +#, fuzzy +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "Tài khoản tạm thời bị khóa (%ld giây còn lại)" + +#~ msgid "Authentication error" +#~ msgstr "Xác thực lỗi" + +#~ msgid "Service error" +#~ msgstr "Lỗi dịch vụ" + +#~ msgid "Unknown user" +#~ msgstr "Người dùng không rõ" + +#~ msgid "Unknown error" +#~ msgstr "Lỗi không rõ" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Xấu số cho --cài lại=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Được thừa nhận lựa chọn%s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [- tập bắt rễ-filename] [- người sử dụng tên người dùng] [- đặt lại " +#~ "[= n]] [- yên tĩnh]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: Không thể thiết lập lại tất cả các người dùng khác không\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/yo.po b/po/yo.po new file mode 100644 index 0000000..2370504 --- /dev/null +++ b/po/yo.po @@ -0,0 +1,497 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the Linux-PAM package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM 1.2.1\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: Yoruba\n" +"Language: yo\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" +"X-Generator: Zanata 3.8.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "O gbọdọ yan ọrọigbaniwọle kukuru." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "O gbọdọ yan ọrọigbaniwọle kukuru." + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "O gbọdọ yan ọrọigbaniwọle kukuru." diff --git a/po/zh_CN.po b/po/zh_CN.po new file mode 100644 index 0000000..4c28e96 --- /dev/null +++ b/po/zh_CN.po @@ -0,0 +1,580 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Christopher Meng , 2012 +# Leah Liu , 2008-2009 +# Tommy He , 2012 +# Xi HUANG , 2007 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +# Charles Lee , 2020. +# Chenbo Pan , 2021. +# pan chenbo , 2021. +# Dingzhong Chen , 2022. +# Liu Tao , 2023. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2023-12-27 09:37+0000\n" +"Last-Translator: Liu Tao \n" +"Language-Team: Chinese (Simplified) \n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.3\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "密码: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "当前的 %s 密码: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "当前的密码: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "新的 %s 密码: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "新的密码: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "重新输入新的 %s 密码: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "重新输入新的密码: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "抱歉,密码不匹配。" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "重新输入 %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "密码更改已取消。" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "登录:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "成功" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "严重错误——立即中止" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "装载模块失败" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "找不到符号" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "服务模块存在错误" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "系统错误" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "内存缓冲区错误" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "权限被拒绝" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "身份验证失败" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "没有足够的凭据可以访问身份验证数据" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "身份验证服务无法检索神身份验证信息" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "底层的身份验证模块无法识别用户" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "已经超出服务的最多重试次数" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "身份验证令牌不再有效;需要新的令牌" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "用户帐户已失效" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "无法为指定的会话创建/移除条目" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "身份验证服务无法检索到用户凭据" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "用户凭据已失效" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "设置用户凭据失败" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "不存在特定于模块的数据" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "传递至 pam_*_item() 的项目无效" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "转换错误" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "身份验证令牌操作错误" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "无法恢复身份验证信息" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "身份验证令牌正被锁定" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "已禁用身份验证令牌期限" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "由密码服务初步检查失败" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "返回值应被 PAM 调度忽略" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "模块未知" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "身份验证令牌失效" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "转换正在等待事件" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "应用程序需要再次调用 libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "未知的 PAM 错误" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...时间即将耗尽...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...对不起,您的时间已经耗尽!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "有错误的转换 (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s 失败:退出代码 %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s 失败:捕获信号 %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s 失败:未知状态 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "用法:%s [--dir /tally/目录的/路径] [--user 用户名] [--reset]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Login Failures Latest failure From\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "由于 %u 次登录失败,此帐户已锁定。" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d 分钟后解锁)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d 分钟后解锁)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %x %A %H:%M:%S %Z" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " 从 %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " 于 %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "上一次登录:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "欢迎使用新帐户!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "最后一次失败的登录:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "自上次成功登录以来,有 %d 次失败的登录尝试。" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "自上次成功登录以来,有 %d 次失败的登录尝试。" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "“%s”的登录次数过多。" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "您没有任何新邮件。" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "您有新邮件。" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "您有旧邮件。" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "您有邮件。" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "您在文件夹 %s 中无邮件。" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "您在文件夹 %s 中有新邮件。" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "您在文件夹 %s 中有旧邮件。" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "您在文件夹 %s 中有邮件。" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "正在创建目录“%s”。" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "无法创建和初始化目录“%s”。" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "密码已使用。请选择其他密码。" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "密码已被使用。" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "默认的安全上下文是 %s。" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "您是否愿意进入不同的角色或者级别?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "角色:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "角色 %s 没有默认类型。" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "级别:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "这不是一个有效的安全上下文。" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "无法取得对 %s 有效的上下文。" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "安全上下文 %s 已分配。" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "密钥生成上下文 %s 已分配。" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "未能初始化 PAM\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "未能 pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "登录:派生失败:%m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "访问已被准许(上次访问是 %ld 秒之前)。" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "您的帐户已过期;请联系您的系统管理员。" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "您必须立即更改密码(管理员强制)。" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "您必须立即更改密码(密码已经过期)。" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "警告:您的密码将在 %d 天后过期。" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "警告:您的密码将在 %d 天后过期。" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "无法更改 NIS 密码。" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "未提供密码。" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "密码未被更改。" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "您必须选择较短的密码。" + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "您必须选择更长的密码。" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "更改 %s 的密码。" + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "您必须等待更长时间以更改密码。" + +#~ msgid "You have no mail." +#~ msgstr "您没有邮件。" + +#~ msgid "is the same as the old one" +#~ msgstr "与旧密码相同" + +#~ msgid "memory allocation error" +#~ msgstr "内存分配错误" + +#~ msgid "is a palindrome" +#~ msgstr "是回文" + +#~ msgid "case changes only" +#~ msgstr "仅更改了大小写" + +#~ msgid "is too similar to the old one" +#~ msgstr "与旧密码过于相似" + +#~ msgid "is too simple" +#~ msgstr "过于简单" + +#~ msgid "is rotated" +#~ msgstr "是旧密码的循环" + +#~ msgid "not enough character classes" +#~ msgstr "没有足够的字符分类" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "包含过多连续相同的字符" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "包含过长的单一字符序列" + +#~ msgid "contains the user name in some form" +#~ msgstr "以某些形式包含用户名" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "无效的密码: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "帐户暂时锁定(剩余 %ld 秒)。" + +#~ msgid "Authentication error" +#~ msgstr "鉴定错误" + +#~ msgid "Service error" +#~ msgstr "服务错误" + +#~ msgid "Unknown user" +#~ msgstr "未知的用户" + +#~ msgid "Unknown error" +#~ msgstr "未知的错误" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: 给定的数字无效 --重设置=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: 未识别的选项 %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "%s: [--文件 根文件名] [--用户 用户名] [--重设置[=n]] [--安静]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: 无法将所有用户重设置为非零\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/zh_HK.po b/po/zh_HK.po new file mode 100644 index 0000000..dd2fbd0 --- /dev/null +++ b/po/zh_HK.po @@ -0,0 +1,498 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +# chong gao , 2021. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2021-09-09 03:04+0000\n" +"Last-Translator: chong gao \n" +"Language-Team: Chinese (Hong Kong) \n" +"Language: zh_HK\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.8\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "密碼: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "" + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "" + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "" + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr "" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr "" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr "" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "" + +#: modules/pam_mail/pam_mail.c:290 +msgid "You do not have any new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:575 +#, fuzzy +msgid "You must choose a shorter password." +msgstr "您必須選擇較短的密碼。" + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "您必須選擇較短的密碼。" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "您必須選擇較短的密碼。" diff --git a/po/zh_TW.po b/po/zh_TW.po new file mode 100644 index 0000000..d4b517f --- /dev/null +++ b/po/zh_TW.po @@ -0,0 +1,576 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Terry Chuang , 2008-2009 +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +# Yi-Jyun Pan , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-05-24 12:40+0000\n" +"Last-Translator: Yi-Jyun Pan \n" +"Language-Team: Chinese (Traditional) \n" +"Language: zh_TW\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.0.4\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "密碼: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, c-format +msgid "Current %s password: " +msgstr "目前的 %s 密碼: " + +#: libpam/pam_get_authtok.c:43 +msgid "Current password: " +msgstr "目前密碼: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "新 %s 密碼: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "新 密碼: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "再次輸入新的 %s 密碼: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "再次輸入新的 密碼: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "抱歉,密碼不符合。" + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "重新輸入 %s" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +msgid "Password change has been aborted." +msgstr "已中止密碼變更作業。" + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "登入:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "成功" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "嚴重錯誤 - 立即終止" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "載入模組失敗" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "找不到符號" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "服務模組發生錯誤" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "系統錯誤" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "記憶體暫存區錯誤" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "許可權被拒絕" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "驗證失敗" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "憑證不足,無法存取驗證資料" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "驗證服務無法取出驗證資訊" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "底層的驗證模組無法識別使用者" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "服務的重試次數已超過最大數量" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "驗證記號已經失效;需要新的驗證記號" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "使用者帳戶已逾期" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "無法為指定的工作階段製作/移除項目" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "驗證服務無法取出使用者憑證" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "使用者憑證已逾期" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "無法設定使用者憑證" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "沒有模組的詳細資料" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "不良的項目傳送至 pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "交談錯誤" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "驗證記號處理錯誤" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "無法恢復驗證資訊" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "驗證記號鎖定忙碌" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "驗證記號計時已停用" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "密碼服務的初步檢查失敗" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "PAM 發送應忽略傳回值" + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "未知的模組" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "驗證記號已逾期" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "交談正在等候事件" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "應用程式需要再次呼叫 libpam" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "未知的 PAM 錯誤" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...時間已經超過...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...抱歉,您的時間已到!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "錯誤的交談 (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "%s 失敗:退出編碼 %d" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "%s 失敗:捕捉到信號 %d%s" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "%s 失敗:不明狀態 0x%x" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"用法:%s [--dir /path/to/tally-directory] [--user username] [--reset]\n" + +#: modules/pam_faillock/main.c:182 +#, fuzzy, c-format +msgid "Login Failures Latest failure From\n" +msgstr "Login Failures Latest failure From\n" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "因為 %u 次登入皆失敗,帳號已鎖定。" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, fuzzy, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "(%d 分鐘後解鎖)" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "(%d 分鐘後解鎖)" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " 從 %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " 在 %.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "上一次登入:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "歡迎使用您的新帳號!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "上一次失敗的登入:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "自上次成功登入後,有 %d 次試圖登入但失敗的紀錄。" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "自從上次成功登入後有 %d 次嘗試登入失敗。" + +#: modules/pam_limits/pam_limits.c:1424 +#, c-format +msgid "There were too many logins for '%s'." +msgstr "登入 '%s' 太多次。" + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "您有新的郵件。" + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "您有新的郵件。" + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "您有舊的郵件。" + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "您有郵件。" + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "資料夾 %s 中沒有您的郵件。" + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "資料夾 %s 中有您的新郵件。" + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "資料夾 %s 中有您的舊郵件。" + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "資料夾 %s 中有您的郵件。" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "建立目錄「%s」。" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "無法建立和初始化「%s」目錄。" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "密碼已經由其他使用者使用。請選擇其他密碼。" + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +msgid "Password has been already used." +msgstr "密碼已被使用過。" + +#: modules/pam_selinux/pam_selinux.c:174 +#, c-format +msgid "The default security context is %s." +msgstr "預設的安全情境為 %s。" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "您是否希望輸入不同的角色或層級?" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "角色:" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "%s 角色沒有預設類型。" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "層級:" + +#: modules/pam_selinux/pam_selinux.c:261 +msgid "This is not a valid security context." +msgstr "此非有效的安全性情境。" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "無法取得 %s 的有效情境。" + +#: modules/pam_selinux/pam_selinux.c:641 +#, c-format +msgid "Security context %s has been assigned." +msgstr "已指定 %s 安全情境。" + +#: modules/pam_selinux/pam_selinux.c:657 +#, c-format +msgid "Key creation context %s has been assigned." +msgstr "已指定 %s 金鑰建立情境。" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "起始 PAM 失敗\n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "pam_set_item() 失敗\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "登入:失敗的分叉:%m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "已賦予存取權限(最後一次存取為 %ld 秒前)。" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +msgid "Your account has expired; please contact your system administrator." +msgstr "您的帳戶已經過期;請洽詢您的系統管理員。" + +#: modules/pam_unix/pam_unix_acct.c:240 +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "您必須立刻變更您的密碼(管理員強制執行)。" + +#: modules/pam_unix/pam_unix_acct.c:246 +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "您必須立刻變更您的密碼(密碼已經過期)。" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "警告:您的密碼將在 %d 天後過期。" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "警告:您的密碼將在 %d 天後過期。" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "無法變更 NIS 密碼。" + +#: modules/pam_unix/pam_unix_passwd.c:557 +msgid "No password has been supplied." +msgstr "未提供密碼。" + +#: modules/pam_unix/pam_unix_passwd.c:558 +msgid "The password has not been changed." +msgstr "未變更密碼。" + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "您必須選擇較短的密碼。" + +#: modules/pam_unix/pam_unix_passwd.c:579 +msgid "You must choose a longer password." +msgstr "您的密碼必須更長。" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "正在更改 %s 的 STRESS 密碼。" + +#: modules/pam_unix/pam_unix_passwd.c:714 +msgid "You must wait longer to change your password." +msgstr "請您稍等一陣子後再變更密碼。" + +#~ msgid "You have no mail." +#~ msgstr "您沒有新郵件。" + +#~ msgid "is the same as the old one" +#~ msgstr "與舊的密碼相同" + +#~ msgid "memory allocation error" +#~ msgstr "記憶體分配錯誤" + +#~ msgid "is a palindrome" +#~ msgstr "是一個回文" + +#~ msgid "case changes only" +#~ msgstr "僅變更大小寫" + +#~ msgid "is too similar to the old one" +#~ msgstr "與舊的密碼太相似" + +#~ msgid "is too simple" +#~ msgstr "太簡單" + +#~ msgid "is rotated" +#~ msgstr "已旋轉" + +#~ msgid "not enough character classes" +#~ msgstr "字元類別不足" + +#~ msgid "contains too many same characters consecutively" +#~ msgstr "包含了太多連續的相同字元" + +#~ msgid "contains too long of a monotonic character sequence" +#~ msgstr "包含過多的單調字元序段" + +#~ msgid "contains the user name in some form" +#~ msgstr "包含了某些格式的用戶名稱" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "不良的密碼: %s" + +#~ msgid "The account is temporarily locked (%ld seconds left)." +#~ msgstr "帳號被暫時鎖定(剩餘 %ld 秒)。" + +#~ msgid "Authentication error" +#~ msgstr "驗證錯誤" + +#~ msgid "Service error" +#~ msgstr "服務錯誤" + +#~ msgid "Unknown user" +#~ msgstr "未知的使用者" + +#~ msgid "Unknown error" +#~ msgstr "未知的錯誤" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: 不良的號碼提供至 --reset=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: 未識別的選項 %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "%s: 無法將所有使用者重新設定為非零\n" + +#~ msgid "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [-f rooted-filename] [--file rooted-filename]\n" +#~ " [-u username] [--user username]\n" +#~ " [-r] [--reset[=n]] [--quiet]\n" diff --git a/po/zu.po b/po/zu.po new file mode 100644 index 0000000..fbc9275 --- /dev/null +++ b/po/zu.po @@ -0,0 +1,569 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Linux-PAM Project +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Tomáš Mráz , 2016. #zanata +# Dmitry V. Levin , 2020. +msgid "" +msgstr "" +"Project-Id-Version: Linux-PAM\n" +"Report-Msgid-Bugs-To: https://github.com/linux-pam/linux-pam/issues\n" +"POT-Creation-Date: 2024-10-13 20:00+0000\n" +"PO-Revision-Date: 2020-03-06 23:59+0000\n" +"Last-Translator: Dmitry V. Levin \n" +"Language-Team: Zulu \n" +"Language: zu\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Weblate 3.11.2\n" + +#: libpam/pam_get_authtok.c:40 modules/pam_exec/pam_exec.c:182 +#: modules/pam_userdb/pam_userdb.c:62 +msgid "Password: " +msgstr "Iphasiwedi: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:42 +#, fuzzy, c-format +msgid "Current %s password: " +msgstr "%s iphasiwedi entsha: " + +#: libpam/pam_get_authtok.c:43 +#, fuzzy +msgid "Current password: " +msgstr "iphasiwedi entsha: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:45 +#, c-format +msgid "New %s password: " +msgstr "%s iphasiwedi entsha: " + +#: libpam/pam_get_authtok.c:46 +msgid "New password: " +msgstr "iphasiwedi entsha: " + +#. For Translators: "%s" is replaced with "". +#: libpam/pam_get_authtok.c:48 +#, c-format +msgid "Retype new %s password: " +msgstr "Thayipha kabusha %s iphasiwedi entsha: " + +#: libpam/pam_get_authtok.c:49 +msgid "Retype new password: " +msgstr "Thayipha kabusha iphasiwedi entsha: " + +#: libpam/pam_get_authtok.c:50 +msgid "Sorry, passwords do not match." +msgstr "Uxolo, amaphasiwedi awahambelani." + +#: libpam/pam_get_authtok.c:143 libpam/pam_get_authtok.c:245 +#, c-format +msgid "Retype %s" +msgstr "" + +#: libpam/pam_get_authtok.c:183 libpam/pam_get_authtok.c:263 +#, fuzzy +msgid "Password change has been aborted." +msgstr "Le phasiwedi isetshenziswa ngothile. Khetha enye." + +#: libpam/pam_item.c:317 +msgid "login:" +msgstr "ngena:" + +#: libpam/pam_strerror.c:41 +msgid "Success" +msgstr "Impumelelo" + +#: libpam/pam_strerror.c:43 +msgid "Critical error - immediate abort" +msgstr "Iphutha elibucayi - yeka ngokushesha" + +#: libpam/pam_strerror.c:45 +msgid "Failed to load module" +msgstr "Ihlulekile ukunezela umkhiqizo owenezelwayo" + +#: libpam/pam_strerror.c:47 +msgid "Symbol not found" +msgstr "Uphawu alutholakali" + +#: libpam/pam_strerror.c:49 +msgid "Error in service module" +msgstr "Iphutha kwimoduli yesevisi" + +#: libpam/pam_strerror.c:51 +msgid "System error" +msgstr "Iphutha lesistimu" + +#: libpam/pam_strerror.c:53 +msgid "Memory buffer error" +msgstr "Iphutha lengobo yesikhashana yememori" + +#: libpam/pam_strerror.c:55 +msgid "Permission denied" +msgstr "Imvume inqatshiwe" + +#: libpam/pam_strerror.c:57 +msgid "Authentication failure" +msgstr "Ukwehluleka kokugunyaza" + +#: libpam/pam_strerror.c:59 +msgid "Insufficient credentials to access authentication data" +msgstr "Izinhlamvu zamagama ezinganele zokungena ekwazisweni kokugunyaza" + +#: libpam/pam_strerror.c:61 +msgid "Authentication service cannot retrieve authentication info" +msgstr "Isevisi yokugunyaza ayikwazi ukulanda ukwaziswa kokugunyaza" + +#: libpam/pam_strerror.c:63 +msgid "User not known to the underlying authentication module" +msgstr "Umsebenzisi akaziwa kwimoduli yokugunyaza engabonakali" + +#: libpam/pam_strerror.c:65 +msgid "Have exhausted maximum number of retries for service" +msgstr "Isifinyelele ekugcineni kwezikhathi ezivumelekile zokuzama le sevisi" + +#: libpam/pam_strerror.c:67 +msgid "Authentication token is no longer valid; new one required" +msgstr "Isiqinisekiso sokugunyaza asisasebenzi, kudingeka esisha" + +#: libpam/pam_strerror.c:69 +msgid "User account has expired" +msgstr "I-akhawunti yomsebenzisi iphelelwe isikhathi" + +#: libpam/pam_strerror.c:71 +msgid "Cannot make/remove an entry for the specified session" +msgstr "Ayikwazi ukwakha/ukususa okufakiwe kohlelo olubonisiwe" + +#: libpam/pam_strerror.c:73 +msgid "Authentication service cannot retrieve user credentials" +msgstr "Isevisi yokugunyaza ayikwazi ukulanda izinhlamvu zomsebenzisi" + +#: libpam/pam_strerror.c:75 +msgid "User credentials expired" +msgstr "Izinhlamvu zomsebenzisi ziphelelwe isikhathi" + +#: libpam/pam_strerror.c:77 +msgid "Failure setting user credentials" +msgstr "Ukwehluleka ukusetha izinhlamvu zomsebenzisi" + +#: libpam/pam_strerror.c:79 +msgid "No module specific data is present" +msgstr "Akukho ukwaziswa okuthile kwemoduli okutholakalayo" + +#: libpam/pam_strerror.c:81 +msgid "Bad item passed to pam_*_item()" +msgstr "Into embi idluliselwe kwi-pam_*_item()" + +#: libpam/pam_strerror.c:83 +msgid "Conversation error" +msgstr "Iphutha lengxoxo" + +#: libpam/pam_strerror.c:85 +msgid "Authentication token manipulation error" +msgstr "Iphutha lokufaka isiqinisekiso sokugunyaza" + +#: libpam/pam_strerror.c:87 +msgid "Authentication information cannot be recovered" +msgstr "Ukwaziswa kokugunyaza akukwazi ukubuyiseka" + +#: libpam/pam_strerror.c:89 +msgid "Authentication token lock busy" +msgstr "Isihluthulelo sesiqinisekiso sokugunyaza simatasa" + +#: libpam/pam_strerror.c:91 +msgid "Authentication token aging disabled" +msgstr "Ukuphela kwesikhathi sesiqinisekiso sokugunyaza kucishiwe" + +#: libpam/pam_strerror.c:93 +msgid "Failed preliminary check by password service" +msgstr "Ukuhlola kokuqala okuhlulekile ngesevisi yephasiwedi" + +#: libpam/pam_strerror.c:95 +msgid "The return value should be ignored by PAM dispatch" +msgstr "I-value yokubuyisa kufanele inganakwa ukuthumela kwe-PAM " + +#: libpam/pam_strerror.c:97 +msgid "Module is unknown" +msgstr "Imoduli ayaziwa" + +#: libpam/pam_strerror.c:99 +msgid "Authentication token expired" +msgstr "Isiqinisekiso sokugunyaza siphelelwe isikhathi" + +#: libpam/pam_strerror.c:101 +msgid "Conversation is waiting for event" +msgstr "Ingxoxo ilinde isenzakalo" + +#: libpam/pam_strerror.c:103 +msgid "Application needs to call libpam again" +msgstr "Uhlelo ludinga ukubiza i-libpam futhi" + +#: libpam/pam_strerror.c:106 +msgid "Unknown PAM error" +msgstr "Iphutha le-PAM elingaziwa" + +#. +#. * external timeout definitions - these can be overridden by the +#. * application. +#. +#. time when we warn +#. time when we timeout +#: libpam_misc/misc_conv.c:36 +msgid "...Time is running out...\n" +msgstr "...Isikhathi siyaphela...\n" + +#: libpam_misc/misc_conv.c:37 +msgid "...Sorry, your time is up!\n" +msgstr "...Uxolo, isikhathi sakho sesiphelile!\n" + +#: libpam_misc/misc_conv.c:351 +#, c-format +msgid "erroneous conversation (%d)\n" +msgstr "ingxoxo enephutha (%d)\n" + +#: modules/pam_exec/pam_exec.c:298 +#, c-format +msgid "%s failed: exit code %d" +msgstr "" + +#: modules/pam_exec/pam_exec.c:308 +#, c-format +msgid "%s failed: caught signal %d%s" +msgstr "" + +#: modules/pam_exec/pam_exec.c:318 +#, c-format +msgid "%s failed: unknown status 0x%x" +msgstr "" + +#: modules/pam_faillock/main.c:131 +#, fuzzy, c-format +msgid "" +"Usage: %s [--dir /path/to/tally-directory] [--user username] [--reset] [--" +"legacy-output]\n" +msgstr "" +"%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#: modules/pam_faillock/main.c:182 +#, c-format +msgid "Login Failures Latest failure From\n" +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:416 +#, c-format +msgid "The account is locked due to %u failed logins." +msgstr "" + +#: modules/pam_faillock/pam_faillock.c:425 +#: modules/pam_faillock/pam_faillock.c:431 +#, c-format +msgid "(%d minute left to unlock)" +msgid_plural "(%d minutes left to unlock)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported. +#: modules/pam_faillock/pam_faillock.c:434 +#, c-format +msgid "(%d minutes left to unlock)" +msgstr "" + +#. TRANSLATORS: "strftime options for date of last login" +#: modules/pam_lastlog/pam_lastlog.c:323 modules/pam_lastlog/pam_lastlog.c:592 +msgid " %a %b %e %H:%M:%S %Z %Y" +msgstr " %a %b %e %H:%M:%S %Z %Y" + +#. TRANSLATORS: " from " +#: modules/pam_lastlog/pam_lastlog.c:332 modules/pam_lastlog/pam_lastlog.c:601 +#, c-format +msgid " from %.*s" +msgstr " kusukela %.*s" + +#. TRANSLATORS: " on " +#: modules/pam_lastlog/pam_lastlog.c:344 modules/pam_lastlog/pam_lastlog.c:613 +#, c-format +msgid " on %.*s" +msgstr " ku-%.*s" + +#. TRANSLATORS: "Last login: from on " +#: modules/pam_lastlog/pam_lastlog.c:354 +#, c-format +msgid "Last login:%s%s%s" +msgstr "Ukungena kokugcina:%s%s%s" + +#: modules/pam_lastlog/pam_lastlog.c:360 +msgid "Welcome to your new account!" +msgstr "Uyamukelwa kwi-akhawunti yakho entsha!" + +#. TRANSLATORS: "Last failed login: from on " +#: modules/pam_lastlog/pam_lastlog.c:623 +#, c-format +msgid "Last failed login:%s%s%s" +msgstr "" + +#: modules/pam_lastlog/pam_lastlog.c:632 modules/pam_lastlog/pam_lastlog.c:639 +#, c-format +msgid "There was %d failed login attempt since the last successful login." +msgid_plural "" +"There were %d failed login attempts since the last successful login." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_lastlog/pam_lastlog.c:644 +#, c-format +msgid "There were %d failed login attempts since the last successful login." +msgstr "" + +#: modules/pam_limits/pam_limits.c:1424 +#, fuzzy, c-format +msgid "There were too many logins for '%s'." +msgstr "Kuningi kakhulu ukungena kwi- '%s' osekwenziwe." + +#: modules/pam_mail/pam_mail.c:290 +#, fuzzy +msgid "You do not have any new mail." +msgstr "Unemeyili entsha." + +#: modules/pam_mail/pam_mail.c:293 +msgid "You have new mail." +msgstr "Unemeyili entsha." + +#: modules/pam_mail/pam_mail.c:296 +msgid "You have old mail." +msgstr "Unemeyili endala." + +#: modules/pam_mail/pam_mail.c:300 +msgid "You have mail." +msgstr "Unemeyili." + +#: modules/pam_mail/pam_mail.c:307 +#, c-format +msgid "You have no mail in folder %s." +msgstr "Akukho meyili onayo kwifolda %s." + +#: modules/pam_mail/pam_mail.c:311 +#, c-format +msgid "You have new mail in folder %s." +msgstr "Unemeyili entsha kwifolda %s." + +#: modules/pam_mail/pam_mail.c:315 +#, c-format +msgid "You have old mail in folder %s." +msgstr "Unemeyili endala kwifolda %s." + +#: modules/pam_mail/pam_mail.c:320 +#, c-format +msgid "You have mail in folder %s." +msgstr "Unemeyili kwifolda %s." + +#: modules/pam_mkhomedir/pam_mkhomedir.c:124 +#, c-format +msgid "Creating directory '%s'." +msgstr "" + +#: modules/pam_mkhomedir/pam_mkhomedir.c:207 +#, c-format +msgid "Unable to create and initialize directory '%s'." +msgstr "" + +#: modules/pam_pwhistory/pam_pwhistory.c:383 +#: modules/pam_unix/pam_unix_passwd.c:584 +msgid "Password has been already used. Choose another." +msgstr "Le phasiwedi isetshenziswa ngothile. Khetha enye." + +#: modules/pam_pwhistory/pam_pwhistory.c:390 +#, fuzzy +msgid "Password has been already used." +msgstr "Le phasiwedi isetshenziswa ngothile. Khetha enye." + +#: modules/pam_selinux/pam_selinux.c:174 +#, fuzzy, c-format +msgid "The default security context is %s." +msgstr "Akuyona indawo yokuphepha esemthethweni" + +#: modules/pam_selinux/pam_selinux.c:178 +msgid "Would you like to enter a different role or level?" +msgstr "" + +#. Allow the user to enter role and level individually +#: modules/pam_selinux/pam_selinux.c:191 +msgid "role:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:195 +#, c-format +msgid "There is no default type for role %s." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:227 +msgid "level:" +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:261 +#, fuzzy +msgid "This is not a valid security context." +msgstr "Akuyona indawo yokuphepha esemthethweni" + +#: modules/pam_selinux/pam_selinux.c:510 +#, c-format +msgid "A valid context for %s could not be obtained." +msgstr "" + +#: modules/pam_selinux/pam_selinux.c:641 +#, fuzzy, c-format +msgid "Security context %s has been assigned." +msgstr "Indawo %s Yokuphepha Yabelwe" + +#: modules/pam_selinux/pam_selinux.c:657 +#, fuzzy, c-format +msgid "Key creation context %s has been assigned." +msgstr "Indawo %s Yokuphepha Yabelwe" + +#: modules/pam_selinux/pam_selinux_check.c:101 +#, c-format +msgid "failed to initialize PAM\n" +msgstr "Ihlulekile ukulungiselela ukuqalisa i-PAM \n" + +#: modules/pam_selinux/pam_selinux_check.c:107 +#, c-format +msgid "failed to pam_set_item()\n" +msgstr "Ihlulekile ukwenza i-pam_set_item()\n" + +#. error in fork() +#: modules/pam_selinux/pam_selinux_check.c:135 +#, c-format +msgid "login: failure forking: %m" +msgstr "ngena: Ihlulekile ukuhlukanisa: %m" + +#: modules/pam_timestamp/pam_timestamp.c:385 +#, c-format +msgid "Access has been granted (last access was %ld seconds ago)." +msgstr "" + +#: modules/pam_unix/pam_unix_acct.c:232 modules/pam_unix/pam_unix_acct.c:254 +#, fuzzy +msgid "Your account has expired; please contact your system administrator." +msgstr "" +"I-akhawunti yakho isiphelelwe isikhathi, sicela uthintana nomqondisi " +"wesistimu yakho" + +#: modules/pam_unix/pam_unix_acct.c:240 +#, fuzzy +msgid "" +"You are required to change your password immediately (administrator " +"enforced)." +msgstr "" +"Kudingeka ukuba ushintshe iphasiwedi yakho ngokushesha (iphoqelelwa " +"ngumqondisi)" + +#: modules/pam_unix/pam_unix_acct.c:246 +#, fuzzy +msgid "" +"You are required to change your password immediately (password expired)." +msgstr "" +"Kudingeka ukuba ushintshe iphasiwedi yakho ngokushesha (iphasiwedi indala)" + +#: modules/pam_unix/pam_unix_acct.c:271 modules/pam_unix/pam_unix_acct.c:278 +#, c-format +msgid "Warning: your password will expire in %d day." +msgid_plural "Warning: your password will expire in %d days." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: only used if dngettext is not supported +#: modules/pam_unix/pam_unix_acct.c:283 +#, c-format +msgid "Warning: your password will expire in %d days." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:459 +msgid "NIS password could not be changed." +msgstr "Iphasiwedi ye-NIS ayivumanga ukushintshwa." + +#: modules/pam_unix/pam_unix_passwd.c:557 +#, fuzzy +msgid "No password has been supplied." +msgstr "Ayikho iphasiwedi enikeziwe" + +#: modules/pam_unix/pam_unix_passwd.c:558 +#, fuzzy +msgid "The password has not been changed." +msgstr "Iphasiwedi ye-NIS ayivumanga ukushintshwa." + +#: modules/pam_unix/pam_unix_passwd.c:575 +msgid "You must choose a shorter password." +msgstr "Kumele ukhethe iphasiwedi emifushane." + +#: modules/pam_unix/pam_unix_passwd.c:579 +#, fuzzy +msgid "You must choose a longer password." +msgstr "Kumelwe ukhethe iphasiwedi ethe ukuba yinjana" + +#: modules/pam_unix/pam_unix_passwd.c:684 +#, c-format +msgid "Changing password for %s." +msgstr "" + +#: modules/pam_unix/pam_unix_passwd.c:714 +#, fuzzy +msgid "You must wait longer to change your password." +msgstr "Kumelwe ulinde isikhashana ukuze ushintshe iphasiwedi yakho" + +#, fuzzy +#~ msgid "You have no mail." +#~ msgstr "Unemeyili entsha." + +#~ msgid "is the same as the old one" +#~ msgstr "iyafana nendala" + +#~ msgid "is a palindrome" +#~ msgstr "iyi-palindrome" + +#~ msgid "case changes only" +#~ msgstr "kushintshe onobumba kuphela" + +#~ msgid "is too similar to the old one" +#~ msgstr "ifana kakhulu nendala" + +#~ msgid "is too simple" +#~ msgstr "ilula kakhulu" + +#~ msgid "is rotated" +#~ msgstr "ijikelezisiwe" + +#~ msgid "BAD PASSWORD: %s" +#~ msgstr "IPHASIWEDI ENGASEBENZI: %s" + +#~ msgid "Authentication error" +#~ msgstr "Iphutha lokugunyaza" + +#~ msgid "Service error" +#~ msgstr "Iphutha lesevisi" + +#~ msgid "Unknown user" +#~ msgstr "Umsebenzisi ongaziwa" + +#~ msgid "Unknown error" +#~ msgstr "Iphutha elingaziwa" + +#~ msgid "%s: Bad number given to --reset=\n" +#~ msgstr "%s: Inombolo eyiphutha enikeziwe ukuba --uqale kabusha=\n" + +#~ msgid "%s: Unrecognised option %s\n" +#~ msgstr "%s: Okukhethile okungaziwa %s\n" + +#~ msgid "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" +#~ msgstr "" +#~ "%s: [--file rooted-filename] [--user username] [--reset[=n]] [--quiet]\n" + +#~ msgid "%s: Can't reset all users to non-zero\n" +#~ msgstr "" +#~ "%s: Ayikwazi ukusetha kabusha bonke abasebenzisi ibase enombolweni " +#~ "ongelona iqanda\n" diff --git a/tests/confdir b/tests/confdir new file mode 100644 index 0000000..3883c86 --- /dev/null +++ b/tests/confdir @@ -0,0 +1 @@ +# This is an empty pam service file for tst-pam_start_confdir diff --git a/tests/meson.build b/tests/meson.build new file mode 100644 index 0000000..21811b1 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,48 @@ +foreach name: ['dlopen', + 'pam_start', + 'pam_end', + 'pam_fail_delay', + 'pam_open_session', + 'pam_close_session', + 'pam_acct_mgmt', + 'pam_authenticate', + 'pam_chauthtok', + 'pam_setcred', + 'pam_get_authtok', + 'pam_get_item', + 'pam_set_item', + 'pam_getenvlist', + 'pam_get_user', + 'pam_get_data', + 'pam_set_data', + 'pam_mkargv', + 'pam_start_confdir', + ] + tst_deps = [libpam_internal_dep, libpam_dep] + if name == 'dlopen' + tst_deps += libdl + endif + tst_name = 'tst-' + name + + tst_exe = executable( + tst_name, + sources: tst_name + '.c', + dependencies: tst_deps, + include_directories: [libpam_private_inc], + c_args: ['-DLIBPAM_COMPILE'], + link_args: exe_link_args, + ) + + if name == 'dlopen' + tst_dlopen = tst_exe + endif + + test( + tst_name, + chdir_meson_build_subdir, + args: [tst_exe], + env: ['MESON_BUILD_SUBDIR=' + meson.current_build_dir(), + 'srcdir=' + meson.current_source_dir() + ], + ) +endforeach diff --git a/tests/tst-dlopen.c b/tests/tst-dlopen.c new file mode 100644 index 0000000..cba3e9a --- /dev/null +++ b/tests/tst-dlopen.c @@ -0,0 +1,45 @@ +/* + Copyright (C) Nalin Dahyabhai 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +*/ + +#include + +#include +#include +#include +#include + +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + +/* Simple program to see if dlopen() would succeed. */ +int main(int argc, char **argv) +{ + int i; + struct stat st; + char buf[PATH_MAX]; + + for (i = 1; i < argc; i++) { + if (dlopen(argv[i], RTLD_NOW)) { + fprintf(stdout, "dlopen() of \"%s\" succeeded.\n", + argv[i]); + } else { + snprintf(buf, sizeof(buf), "./%s", argv[i]); + if ((stat(buf, &st) == 0) && dlopen(buf, RTLD_NOW)) { + fprintf(stdout, "dlopen() of \"./%s\" " + "succeeded.\n", argv[i]); + } else { + fprintf(stdout, "dlopen() of \"%s\" failed: " + "%s\n", argv[i], dlerror()); + return 1; + } + } + } + return 0; +} diff --git a/tests/tst-pam_acct_mgmt.c b/tests/tst-pam_acct_mgmt.c new file mode 100644 index 0000000..b355a97 --- /dev/null +++ b/tests/tst-pam_acct_mgmt.c @@ -0,0 +1,56 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +int +main (void) +{ + int retval; + + /* 1: Call with NULL as pam handle */ + retval = pam_acct_mgmt (NULL, 0); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_acct_mgmt (NULL, 0) returned PAM_SUCCESS\n"); + return 1; + } + + return 0; +} diff --git a/tests/tst-pam_authenticate.c b/tests/tst-pam_authenticate.c new file mode 100644 index 0000000..366a4b1 --- /dev/null +++ b/tests/tst-pam_authenticate.c @@ -0,0 +1,56 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +int +main (void) +{ + int retval; + + /* 1: Call with NULL as pam handle */ + retval = pam_authenticate (NULL, 0); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "tst-pam_authenticate (NULL, 0) returned PAM_SUCCESS\n"); + return 1; + } + + return 0; +} diff --git a/tests/tst-pam_chauthtok.c b/tests/tst-pam_chauthtok.c new file mode 100644 index 0000000..5cf9a7e --- /dev/null +++ b/tests/tst-pam_chauthtok.c @@ -0,0 +1,56 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +int +main (void) +{ + int retval; + + /* 1: Call with NULL as pam handle */ + retval = pam_chauthtok (NULL, 0); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "tst-pam_chauthtok (NULL, 0) returned PAM_SUCCESS\n"); + return 1; + } + + return 0; +} diff --git a/tests/tst-pam_close_session.c b/tests/tst-pam_close_session.c new file mode 100644 index 0000000..9954b75 --- /dev/null +++ b/tests/tst-pam_close_session.c @@ -0,0 +1,56 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +int +main (void) +{ + int retval; + + /* 1: Call with NULL as pam handle */ + retval = pam_close_session (NULL, 0); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_close_session (NULL, 0) returned PAM_SUCCESS\n"); + return 1; + } + + return 0; +} diff --git a/tests/tst-pam_end.c b/tests/tst-pam_end.c new file mode 100644 index 0000000..902ae71 --- /dev/null +++ b/tests/tst-pam_end.c @@ -0,0 +1,77 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +int +main (void) +{ + const char *service = "dummy"; + const char *user = "root"; + struct pam_conv conv = { NULL, NULL }; + pam_handle_t *pamh; + int retval; + + /* 1: close valid pam handle */ + /* create valid pam handle */ + retval = pam_start (service, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + retval = pam_end (pamh, 0); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_end (pamh, 0) returned %d\n", retval); + return 1; + } + + /* 2: close NULL pam handle */ + retval = pam_end (NULL, 0); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_end (NULL, 0) returned %d\n", retval); + return 1; + } + + return 0; +} diff --git a/tests/tst-pam_fail_delay.c b/tests/tst-pam_fail_delay.c new file mode 100644 index 0000000..0126af9 --- /dev/null +++ b/tests/tst-pam_fail_delay.c @@ -0,0 +1,79 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +int +main (void) +{ + const char *service = "dummy"; + const char *user = "root"; + struct pam_conv conv = { NULL, NULL }; + pam_handle_t *pamh; + int retval; + + /* 1: set valid delay */ + /* create valid pam handle */ + retval = pam_start (service, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + retval = pam_fail_delay (pamh, 60); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_fail_delay (pamh, 60) returned %d\n", retval); + return 1; + } + + pam_end (pamh, retval); + + /* 2: use NULL pam handle */ + retval = pam_fail_delay (NULL, 60); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_fail_delay (NULL, 60) returned %d\n", retval); + return 1; + } + + return 0; +} diff --git a/tests/tst-pam_get_authtok.c b/tests/tst-pam_get_authtok.c new file mode 100644 index 0000000..ffda596 --- /dev/null +++ b/tests/tst-pam_get_authtok.c @@ -0,0 +1,51 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test_assert.h" +#include +#include + +int +main (void) +{ + const char *authtok = "test"; + const char *prompt = "test"; + + /* 1: Call pam_get_authtok_verify with NULL as pam handle */ + ASSERT_EQ(PAM_SYSTEM_ERR, pam_get_authtok_verify (NULL, &authtok, prompt)); + + /* 2: Call pam_get_authtok with NULL as pam handle */ + ASSERT_EQ(PAM_SYSTEM_ERR, pam_get_authtok (NULL, 0, &authtok, prompt)); + + return 0; +} diff --git a/tests/tst-pam_get_data.c b/tests/tst-pam_get_data.c new file mode 100644 index 0000000..d2fab9c --- /dev/null +++ b/tests/tst-pam_get_data.c @@ -0,0 +1,111 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test_assert.h" + +#include +#include +#include + +#include +#include +#include + +static void +tst_str_data_cleanup(pam_handle_t *pamh UNUSED, void *data, int error_status) +{ + const char *q = data ? "\"" : ""; + fprintf(stderr, + "tst_cleanup was called: data=%s%s%s, error_status=%d\n", + q, data ? (char *) data : "NULL", q, error_status); + free(data); +} + +int +main(void) +{ + const char *service = "dummy"; + const char *user = "root"; + struct pam_conv conv = { NULL, NULL }; + pam_handle_t *pamh; + void *dataptr; + const void *constdataptr; + + /* 1: Call with NULL as pam handle */ + ASSERT_NE(PAM_SUCCESS, + pam_get_data(NULL, "tst-pam_get_data-1", &constdataptr)); + + /* setup pam handle */ + ASSERT_EQ(PAM_SUCCESS, pam_start(service, user, &conv, &pamh)); + + /* 2: check for call from application */ + ASSERT_EQ(PAM_SYSTEM_ERR, + pam_get_data(pamh, "tst-pam_get_data-2", &constdataptr)); + + /* 3: Check that pam data is properly set and replaced */ + __PAM_TO_MODULE(pamh); + + ASSERT_NE(NULL, dataptr = strdup("test3a")); + ASSERT_EQ(PAM_SUCCESS, + pam_set_data(pamh, "tst-pam_get_data-3", dataptr, + tst_str_data_cleanup)); + ASSERT_EQ(PAM_SUCCESS, + pam_get_data(pamh, "tst-pam_get_data-3", &constdataptr)); + ASSERT_EQ(dataptr, constdataptr); + ASSERT_EQ(0, strcmp((const char *) constdataptr, "test3a")); + + ASSERT_NE(NULL, dataptr = strdup("test3b")); + ASSERT_EQ(PAM_SUCCESS, + pam_set_data(pamh, "tst-pam_get_data-3", dataptr, + tst_str_data_cleanup)); + ASSERT_EQ(PAM_SUCCESS, + pam_get_data(pamh, "tst-pam_get_data-3", &constdataptr)); + ASSERT_EQ(dataptr, constdataptr); + ASSERT_EQ(0, strcmp((const char *) constdataptr, "test3b")); + + ASSERT_EQ(PAM_SUCCESS, + pam_set_data(pamh, "tst-pam_get_data-3", NULL, + tst_str_data_cleanup)); + ASSERT_EQ(PAM_SUCCESS, + pam_get_data(pamh, "tst-pam_get_data-3", &constdataptr)); + ASSERT_EQ(NULL, constdataptr); + + ASSERT_EQ(PAM_NO_MODULE_DATA, + pam_get_data(pamh, "tst-pam_get_data-4", &constdataptr)); + + __PAM_TO_APP(pamh); + + ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 987)); + + return 0; +} diff --git a/tests/tst-pam_get_item.c b/tests/tst-pam_get_item.c new file mode 100644 index 0000000..cfb2a32 --- /dev/null +++ b/tests/tst-pam_get_item.c @@ -0,0 +1,132 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include +#include "pam_inline.h" + +struct mapping { + int type; + const char *string; + int expected; +}; + +struct mapping items[] = { + {PAM_SERVICE, "PAM_SERVICE", PAM_SUCCESS}, + {PAM_USER, "PAM_USER", 0}, + {PAM_TTY, "PAM_TTY", 0}, + {PAM_RHOST, "PAM_RHOST", 0}, + {PAM_CONV, "PAM_CONV", 0}, + {PAM_AUTHTOK, "PAM_AUTHTOK", PAM_BAD_ITEM}, + {PAM_OLDAUTHTOK, "PAM_OLDAUTHTOK", PAM_BAD_ITEM}, + {PAM_RUSER, "PAM_RUSER", 0}, + {PAM_USER_PROMPT, "PAM_USER_PROMPT", 0}, + {PAM_FAIL_DELAY, "PAM_FAIL_DELAY", 0}, + {PAM_AUTHTOK_TYPE, "PAM_AUTHTOK_TYPE", 0} +}; + +int +main (void) +{ + const char *service = "dummy"; + const char *user = "root"; + struct pam_conv conv = { NULL, NULL }; + pam_handle_t *pamh; + int retval; + unsigned int i; + const void *value; + + /* 1: Call with NULL as pam handle */ + retval = pam_get_item (NULL, PAM_SERVICE, &value); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_get_item (NULL, 0) returned PAM_SUCCESS\n"); + return 1; + } + + /* setup pam handle */ + retval = pam_start (service, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + /* 2: check for valid item types. Expected return value is + PAM_SUCCESS, except it has to fail. */ + for (i = 0; i < PAM_ARRAY_SIZE(items); i++) + { + retval = pam_get_item (pamh, items[i].type, &value); + + if (retval != items[i].expected) + { + fprintf (stderr, + "pam_get_item failed to get value for %s. Returned %d\n", + items[i].string, retval); + return 1; + } + } + + /* 3: check for bad item */ + retval = pam_get_item (pamh, -1, &value); + if (retval != PAM_BAD_ITEM) + { + fprintf (stderr, + "pam_get_item returned %d when expecting PAM_BAD_ITEM\n", + retval); + return 1; + } + + /* 4: check for valid item types, but NULL as value address. */ + for (i = 0; i < PAM_ARRAY_SIZE(items); i++) + { + retval = pam_get_item (pamh, items[i].type, NULL); + + if (retval != PAM_PERM_DENIED) + { + fprintf (stderr, + "pam_get_item returned %d to get value for %s\n", + retval, items[i].string); + return 1; + } + } + + pam_end (pamh, 0); + + return 0; +} diff --git a/tests/tst-pam_get_user.c b/tests/tst-pam_get_user.c new file mode 100644 index 0000000..916c6cc --- /dev/null +++ b/tests/tst-pam_get_user.c @@ -0,0 +1,172 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include + +static const char *prompt = "myprompt:"; +static const char *user = "itsme"; + +static int +login_conv (int num_msg, const struct pam_message **mesg, + struct pam_response **resp, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + reply = calloc(num_msg, sizeof (struct pam_response)); + + if (reply == NULL) + return PAM_BUF_ERR; + + for (count = 0; count < num_msg; count++) + { + reply[count].resp_retcode = 0; + reply[count].resp = NULL; + + switch (mesg[count]->msg_style) + { + case PAM_PROMPT_ECHO_ON: + if (strcmp (mesg[count]->msg, prompt) != 0) + { + fprintf (stderr, "conv function called with wrong prompt: %s\n", + mesg[count]->msg); + exit (1); + } + reply[count].resp = strdup (user); + break; + + default: + fprintf (stderr, + "pam_get_user calls conv function with unexpected msg style"); + exit (1); + } + } + + *resp = reply; + return PAM_SUCCESS; +} + +int +main (void) +{ + const char *service = "dummy"; + const char *value; + struct pam_conv conv = { &login_conv, NULL}; + pam_handle_t *pamh; + int retval; + + /* 1: Call with NULL for every argument */ + retval = pam_get_user (NULL, NULL, NULL); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, + "tst-pam_get_user (NULL, NULL, NULL) returned PAM_SUCCESS\n"); + return 1; + } + + /* setup pam handle */ + retval = pam_start (service, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + /* 2: Call with valid pamh handle but NULL for user */ + retval = pam_get_user (pamh, NULL, NULL); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, + "tst-pam_get_user (pamh, NULL, NULL) returned PAM_SUCCESS\n"); + return 1; + } + + /* 3: Call with valid pamh handle and valid user ptr */ + retval = pam_get_user (pamh, &value, NULL); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "tst-pam_get_user (pamh, &value, NULL) returned %d\n", + retval); + return 1; + } + if (strcmp (user, value) != 0) + { + fprintf (stderr, + "tst-pam_get_user (pamh, &value, NULL) mismatch:\n" + "expected: %s\n" + "got: %s\n", user, value); + return 1; + } + + pam_end (pamh, 0); + + /* setup pam handle without user */ + retval = pam_start (service, NULL, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + /* 4: Call with valid pamh handle and valid user ptr */ + retval = pam_get_user (pamh, &value, prompt); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "tst-pam_get_user (pamh, &value, prompt) returned %d\n", + retval); + return 1; + } + if (strcmp (user, value) != 0) + { + fprintf (stderr, + "tst-pam_get_user (pamh, &value, prompt) mismatch:\n" + "expected: %s\n" + "got: %s\n", user, value); + return 1; + } + + pam_end (pamh, 0); + + return 0; +} diff --git a/tests/tst-pam_getenvlist.c b/tests/tst-pam_getenvlist.c new file mode 100644 index 0000000..4e90c0c --- /dev/null +++ b/tests/tst-pam_getenvlist.c @@ -0,0 +1,134 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include + +const char *envvals[] = {"VAL1=1", "VAL2=2", "VAL3=3"}; + +int +main (void) +{ + const char *service = "dummy"; + const char *user = "root"; + struct pam_conv conv = { NULL, NULL }; + pam_handle_t *pamh; + int retval; + char **ptr; + char *temp; + int var, i; + + /* 1: Call with NULL as pam handle */ + ptr = pam_getenvlist (NULL); + if (ptr != NULL) + { + fprintf (stderr, "pam_getenvlist (NULL) does not return NULL\n"); + return 1; + } + + /* setup pam handle */ + retval = pam_start (service, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + /* 2: Call with pam handle, but no environment set */ + ptr = pam_getenvlist (pamh); + if (ptr == NULL || *ptr != NULL) + { + fprintf (stderr, + "pam_getenvlist (pamh) does not return pointer to NULL\n"); + temp = ptr ? *ptr : NULL; + var = 0; + while (temp) + { + printf ("%s\n", temp); + var++; + temp = *(ptr + var); + } + return 1; + } + free (ptr); + + /* set environment variable */ + for (i = 0; i < 3; i++) + { + retval = pam_putenv (pamh, envvals[i]); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_putenv (pamh, \"%s\") returned %d\n", + envvals[i], retval); + return 1; + } + } + + /* 3: Call with pam handle and environment set */ + ptr = pam_getenvlist (pamh); + if (ptr == NULL) + { + fprintf (stderr, "pam_getenvlist (pamh) returned NULL\n"); + return 1; + } + else + { + temp = *ptr; + var = 0; + while (temp) + { + if (strcmp (temp, envvals[var]) != 0) + { + fprintf (stderr, + "pam_getenvlist returns wrong value:\n" + "expected: %s\n" + "got: %s\n", envvals[var], temp); + return 1; + } + free (temp); + var++; + temp = *(ptr + var); + } + free (ptr); + } + + pam_end (pamh, retval); + + return 0; +} diff --git a/tests/tst-pam_mkargv.c b/tests/tst-pam_mkargv.c new file mode 100644 index 0000000..25d07cc --- /dev/null +++ b/tests/tst-pam_mkargv.c @@ -0,0 +1,54 @@ +/* + Copyright (C) Thorsten Kukuk 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation in version 2 of the License. +*/ + +#include + +#include +#include + +#include "pam_misc.c" + +/* Simple program to see if _pam_mkargv() would succeed. */ +int main(void) +{ + static const char argvstring[] = "user = XENDT\\userα user=XENDT\\user1"; + static const char * const argvresult[] = {"user", "=", "XENDT\\userα", + "user=XENDT\\user1"}; + int myargc; + char **myargv; + int argvlen; + int explen; + int i; + + explen = sizeof(argvstring) * ((sizeof(char)) + sizeof(char *)); + argvlen = _pam_mkargv(argvstring, &myargv, &myargc); + +#if 0 + printf ("argvlen=%i, argc=%i", argvlen, myargc); + for (i = 0; i < myargc; i++) { + printf(", argv[%d]=%s", i, myargv[i]); + } + printf ("\n"); +#endif + + if (argvlen != explen) + return 1; + + if (myargc != 4) + return 1; + + for (i = 0; i < 4; i++) + { + if (strcmp (myargv[i], argvresult[i]) != 0) + return 1; + } + + free(myargv); + + return 0; +} diff --git a/tests/tst-pam_open_session.c b/tests/tst-pam_open_session.c new file mode 100644 index 0000000..5ff91b8 --- /dev/null +++ b/tests/tst-pam_open_session.c @@ -0,0 +1,56 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +int +main (void) +{ + int retval; + + /* 1: Call with NULL as pam handle */ + retval = pam_open_session (NULL, 0); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_open_session (NULL, 0) returned PAM_SUCCESS\n"); + return 1; + } + + return 0; +} diff --git a/tests/tst-pam_set_data.c b/tests/tst-pam_set_data.c new file mode 100644 index 0000000..3fbe062 --- /dev/null +++ b/tests/tst-pam_set_data.c @@ -0,0 +1,485 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include +#include + +static int cleanup_was_called = 0; +static int cleanup3_was_called = 0; +static int cleanup3_retval = 0; +static int cleanup6_was_called = 0; +static int cleanup6_retval = 0; +static int cleanup7_was_called = 0; +static int cleanup7_retval = 0; +static int cleanup7b_was_called = 0; +static int cleanup7b_retval = 0; +static int cleanup8_was_called = 0; +static int cleanup8_retval = 0; + +static void +tst_cleanup (pam_handle_t *pamh UNUSED, void *data, int error_status) +{ + cleanup_was_called = 1; + fprintf (stderr, "tst_cleanup was called: data=\"%s\", error_status=%d\n", + (char *)data, error_status); +} + +static void +tst_cleanup_3 (pam_handle_t *pamh UNUSED, void *data, int error_status) +{ + cleanup3_was_called = 1; + + if (strcmp (data, "test3") != 0) + { + fprintf (stderr, "tst_cleanup_3 called with wrong data, got \"%s\"\n", + (char *)data); + cleanup3_retval = 1; + return; + } + + free (data); + + if (error_status & PAM_DATA_REPLACE) + { + fprintf (stderr, "tst_cleanup_3 called with PAM_DATA_REPLACE set\n"); + cleanup3_retval = 1; + return; + } + + if (error_status & PAM_DATA_SILENT) + { + fprintf (stderr, "tst_cleanup_3 called with PAM_DATA_SILENT set\n"); + cleanup3_retval = 1; + return; + } + + if (error_status != 0) + { + fprintf (stderr, "tst_cleanup_3 called with error_status set: %d\n", + error_status); + cleanup3_retval = 1; + return; + } +} + +static void +tst_cleanup_6 (pam_handle_t *pamh UNUSED, void *data, int error_status) +{ + cleanup6_was_called = 1; + + if (error_status & PAM_DATA_SILENT) + { + fprintf (stderr, "tst_cleanup_6 called with PAM_DATA_SILENT set\n"); + cleanup6_retval = 1; + return; + } + + if (error_status & PAM_DATA_REPLACE) + { + if (strcmp (data, "test6a") != 0) + { + fprintf (stderr, "tst_cleanup_6 called with wrong data, got \"%s\"\n", + (char *)data); + cleanup6_retval = 1; + return; + } + + if (error_status != PAM_DATA_REPLACE) + { + fprintf (stderr, "tst_cleanup_6 called with error_status set: %d\n", + error_status); + cleanup6_retval = 1; + return; + } + } + else + { + if (strcmp (data, "test6b") != 0) + { + fprintf (stderr, "tst_cleanup_6 called with wrong data, got \"%s\"\n", + (char *)data); + cleanup6_retval = 1; + return; + } + + if (error_status != 0) + { + fprintf (stderr, "tst_cleanup_6 called with error_status set: %d\n", + error_status); + cleanup6_retval = 1; + return; + } + } + + free (data); +} + +static void +tst_cleanup_7 (pam_handle_t *pamh UNUSED, void *data, int error_status) +{ + cleanup7_was_called = 1; + + if (error_status & PAM_DATA_SILENT) + { + fprintf (stderr, "tst_cleanup_7 called with PAM_DATA_SILENT set\n"); + cleanup7_retval = 1; + return; + } + + if (error_status & PAM_DATA_REPLACE) + { + if (strcmp (data, "test7a") != 0) + { + fprintf (stderr, "tst_cleanup_7 called with wrong data, got \"%s\"\n", + (char *)data); + cleanup7_retval = 1; + return; + } + + if (error_status != PAM_DATA_REPLACE) + { + fprintf (stderr, "tst_cleanup_7 called with error_status set: %d\n", + error_status); + cleanup7_retval = 1; + return; + } + } + else + { + fprintf (stderr, "tst_cleanup_7 called without PAM_DATA_REPLACE set: %d\n", + error_status); + cleanup7_retval = 1; + return; + } + + free (data); +} + +static void +tst_cleanup_7b (pam_handle_t *pamh UNUSED, void *data, int error_status) +{ + cleanup7b_was_called = 1; + + if (strcmp (data, "test7b") != 0) + { + fprintf (stderr, "tst_cleanup_7b called with wrong data, got \"%s\"\n", + (char *)data); + cleanup7b_retval = 1; + return; + } + + free (data); + + if (error_status & PAM_DATA_REPLACE) + { + fprintf (stderr, "tst_cleanup_7b called with PAM_DATA_REPLACE set\n"); + cleanup7b_retval = 1; + return; + } + + if (error_status & PAM_DATA_SILENT) + { + fprintf (stderr, "tst_cleanup_7b called with PAM_DATA_SILENT set\n"); + cleanup7b_retval = 1; + return; + } + + if (error_status != 0) + { + fprintf (stderr, "tst_cleanup_7b called with error_status set: %d\n", + error_status); + cleanup7b_retval = 1; + return; + } +} + +static void +tst_cleanup_8 (pam_handle_t *pamh UNUSED, void *data, int error_status) +{ + cleanup8_was_called = 1; + + if (strcmp (data, "test8") != 0) + { + fprintf (stderr, "tst_cleanup_8 called with wrong data, got \"%s\"\n", + (char *)data); + cleanup8_retval = 1; + return; + } + + free (data); + + if (error_status & PAM_DATA_REPLACE) + { + fprintf (stderr, "tst_cleanup_8 called with PAM_DATA_REPLACE set\n"); + cleanup8_retval = 1; + return; + } + + if (error_status & PAM_DATA_SILENT) + { + fprintf (stderr, "tst_cleanup_8 called with PAM_DATA_SILENT set\n"); + cleanup8_retval = 1; + return; + } + + if (error_status != 987) + { + fprintf (stderr, "tst_cleanup_8 called with wrong error_status set: %d\n", + error_status); + cleanup8_retval = 1; + return; + } +} + +int +main (void) +{ + const char *service = "dummy"; + const char *user = "root"; + struct pam_conv conv = { NULL, NULL }; + pam_handle_t *pamh; + void *dataptr; + int retval; + + /* 1: Call with NULL as pam handle */ + dataptr = strdup ("test1"); + retval = pam_set_data (NULL, "tst-pam_set_data-1", dataptr, tst_cleanup); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_set_data (NULL, ...) returned PAM_SUCCESS\n"); + return 1; + } + free (dataptr); + + /* setup pam handle */ + retval = pam_start (service, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + /* 2: check for call from application */ + dataptr = strdup ("test2"); + retval = pam_set_data (pamh, "tst-pam_set_data-2", dataptr, tst_cleanup); + if (retval != PAM_SYSTEM_ERR) + { + fprintf (stderr, + "pam_set_data returned %d when expecting PAM_SYSTEM_ERR\n", + retval); + return 1; + } + free (dataptr); + + + /* 3: check for call from module */ + __PAM_TO_MODULE(pamh); + dataptr = strdup ("test3"); + retval = pam_set_data (pamh, "tst-pam_set_data-3", dataptr, + tst_cleanup_3); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "pam_set_data failed: %d\n", + retval); + return 1; + } + + /* 4: check for call with NULL as module_data_name */ + dataptr = strdup ("test4"); + retval = pam_set_data (pamh, NULL, dataptr, tst_cleanup); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, + "pam_set_data with NULL as module_data_name succeeded!\n"); + return 1; + } + free (dataptr); + + /* 5: check for call with NULL as cleanup function */ + dataptr = strdup ("test5"); + retval = pam_set_data (pamh, "tst-pam_set_data-5", dataptr, NULL); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "pam_set_data with NULL as cleanup function failed: %d\n", + retval); + return 1; + } + free (dataptr); + + /* 6: Overwrite data and check cleanup flags */ + dataptr = strdup ("test6a"); + retval = pam_set_data (pamh, "tst-pam_set_data-6", dataptr, + tst_cleanup_6); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "test6: first pam_set_data failed: %d\n", + retval); + return 1; + } + + dataptr = strdup ("test6b"); + retval = pam_set_data (pamh, "tst-pam_set_data-6", dataptr, + tst_cleanup_6); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "test6: second pam_set_data failed: %d\n", + retval); + return 1; + } + + /* 7: Overwrite data and cleanup function, check cleanup flags */ + dataptr = strdup ("test7a"); + retval = pam_set_data (pamh, "tst-pam_set_data-7", dataptr, + tst_cleanup_7); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "test7: first pam_set_data failed: %d\n", + retval); + return 1; + } + + dataptr = strdup ("test7b"); + retval = pam_set_data (pamh, "tst-pam_set_data-7", dataptr, + tst_cleanup_7b); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "test7: second pam_set_data failed: %d\n", + retval); + return 1; + } + + __PAM_TO_APP(pamh); + + /* Close PAM handle and check return codes of cleanup functions */ + retval = pam_end (pamh, 0); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "pam_end reported an error: %d\n", + retval); + return 1; + } + + if (cleanup_was_called == 1) + return 1; + + if (cleanup3_was_called == 0) + { + fprintf (stderr, "tst_cleanup_3 was never called!\n"); + return 1; + } + if (cleanup3_retval != 0) + return 1; + + if (cleanup6_was_called == 0) + { + fprintf (stderr, "tst_cleanup_6 was never called!\n"); + return 1; + } + if (cleanup6_retval != 0) + return 1; + + if (cleanup7_was_called == 0) + { + fprintf (stderr, "tst_cleanup_7 was never called!\n"); + return 1; + } + if (cleanup7_retval != 0) + return 1; + + if (cleanup7b_was_called == 0) + { + fprintf (stderr, "tst_cleanup_7b was never called!\n"); + return 1; + } + if (cleanup7b_retval != 0) + return 1; + + /* test if error code is delivered to cleanup function */ + /* setup pam handle */ + retval = pam_start (service, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + /* 8: check if cleanup function is called with correct error code */ + __PAM_TO_MODULE(pamh); + dataptr = strdup ("test8"); + retval = pam_set_data (pamh, "tst-pam_set_data-8", dataptr, + tst_cleanup_8); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "test8: pam_set_data failed: %d\n", + retval); + return 1; + } + + __PAM_TO_APP(pamh); + + retval = pam_end (pamh, 987); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "pam_end reported an error: %d\n", + retval); + return 1; + } + + if (cleanup8_was_called == 0) + { + fprintf (stderr, "tst_cleanup_3 was never called!\n"); + return 1; + } + + if (cleanup8_retval != 0) + return 1; + + return 0; +} diff --git a/tests/tst-pam_set_item.c b/tests/tst-pam_set_item.c new file mode 100644 index 0000000..b7f21fb --- /dev/null +++ b/tests/tst-pam_set_item.c @@ -0,0 +1,158 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include +#include "pam_inline.h" + +struct mapping { + int type; + const char *string; + int expected; + const char *new_value; +}; + +struct mapping items[] = { + {PAM_SERVICE, "PAM_SERVICE", PAM_SUCCESS, "logout"}, + {PAM_USER, "PAM_USER", PAM_SUCCESS, "noroot"}, + {PAM_TTY, "PAM_TTY", PAM_SUCCESS, "TTyX"}, + {PAM_RHOST, "PAM_RHOST", PAM_SUCCESS, "remote"}, + {PAM_AUTHTOK, "PAM_AUTHTOK", PAM_BAD_ITEM, "none"}, + {PAM_OLDAUTHTOK, "PAM_OLDAUTHTOK", PAM_BAD_ITEM, "none"}, + {PAM_RUSER, "PAM_RUSER", PAM_SUCCESS, "noroot"}, + {PAM_USER_PROMPT, "PAM_USER_PROMPT", PAM_SUCCESS, "your name: "}, + {PAM_FAIL_DELAY, "PAM_FAIL_DELAY", PAM_SUCCESS, "4000"}, + {PAM_AUTHTOK_TYPE, "PAM_AUTHTOK_TYPE", PAM_SUCCESS, "U**X"} +}; + +int +main (void) +{ + const char *service = "dummy"; + const char *user = "root"; + struct pam_conv conv = { NULL, NULL }; + pam_handle_t *pamh; + int retval; + unsigned int i; + + /* 1: Call with NULL as pam handle */ + retval = pam_set_item (NULL, PAM_SERVICE, "dummy"); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_set_item (NULL, ...) returned PAM_SUCCESS\n"); + return 1; + } + + /* setup pam handle */ + retval = pam_start (service, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + /* 2: check for bad item */ + retval = pam_set_item (pamh, -1, "dummy"); + if (retval != PAM_BAD_ITEM) + { + fprintf (stderr, + "pam_set_item returned %d when expecting PAM_BAD_ITEM\n", + retval); + return 1; + } + + /* 3: try to set PAM_CONV to NULL */ + retval = pam_set_item (pamh, PAM_CONV, NULL); + if (retval != PAM_PERM_DENIED) + { + fprintf (stderr, + "pam_set_item (pamh, PAM_CONV, NULL) returned %d\n", + retval); + return 1; + } + + /* 4: try to replace all items */ + for (i = 0; i < PAM_ARRAY_SIZE(items); i++) + { + retval = pam_set_item (pamh, items[i].type, items[i].new_value); + + if (retval != items[i].expected) + { + fprintf (stderr, + "pam_set_item failed to set value for %s. Returned %d\n", + items[i].string, retval); + return 1; + } + else if (items[i].expected == PAM_SUCCESS) + { + const void *value; + + retval = pam_get_item (pamh, items[i].type, &value); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, + "pam_get_item was not able to fetch changed value: %d\n", + retval); + return 1; + } + if (strcmp (items[i].new_value, value) != 0) + { + fprintf (stderr, + "pam_get_item got wrong value:\n" + "expected: %s\n" + "got: %s\n", items[i].new_value, (const char *)value); + return 1; + } + } + } + + /* 5: try to set PAM_SERVICE to NULL */ + retval = pam_set_item (pamh, PAM_SERVICE, NULL); + if (retval != PAM_BAD_ITEM) + { + fprintf (stderr, + "pam_set_item (pamh, PAM_SERVICE, NULL) returned %d\n", + retval); + return 1; + } + + pam_end (pamh, 0); + + return 0; +} diff --git a/tests/tst-pam_setcred.c b/tests/tst-pam_setcred.c new file mode 100644 index 0000000..c878d64 --- /dev/null +++ b/tests/tst-pam_setcred.c @@ -0,0 +1,56 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +int +main (void) +{ + int retval; + + /* 1: Call with NULL as pam handle */ + retval = pam_setcred (NULL, 0); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_setcred (NULL, 0) returned PAM_SUCCESS\n"); + return 1; + } + + return 0; +} diff --git a/tests/tst-pam_start.c b/tests/tst-pam_start.c new file mode 100644 index 0000000..728eafe --- /dev/null +++ b/tests/tst-pam_start.c @@ -0,0 +1,109 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + + +int +main (void) +{ + const char *service = "dummy"; + const char *user = "root"; + struct pam_conv conv = { NULL, NULL }; + pam_handle_t *pamh; + int retval; + + /* 1: check with valid arguments */ + retval = pam_start (service, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, &pamh) returned %d\n", + service, user, retval); + return 1; + } + else if (pamh == NULL) + { + fprintf (stderr, + "pam_start (%s, %s, &conv, &pamh) returned NULL for pamh\n", + service, user); + return 1; + } + + pam_end (pamh, retval); + + /* 2: check with NULL for service */ + retval = pam_start (NULL, user, &conv, &pamh); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_start (NULL, %s, &conv, &pamh) returned %d\n", + user, retval); + return 1; + } + + /* 3: check with NULL for user */ + retval = pam_start (service, NULL, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, NULL, &conv, &pamh) returned %d\n", + service, retval); + return 1; + } + + pam_end (pamh, retval); + + + /* 4: check with NULL for conv */ + retval = pam_start (service, user, NULL, &pamh); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, NULL, &pamh) returned %d\n", + service, user, retval); + return 1; + } + + /* 5: check with NULL for pamh */ + retval = pam_start (service, user, &conv, NULL); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_start (%s, %s, &conv, NULL) returned %d\n", + service, user, retval); + return 1; + } + + return 0; +} diff --git a/tests/tst-pam_start_confdir.c b/tests/tst-pam_start_confdir.c new file mode 100644 index 0000000..cb2c570 --- /dev/null +++ b/tests/tst-pam_start_confdir.c @@ -0,0 +1,103 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include + + +int +main (void) +{ + const char *service = "confdir"; + const char *xservice = "nonexistent-service"; + const char *user = "root"; + const char *confdir; + const char *xconfdir = "/nonexistent-confdir"; + struct pam_conv conv = { NULL, NULL }; + pam_handle_t *pamh; + int retval; + + confdir = getenv("srcdir"); + + if (confdir == NULL) + { + fprintf (stderr, "Error: srcdir not set\n"); + return 1; + } + + /* 1: check with valid arguments */ + retval = pam_start_confdir (service, user, &conv, confdir, &pamh); + if (retval != PAM_SUCCESS) + { + fprintf (stderr, "pam_start_confdir (%s, %s, &conv, %s, &pamh) returned %d\n", + service, user, confdir, retval); + return 1; + } + else if (pamh == NULL) + { + fprintf (stderr, "pam_start_confdir (%s, %s, &conv, %s, &pamh) returned NULL for pamh\n", + service, user, confdir); + return 1; + } + + pam_end (pamh, retval); + + /* 2: check with invalid service */ + retval = pam_start_confdir (xservice, user, &conv, confdir, &pamh); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_start_confdir (%s, %s, &conv, %s, &pamh) incorrectly succeeded\n", + xservice, user, confdir); + return 1; + } + + pam_end (pamh, retval); + + /* 3: check with invalid confdir */ + retval = pam_start_confdir (service, user, &conv, xconfdir, &pamh); + if (retval == PAM_SUCCESS) + { + fprintf (stderr, "pam_start_confdir (%s, %s, &conv, %s, &pamh) incorrectly succeeded\n", + service, user, xconfdir); + return 1; + } + + pam_end (pamh, retval); + + return 0; +} diff --git a/xtests/access.conf b/xtests/access.conf new file mode 100644 index 0000000..25462dd --- /dev/null +++ b/xtests/access.conf @@ -0,0 +1,3 @@ + +-:ALL EXCEPT (tstpamaccess) tstpamaccess3 :LOCAL +-:ALL:127.0.0.1 diff --git a/xtests/group.conf b/xtests/group.conf new file mode 100644 index 0000000..2cb3487 --- /dev/null +++ b/xtests/group.conf @@ -0,0 +1,2 @@ + +tst-pam_group1;tty1;tstpamgrp;Al0000-2400;tstpamgrpg diff --git a/xtests/limits.conf b/xtests/limits.conf new file mode 100644 index 0000000..41a3ea3 --- /dev/null +++ b/xtests/limits.conf @@ -0,0 +1,2 @@ +* soft nice 19 +* hard nice -20 diff --git a/xtests/meson.build b/xtests/meson.build new file mode 100644 index 0000000..96b2612 --- /dev/null +++ b/xtests/meson.build @@ -0,0 +1,70 @@ +xtest_progs = [ + 'tst-pam_access1', + 'tst-pam_access2', + 'tst-pam_access3', + 'tst-pam_access4', + 'tst-pam_authfail', + 'tst-pam_authsucceed', + 'tst-pam_dispatch1', + 'tst-pam_dispatch2', + 'tst-pam_dispatch3', + 'tst-pam_dispatch4', + 'tst-pam_dispatch5', + 'tst-pam_group1', + 'tst-pam_limits1', + 'tst-pam_motd', + 'tst-pam_pwhistory1', + 'tst-pam_shells', + 'tst-pam_succeed_if1', + 'tst-pam_time1', + 'tst-pam_unix1', + 'tst-pam_unix2', + 'tst-pam_unix3', + 'tst-pam_unix4', +] + +foreach prog: xtest_progs + tst_exe = executable( + prog, + sources: prog + '.c', + c_args: ['-DLIBPAM_COMPILE'], + link_args: exe_link_args, + include_directories: [libpamc_inc], + dependencies: [libpam_dep, libpam_misc_dep], + ) + + test( + 'xtests ' + prog, + chdir_meson_build_subdir, + args: [ + files('run-xtests.sh'), + meson.current_source_dir(), + fs.name(tst_exe), + ], + env: ['MESON_BUILD_SUBDIR=' + meson.current_build_dir()], + is_parallel: false, + ) +endforeach + +xtest_scripts = [ + 'tst-pam_assemble_line1', + 'tst-pam_substack1', + 'tst-pam_substack2', + 'tst-pam_substack3', + 'tst-pam_substack4', + 'tst-pam_substack5', +] + +foreach name: xtest_scripts + test( + 'xtests ' + name, + chdir_meson_build_subdir, + args: [ + files('run-xtests.sh'), + meson.current_source_dir(), + name, + ], + env: ['MESON_BUILD_SUBDIR=' + meson.current_build_dir()], + is_parallel: false, + ) +endforeach diff --git a/xtests/run-xtests.sh b/xtests/run-xtests.sh new file mode 100755 index 0000000..42e92d7 --- /dev/null +++ b/xtests/run-xtests.sh @@ -0,0 +1,80 @@ +#!/bin/sh + +SRCDIR=$1 +shift 1 +[ -z "${SRCDIR}" ] && SRCDIR='.' + +if test `id -u` -ne 0 ; then + echo "You need to be root to run the tests" + exit 77 +fi + +XTESTS="$@" + +failed=0 +pass=0 +skipped=0 +all=0 + +mkdir -p /etc/security +for config in access.conf group.conf time.conf limits.conf ; do + [ -f "/etc/security/$config" ] && + mv /etc/security/$config /etc/security/$config-pam-xtests + install -m 644 "${SRCDIR}"/$config /etc/security/$config +done +[ -f /etc/shells ] && + mv /etc/shells /etc/shells-pam-xtests +install -m 644 "${SRCDIR}"/shells.conf /etc/shells + +[ -f /etc/security/opasswd ] && + mv /etc/security/opasswd /etc/security/opasswd-pam-xtests + +for testname in $XTESTS ; do + for cfg in "${SRCDIR}"/$testname*.pamd ; do + install -m 644 $cfg /etc/pam.d/$(basename $cfg .pamd) + done + if test -f "${SRCDIR}"/$testname.sh ; then + test -x "${SRCDIR}"/$testname.sh || chmod 755 "${SRCDIR}"/$testname.sh + "${SRCDIR}"/$testname.sh > /dev/null + else + ./$testname > /dev/null + fi + RETVAL=$? + if test $RETVAL -eq 77 ; then + echo "SKIP: $testname" + skipped=`expr $skipped + 1` + elif test $RETVAL -ne 0 ; then + echo "FAIL: $testname" + failed=`expr $failed + 1` + else + echo "PASS: $testname" + pass=`expr $pass + 1` + fi + all=`expr $all + 1` + rm -f /etc/pam.d/$testname* +done + +for config in access.conf group.conf time.conf limits.conf opasswd ; do + if [ -f "/etc/security/$config-pam-xtests" ]; then + mv /etc/security/$config-pam-xtests /etc/security/$config + else + rm -f /etc/security/$config + fi +done + +[ -f "/etc/shells-pam-xtests" ] && + mv /etc/shells-pam-xtests /etc/shells + +if test "$failed" -ne 0; then + echo "===================" + echo "$failed of $all tests failed" + echo "$skipped tests not run" + echo "===================" + exit 1 +else + echo "==================" + echo "$all tests passed" + echo "$skipped tests not run" + echo "==================" +fi +exit 0 diff --git a/xtests/shells.conf b/xtests/shells.conf new file mode 100644 index 0000000..74776e6 --- /dev/null +++ b/xtests/shells.conf @@ -0,0 +1,3 @@ +/bin/ash +/bin/testbash +/bin/csh diff --git a/xtests/time.conf b/xtests/time.conf new file mode 100644 index 0000000..518124c --- /dev/null +++ b/xtests/time.conf @@ -0,0 +1,2 @@ +*;*;you|me;!Al0000-2400 +*;*;x|y;!Al0000-2400 diff --git a/xtests/tst-pam_access1.c b/xtests/tst-pam_access1.c new file mode 100644 index 0000000..271f486 --- /dev/null +++ b/xtests/tst-pam_access1.c @@ -0,0 +1,130 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + test case: + + Check the following line in access.conf: + -:ALL EXCEPT (tstpamaccess):LOCAL + + User is member of group tstpamaccess, pam_authenticate should pass. +*/ + +#include + +#include +#include +#include +#include + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup ("!!"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh = NULL; + const char *user="tstpamaccess1"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_access1", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access1: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_set_item (pamh, PAM_TTY, "/dev/tty1"); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, + "pam_access1: pam_set_item(PAM_TTY) returned %d\n", + retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access1: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access1: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_access1.pamd b/xtests/tst-pam_access1.pamd new file mode 100644 index 0000000..a70f2d9 --- /dev/null +++ b/xtests/tst-pam_access1.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_access.so nodefgroup +account required pam_permit.so +password required pam_permit.so +session required pam_permit.so diff --git a/xtests/tst-pam_access1.sh b/xtests/tst-pam_access1.sh new file mode 100755 index 0000000..70521d2 --- /dev/null +++ b/xtests/tst-pam_access1.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +/usr/sbin/groupadd tstpamaccess +/usr/sbin/useradd -G tstpamaccess -p '!!' tstpamaccess1 +./tst-pam_access1 +RET=$? +/usr/sbin/userdel -r tstpamaccess1 2> /dev/null +/usr/sbin/groupdel tstpamaccess 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_access2.c b/xtests/tst-pam_access2.c new file mode 100644 index 0000000..7c427f9 --- /dev/null +++ b/xtests/tst-pam_access2.c @@ -0,0 +1,130 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + test case: + + Check the following line in access.conf: + -:ALL EXCEPT (tstpamaccess):LOCAL + + User is not member of group tstpamaccess, pam_authenticate should fail. +*/ + +#include + +#include +#include +#include +#include + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup ("!!"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh = NULL; + const char *user="tstpamaccess2"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_access2", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access2: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_set_item (pamh, PAM_TTY, "/dev/tty1"); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, + "pam_access2: pam_set_item(PAM_TTY) returned %d\n", + retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_PERM_DENIED) + { + if (debug) + fprintf (stderr, "pam_access2: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access2: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_access2.pamd b/xtests/tst-pam_access2.pamd new file mode 100644 index 0000000..a70f2d9 --- /dev/null +++ b/xtests/tst-pam_access2.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_access.so nodefgroup +account required pam_permit.so +password required pam_permit.so +session required pam_permit.so diff --git a/xtests/tst-pam_access2.sh b/xtests/tst-pam_access2.sh new file mode 100755 index 0000000..7e3e60f --- /dev/null +++ b/xtests/tst-pam_access2.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +/usr/sbin/groupadd tstpamaccess +/usr/sbin/useradd -p '!!' tstpamaccess2 +./tst-pam_access2 +RET=$? +/usr/sbin/userdel -r tstpamaccess2 2> /dev/null +/usr/sbin/groupdel tstpamaccess 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_access3.c b/xtests/tst-pam_access3.c new file mode 100644 index 0000000..508f244 --- /dev/null +++ b/xtests/tst-pam_access3.c @@ -0,0 +1,130 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + test case: + + Check the following line in access.conf: + -:ALL EXCEPT tstpamaccess3 :LOCAL + + pam_authenticate should pass for user tstpamaccess3 +*/ + +#include + +#include +#include +#include +#include + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup ("!!"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh = NULL; + const char *user="tstpamaccess3"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_access3", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access3: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_set_item (pamh, PAM_TTY, "/dev/tty1"); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, + "pam_access3: pam_set_item(PAM_TTY) returned %d\n", + retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access3: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access3: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_access3.pamd b/xtests/tst-pam_access3.pamd new file mode 100644 index 0000000..a70f2d9 --- /dev/null +++ b/xtests/tst-pam_access3.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_access.so nodefgroup +account required pam_permit.so +password required pam_permit.so +session required pam_permit.so diff --git a/xtests/tst-pam_access3.sh b/xtests/tst-pam_access3.sh new file mode 100755 index 0000000..3630e2e --- /dev/null +++ b/xtests/tst-pam_access3.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +/usr/sbin/useradd -p '!!' tstpamaccess3 +./tst-pam_access3 +RET=$? +/usr/sbin/userdel -r tstpamaccess3 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_access4.c b/xtests/tst-pam_access4.c new file mode 100644 index 0000000..73593d5 --- /dev/null +++ b/xtests/tst-pam_access4.c @@ -0,0 +1,168 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + test case: + + Check the following lines in access.conf: + -:ALL EXCEPT tstpamaccess3 :LOCAL + -:ALL:127.0.0.1 + + pam_authenticate should fail for /dev/tty1, pass for www.example.com, + and fail again for localhost +*/ + +#include + +#include +#include +#include +#include + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup ("!!"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh = NULL; + const char *user="tstpamaccess4"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_access4", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access4: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_set_item (pamh, PAM_TTY, "/dev/tty1"); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, + "pam_access4-1: pam_set_item(PAM_TTY) returned %d\n", + retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_PERM_DENIED) + { + if (debug) + fprintf (stderr, "pam_access4-1: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_set_item (pamh, PAM_RHOST, "www.example.com"); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, + "pam_access4-2: pam_set_item(PAM_RHOST) returned %d\n", + retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access4-2: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_set_item (pamh, PAM_RHOST, "localhost"); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, + "pam_access4-3: pam_set_item(PAM_RHOST) returned %d\n", + retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_PERM_DENIED) + { + if (debug) + fprintf (stderr, "pam_access4-3: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access4: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_access4.pamd b/xtests/tst-pam_access4.pamd new file mode 100644 index 0000000..a70f2d9 --- /dev/null +++ b/xtests/tst-pam_access4.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_access.so nodefgroup +account required pam_permit.so +password required pam_permit.so +session required pam_permit.so diff --git a/xtests/tst-pam_access4.sh b/xtests/tst-pam_access4.sh new file mode 100755 index 0000000..4538df4 --- /dev/null +++ b/xtests/tst-pam_access4.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +/usr/sbin/useradd -p '!!' tstpamaccess4 +./tst-pam_access4 +RET=$? +/usr/sbin/userdel -r tstpamaccess4 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_assemble_line1.pamd b/xtests/tst-pam_assemble_line1.pamd new file mode 100644 index 0000000..431b3ba --- /dev/null +++ b/xtests/tst-pam_assemble_line1.pamd @@ -0,0 +1,8 @@ +#%PAM-1.0 +# Test that _pam_assemble_line() does not crash with long lines. +# printf '%511s\\\n%511s\\\n%511s\\\n%511s\\\n' + \ + \ + \ + \ +auth required pam_deny.so diff --git a/xtests/tst-pam_assemble_line1.sh b/xtests/tst-pam_assemble_line1.sh new file mode 100755 index 0000000..dc2a675 --- /dev/null +++ b/xtests/tst-pam_assemble_line1.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec ./tst-pam_authfail tst-pam_assemble_line1 diff --git a/xtests/tst-pam_authfail.c b/xtests/tst-pam_authfail.c new file mode 100644 index 0000000..448b721 --- /dev/null +++ b/xtests/tst-pam_authfail.c @@ -0,0 +1,94 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + + +/* Check that auth stack fails. */ + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="nobody"; + const char *stack="tst-pam_authfail"; + int retval; + int debug = 0; + + if (argc > 2) { + stack = argv[2]; + } + + if (argc > 1) { + if (strcmp (argv[1], "-d") == 0) + debug = 1; + else + stack = argv[1]; + } + + + retval = pam_start(stack, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test3: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_authenticate(pamh, 0); + if (retval == PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test3: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end(pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test3: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_authfail.pamd b/xtests/tst-pam_authfail.pamd new file mode 100644 index 0000000..8ff1a40 --- /dev/null +++ b/xtests/tst-pam_authfail.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +# test that successful sufficient module cannot affect stack +# after failed required module +auth required pam_debug.so auth=perm_denied +auth sufficient pam_debug.so auth=success diff --git a/xtests/tst-pam_authsucceed.c b/xtests/tst-pam_authsucceed.c new file mode 100644 index 0000000..936d8ec --- /dev/null +++ b/xtests/tst-pam_authsucceed.c @@ -0,0 +1,94 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + + +/* Check that auth stack succeeds. */ + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="nobody"; + const char *stack="tst-pam_authsucceed"; + int retval; + int debug = 0; + + if (argc > 2) { + stack = argv[2]; + } + + if (argc > 1) { + if (strcmp (argv[1], "-d") == 0) + debug = 1; + else + stack = argv[1]; + } + + + retval = pam_start(stack, user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test3: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_authenticate(pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test3: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end(pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test3: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_authsucceed.pamd b/xtests/tst-pam_authsucceed.pamd new file mode 100644 index 0000000..abaa1ef --- /dev/null +++ b/xtests/tst-pam_authsucceed.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +# test that failed sufficient module cannot affect stack +# with following successful required module +auth sufficient pam_debug.so auth=auth_err +auth required pam_debug.so auth=success diff --git a/xtests/tst-pam_dispatch1.c b/xtests/tst-pam_dispatch1.c new file mode 100644 index 0000000..0c082c1 --- /dev/null +++ b/xtests/tst-pam_dispatch1.c @@ -0,0 +1,97 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + +static int debug = 0; + +/* + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=196859 + + This stack should not return PAM_IGNORE to the application: + auth [default=bad] pam_debug.so auth=ignore +*/ +static int +test1 (void) +{ + pam_handle_t *pamh=NULL; + const char *user="nobody"; + int retval; + + retval = pam_start("tst-pam_dispatch1", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test1: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_authenticate(pamh, 0); + if (retval != PAM_PERM_DENIED) + { + if (debug) + fprintf (stderr, "test1: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end(pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test1: pam_end returned %d\n", retval); + return 1; + } + return 0; +} + + +int main(int argc, char *argv[]) +{ + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + if (test1 ()) + return 1; + + return 0; +} diff --git a/xtests/tst-pam_dispatch1.pamd b/xtests/tst-pam_dispatch1.pamd new file mode 100644 index 0000000..9bfc87c --- /dev/null +++ b/xtests/tst-pam_dispatch1.pamd @@ -0,0 +1,3 @@ +# https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=196859 +# This stack should not return PAM_IGNORE to the application: +auth [default=bad] pam_debug.so auth=ignore diff --git a/xtests/tst-pam_dispatch2.c b/xtests/tst-pam_dispatch2.c new file mode 100644 index 0000000..bf3f634 --- /dev/null +++ b/xtests/tst-pam_dispatch2.c @@ -0,0 +1,96 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + +static int debug = 0; + +/* + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=196859 + + This stack should not return PAM_IGNORE to the application: + auth [default=die] pam_debug.so auth=ignore +*/ +static int +test2 (void) +{ + pam_handle_t *pamh=NULL; + const char *user="nobody"; + int retval; + + retval = pam_start("tst-pam_dispatch2", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test2: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_authenticate(pamh, 0); + if (retval != PAM_PERM_DENIED) + { + if (debug) + fprintf (stderr, "test2: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end(pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test2: pam_end returned %d\n", retval); + return 1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + if (test2 ()) + return 1; + + return 0; +} diff --git a/xtests/tst-pam_dispatch2.pamd b/xtests/tst-pam_dispatch2.pamd new file mode 100644 index 0000000..79f5260 --- /dev/null +++ b/xtests/tst-pam_dispatch2.pamd @@ -0,0 +1,3 @@ +# https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=196859 +# This stack should not return PAM_IGNORE to the application: +auth [default=die] pam_debug.so auth=ignore diff --git a/xtests/tst-pam_dispatch3.c b/xtests/tst-pam_dispatch3.c new file mode 100644 index 0000000..bbe9ed2 --- /dev/null +++ b/xtests/tst-pam_dispatch3.c @@ -0,0 +1,85 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + + +/* Check that errors of optional modules are ignored and that + required modules after a sufficient one are not executed. */ + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="nobody"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_dispatch3", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test3: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_authenticate(pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test3: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end(pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test3: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_dispatch3.pamd b/xtests/tst-pam_dispatch3.pamd new file mode 100644 index 0000000..7f290ab --- /dev/null +++ b/xtests/tst-pam_dispatch3.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth optional pam_debug.so auth=auth_err +auth sufficient pam_debug.so auth=success +auth required pam_debug.so auth=perm_denied +account required pam_debug.so acct=acct_expired diff --git a/xtests/tst-pam_dispatch4.c b/xtests/tst-pam_dispatch4.c new file mode 100644 index 0000000..00ee457 --- /dev/null +++ b/xtests/tst-pam_dispatch4.c @@ -0,0 +1,92 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + + +/* Check that jumps are processed correctly. */ + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="nobody"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_dispatch4", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_dispatch4: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_dispatch4: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_acct_mgmt (pamh, 0); + if (retval == PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_dispatch4: pam_acct_mgmt returned %d\n", retval); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "test4: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_dispatch4.pamd b/xtests/tst-pam_dispatch4.pamd new file mode 100644 index 0000000..ac995ad --- /dev/null +++ b/xtests/tst-pam_dispatch4.pamd @@ -0,0 +1,8 @@ +#%PAM-1.0 +# We jump to end of the stack with previous pam_permit.so, should pass +auth required pam_permit.so +auth [success=1 default=ignore] pam_debug.so auth=success +auth required pam_deny.so +# We jump to end of the stack without any module in OK state, should fail +account [success=1 default=ignore] pam_debug.so account=success +account required pam_deny.so diff --git a/xtests/tst-pam_dispatch5.c b/xtests/tst-pam_dispatch5.c new file mode 100644 index 0000000..576dbd4 --- /dev/null +++ b/xtests/tst-pam_dispatch5.c @@ -0,0 +1,84 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + + +/* jump after the end of the stack and make sure we don't seg.fault. */ + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="nobody"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_dispatch5", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_dispatch5: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_PERM_DENIED) + { + if (debug) + fprintf (stderr, "pam_dispatch5: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_dispatch5: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_dispatch5.pamd b/xtests/tst-pam_dispatch5.pamd new file mode 100644 index 0000000..ea781f7 --- /dev/null +++ b/xtests/tst-pam_dispatch5.pamd @@ -0,0 +1,4 @@ +#%PAM-1.0 +# Jump after the end of the stack +auth [success=2 default=bad] pam_permit.so +auth required pam_deny.so diff --git a/xtests/tst-pam_group1.c b/xtests/tst-pam_group1.c new file mode 100644 index 0000000..30c121d --- /dev/null +++ b/xtests/tst-pam_group1.c @@ -0,0 +1,207 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + test case: + + Check the following line in group.conf: + + tst-pam_group1;*;tstpamgrp;Al0000-2400;tstpamgrpg + + + pam_group should add group tstpamgrpg to user tstpamgrp, but not + to tstpamgrp2. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define GROUP_BLK 10 +#define blk_size(len) (((len-1 + GROUP_BLK)/GROUP_BLK)*GROUP_BLK) + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup ("!!"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + +static int debug = 0; + +static int +run_test (const char *user, gid_t groupid, int needit) +{ + pam_handle_t *pamh = NULL; + int retval; + int no_grps; + + retval = pam_start("tst-pam_group1", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_group1: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_set_item (pamh, PAM_TTY, "/dev/tty1"); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, + "pam_group1: pam_set_item(PAM_TTY) returned %d\n", + retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_group1: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_setcred (pamh, PAM_ESTABLISH_CRED); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_group1: pam_setcred returned %d\n", retval); + return 1; + } + + + no_grps = getgroups(0, NULL); /* find the current number of groups */ + if (no_grps > 0) + { + int i, found; + gid_t *grps = calloc (blk_size (no_grps), sizeof(gid_t)); + + if (getgroups(no_grps, grps) < 0) + { + if (debug) + fprintf (stderr, "pam_group1: getroups returned error: %m\n"); + pam_end (pamh, PAM_SYSTEM_ERR); + return 1; + } + + found = 0; + for (i = 0; i < no_grps; ++i) + { +#if 0 + if (debug) + fprintf (stderr, "gid[%d]=%d\n", i, grps[i]); +#endif + if (grps[i] == groupid) + found = 1; + } + if ((needit && found) || (!needit && !found)) + { + /* everything is ok */ + } + else + { + pam_end (pamh, PAM_SYSTEM_ERR); + if (debug) + fprintf (stderr, + "pam_group1: unexpected result for %s: needit=%d, found=%d\n", + user, needit, found); + return 1; + } + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_group1: pam_end returned %d\n", retval); + return 1; + } + return 0; +} + +int +main(int argc, char *argv[]) +{ + struct group *grp; + gid_t grpid; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + grp = getgrnam ("tstpamgrpg"); + if (grp == NULL) + return 1; + grpid = grp->gr_gid; + + if (run_test ("root", grpid, 0) != 0 || + run_test ("tstpamgrp2", grpid, 0) != 0 || + run_test ("tstpamgrp", grpid, 1) != 0) + return 1; + + return 0; +} diff --git a/xtests/tst-pam_group1.pamd b/xtests/tst-pam_group1.pamd new file mode 100644 index 0000000..e75d0d1 --- /dev/null +++ b/xtests/tst-pam_group1.pamd @@ -0,0 +1,6 @@ +#%PAM-1.0 +auth required pam_group.so +auth required pam_permit.so +account required pam_permit.so +password required pam_permit.so +session required pam_permit.so diff --git a/xtests/tst-pam_group1.sh b/xtests/tst-pam_group1.sh new file mode 100755 index 0000000..44faca9 --- /dev/null +++ b/xtests/tst-pam_group1.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +/usr/sbin/groupadd tstpamgrpg +/usr/sbin/useradd -p '!!' tstpamgrp +/usr/sbin/useradd -p '!!' tstpamgrp2 +./tst-pam_group1 +RET=$? +/usr/sbin/userdel -r tstpamgrp 2> /dev/null +/usr/sbin/userdel -r tstpamgrp2 2> /dev/null +/usr/sbin/groupdel tstpamgrpg 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_limits1.c b/xtests/tst-pam_limits1.c new file mode 100644 index 0000000..ed4982d --- /dev/null +++ b/xtests/tst-pam_limits1.c @@ -0,0 +1,154 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + test case: + + Check the following line in limits.conf: + * soft nice 19 + * hard nice -20 + + getrlimit should return soft=1 and hard=40. +*/ + +#include + +#include +#include +#include +#include +#include + +#include + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup ("!!"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh = NULL; + const char *user="tstpamlimits"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + +#ifdef RLIMIT_NICE + retval = pam_start("tst-pam_limits1", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_limits1: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_set_item (pamh, PAM_TTY, "/dev/tty1"); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, + "pam_limits1: pam_set_item(PAM_TTY) returned %d\n", + retval); + return 1; + } + + retval = pam_open_session (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_limits1: pam_open_session returned %d\n", + retval); + return 1; + } + + struct rlimit rlim; + + getrlimit (RLIMIT_NICE, &rlim); + + if (rlim.rlim_cur != 1 && rlim.rlim_max != 40) + { + if (debug) + fprintf (stderr, "pam_limits1: getrlimit failed, soft=%u, hard=%u\n", + (unsigned int) rlim.rlim_cur, (unsigned int) rlim.rlim_max); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_limits1: pam_end returned %d\n", retval); + return 1; + } + return 0; +#else + if (debug) + fprintf (stderr, "pam_limits1: RLIMIT_NICE does not exist)\n"); + + return 77; +#endif +} diff --git a/xtests/tst-pam_limits1.pamd b/xtests/tst-pam_limits1.pamd new file mode 100644 index 0000000..7b1771c --- /dev/null +++ b/xtests/tst-pam_limits1.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_permit.so +account required pam_permit.so +password required pam_permit.so +session required pam_limits.so diff --git a/xtests/tst-pam_limits1.sh b/xtests/tst-pam_limits1.sh new file mode 100755 index 0000000..32c021d --- /dev/null +++ b/xtests/tst-pam_limits1.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +/usr/sbin/useradd -p '!!' tstpamlimits +./tst-pam_limits1 +RET=$? +/usr/sbin/userdel -r tstpamlimits 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_motd.c b/xtests/tst-pam_motd.c new file mode 100644 index 0000000..2cc006a --- /dev/null +++ b/xtests/tst-pam_motd.c @@ -0,0 +1,67 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +static struct pam_conv conv = { + misc_conv, + NULL +}; + +int main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + char *tst_arg = NULL; + int retval; + + if (argc > 1) + tst_arg = argv[1]; + + retval = pam_start(tst_arg, NULL, &conv, &pamh); + + retval = pam_open_session(pamh, 0); + + retval = pam_close_session(pamh, 0); + + if (pam_end(pamh,retval) != PAM_SUCCESS) { /* close Linux-PAM */ + pamh = NULL; + exit(1); + } + + return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success */ +} diff --git a/xtests/tst-pam_motd.sh b/xtests/tst-pam_motd.sh new file mode 100755 index 0000000..9080128 --- /dev/null +++ b/xtests/tst-pam_motd.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +./tst-pam_motd1.sh +./tst-pam_motd2.sh +./tst-pam_motd3.sh +./tst-pam_motd4.sh diff --git a/xtests/tst-pam_motd1.pamd b/xtests/tst-pam_motd1.pamd new file mode 100644 index 0000000..ddea82c --- /dev/null +++ b/xtests/tst-pam_motd1.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +session required pam_permit.so +session optional pam_motd.so motd=tst-pam_motd1.d/etc/motd motd_dir=tst-pam_motd1.d/etc/motd.d diff --git a/xtests/tst-pam_motd1.sh b/xtests/tst-pam_motd1.sh new file mode 100755 index 0000000..cc88854 --- /dev/null +++ b/xtests/tst-pam_motd1.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +TST_DIR="tst-pam_motd1.d" + +function tst_cleanup() { + rm -rf "${TST_DIR}" + rm -f tst-pam_motd1.out +} + +mkdir -p ${TST_DIR} +mkdir -p ${TST_DIR}/etc/motd.d + +# Verify the case of single motd and motd.d directory works +echo "motd: /etc/motd" > ${TST_DIR}/etc/motd +echo "motd: /etc/motd.d/test" > ${TST_DIR}/etc/motd.d/test + +./tst-pam_motd tst-pam_motd1 > tst-pam_motd1.out + +RET=$? + +motd_to_show_output=$(cat tst-pam_motd1.out | grep "motd: /etc/motd") +if [ -z "${motd_to_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +motd_dir_to_show_output=$(cat tst-pam_motd1.out | grep "motd: /etc/motd.d/test") +if [ -z "${motd_dir_to_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +tst_cleanup +exit $RET diff --git a/xtests/tst-pam_motd2.pamd b/xtests/tst-pam_motd2.pamd new file mode 100644 index 0000000..8200191 --- /dev/null +++ b/xtests/tst-pam_motd2.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +session required pam_permit.so +session optional pam_motd.so motd=tst-pam_motd2.d/etc/motd:tst-pam_motd2.d/run/motd:tst-pam_motd2.d/usr/lib/motd motd_dir=tst-pam_motd2.d/etc/motd.d:tst-pam_motd2.d/run/motd.d:tst-pam_motd2.d/usr/lib/motd.d diff --git a/xtests/tst-pam_motd2.sh b/xtests/tst-pam_motd2.sh new file mode 100755 index 0000000..d26ea92 --- /dev/null +++ b/xtests/tst-pam_motd2.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +TST_DIR="tst-pam_motd2.d" + +function tst_cleanup() { + rm -rf "${TST_DIR}" + rm -f tst-pam_motd2.out +} + +mkdir -p ${TST_DIR} +mkdir -p ${TST_DIR}/etc/motd.d +mkdir -p ${TST_DIR}/run/motd.d +mkdir -p ${TST_DIR}/usr/lib/motd.d + +echo "motd: /etc/motd" > ${TST_DIR}/etc/motd +echo "motd: /run/motd" > ${TST_DIR}/run/motd +echo "motd: /usr/lib/motd" > ${TST_DIR}/usr/lib/motd + +# Drop a motd file in test directories such that every overriding +# condition (for 3 directories in this case) will be seen. +echo "motd: e0r0u1 in usr/lib - will show" > ${TST_DIR}/usr/lib/motd.d/e0r0u1.motd +echo "motd: e0r1u0 in run - will show" > ${TST_DIR}/run/motd.d/e0r1u0.motd +echo "motd: e0r1u1 in usr/lib - not show" > ${TST_DIR}/usr/lib/motd.d/e0r1u1.motd +echo "motd: e0r1u1 in run - will show" > ${TST_DIR}/run/motd.d/e0r1u1.motd +echo "motd: e1r0u0 in etc - will show" > ${TST_DIR}/etc/motd.d/e1r0u0.motd +echo "motd: e1r0u1 in usr/lib - not show" > ${TST_DIR}/usr/lib/motd.d/e1r0u1.motd +echo "motd: e1r0u1 in etc - will show" > ${TST_DIR}/etc/motd.d/e1r0u1.motd +echo "motd: e1r1u0 in run - not show" > ${TST_DIR}/run/motd.d/e1r1u0.motd +echo "motd: e1r1u0 in etc - will show" > ${TST_DIR}/etc/motd.d/e1r1u0.motd +echo "motd: e1r1u1 in usr/lib - not show" > ${TST_DIR}/usr/lib/motd.d/e1r1u1.motd +echo "motd: e1r1u1 in run - not show" > ${TST_DIR}/run/motd.d/e1r1u1.motd +echo "motd: e1r1u1 in etc - will show" > ${TST_DIR}/etc/motd.d/e1r1u1.motd + +./tst-pam_motd tst-pam_motd2 > tst-pam_motd2.out + +RET=$? + +motd_to_show_output=$(cat tst-pam_motd2.out | grep "motd: /etc/motd") +if [ -z "${motd_to_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +motd_dir_not_show_output=$(cat tst-pam_motd2.out | grep "not show") +if [ -n "${motd_dir_not_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +tst_cleanup +exit $RET diff --git a/xtests/tst-pam_motd3.pamd b/xtests/tst-pam_motd3.pamd new file mode 100644 index 0000000..a8b8cbf --- /dev/null +++ b/xtests/tst-pam_motd3.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +session required pam_permit.so +session optional pam_motd.so motd=tst-pam_motd3.d/etc/motd:tst-pam_motd3.d/run/motd:tst-pam_motd3.d/usr/lib/motd motd_dir=tst-pam_motd3.d/etc/motd.d:tst-pam_motd3.d/run/motd.d:tst-pam_motd3.d/usr/lib/motd.d diff --git a/xtests/tst-pam_motd3.sh b/xtests/tst-pam_motd3.sh new file mode 100755 index 0000000..e18856b --- /dev/null +++ b/xtests/tst-pam_motd3.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +TST_DIR="tst-pam_motd3.d" + +function tst_cleanup() { + rm -rf "${TST_DIR}" + rm -f tst-pam_motd3.out +} + +mkdir -p ${TST_DIR} +mkdir -p ${TST_DIR}/etc/motd.d +mkdir -p ${TST_DIR}/run/motd.d +mkdir -p ${TST_DIR}/usr/lib/motd.d + +# Verify motd is still displayed when not overridden +echo "motd: test-show in run - show" > ${TST_DIR}/run/motd.d/test-show.motd + +# Test overridden by a symlink to a file that isn't /dev/null; symlink target should show +echo "motd: hidden-by-symlink in usr/lib - not show" > ${TST_DIR}/usr/lib/motd.d/hidden-by-symlink.motd +echo "motd: test-from-symlink - show" > ${TST_DIR}/test-from-symlink.motd +ln -sr ${TST_DIR}/test-from-symlink.motd ${TST_DIR}/run/motd.d/hidden-by-symlink.motd + +# Test hidden by a null symlink +echo "motd: hidden-by-null-symlink in run - not show" > ${TST_DIR}/run/motd.d/hidden-by-null-symlink.motd +ln -s /dev/null ${TST_DIR}/etc/motd.d/hidden-by-null-symlink.motd + +./tst-pam_motd tst-pam_motd3 > tst-pam_motd3.out + +RET=$? + +motd_dir_not_show_output=$(cat tst-pam_motd3.out | grep "not show") +if [ -n "${motd_dir_not_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +motd_test_show_output=$(cat tst-pam_motd3.out | grep "test-show.*- show") +if [ -z "${motd_test_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +motd_general_symlink_show_output=$(cat tst-pam_motd3.out | grep "test-from-symlink.*- show") +if [ -z "${motd_general_symlink_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +tst_cleanup +exit $RET diff --git a/xtests/tst-pam_motd4.pamd b/xtests/tst-pam_motd4.pamd new file mode 100644 index 0000000..9dc311a --- /dev/null +++ b/xtests/tst-pam_motd4.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +session required pam_permit.so +session optional pam_motd.so motd=tst-pam_motd4.d/etc/motd diff --git a/xtests/tst-pam_motd4.sh b/xtests/tst-pam_motd4.sh new file mode 100755 index 0000000..6022177 --- /dev/null +++ b/xtests/tst-pam_motd4.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +TST_DIR="tst-pam_motd4.d" + +function tst_cleanup() { + rm -rf "${TST_DIR}" + rm -f tst-pam_motd4.out +} + +mkdir -p ${TST_DIR}/etc + +# Verify the case of single motd with no motd_dir given in tst-pam_motd4.pamd +echo "motd: /etc/motd" > ${TST_DIR}/etc/motd + +./tst-pam_motd tst-pam_motd4 > tst-pam_motd4.out + +RET=$? + +motd_to_show_output=$(cat tst-pam_motd4.out | grep "motd: /etc/motd") +if [ -z "${motd_to_show_output}" ]; +then + tst_cleanup + exit 1 +fi + +tst_cleanup +exit $RET diff --git a/xtests/tst-pam_pwhistory1.c b/xtests/tst-pam_pwhistory1.c new file mode 100644 index 0000000..13adb05 --- /dev/null +++ b/xtests/tst-pam_pwhistory1.c @@ -0,0 +1,167 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Check remember handling + * Change ten times the password + * Try the ten passwords again, should always be rejected + * Try a new password, should succeed + */ + +#include + +#include +#include +#include +#include +#include "pam_inline.h" + +static unsigned int in_test; + +static const char *passwords[] = { + "pamhistory01", "pamhistory02", "pamhistory03", + "pamhistory04", "pamhistory05", "pamhistory06", + "pamhistory07", "pamhistory08", "pamhistory09", + "pamhistory10", + "pamhistory01", "pamhistory02", "pamhistory03", + "pamhistory04", "pamhistory05", "pamhistory06", + "pamhistory07", "pamhistory08", "pamhistory09", + "pamhistory10", + "pamhistory11", + "pamhistory01", "pamhistory02", "pamhistory03", + "pamhistory04", "pamhistory05", "pamhistory06", + "pamhistory07", "pamhistory08", "pamhistory09", + "pamhistory10"}; + +static int debug; + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + if (debug) + fprintf (stderr, "msg_style=%d, msg=%s\n", msgm[0]->msg_style, + msgm[0]->msg); + + if (msgm[0]->msg_style != 1) + return PAM_SUCCESS; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup (passwords[in_test]); + if (debug) + fprintf (stderr, "send password %s\n", reply[count].resp); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="tstpampwhistory"; + int retval; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + for (in_test = 0; in_test < PAM_ARRAY_SIZE(passwords); in_test++) + { + + retval = pam_start("tst-pam_pwhistory1", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pwhistory1-%u: pam_start returned %d\n", + in_test, retval); + return 1; + } + + retval = pam_chauthtok (pamh, 0); + if (in_test < 10 || in_test == 20) + { + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pwhistory1-%u: pam_chauthtok returned %d\n", + in_test, retval); + return 1; + } + } + else if (in_test < 20) + { + if (retval != PAM_MAXTRIES) + { + if (debug) + fprintf (stderr, "pwhistory1-%u: pam_chauthtok returned %d\n", + in_test, retval); + return 1; + } + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pwhistory1: pam_end returned %d\n", retval); + return 1; + } + } + + return 0; +} diff --git a/xtests/tst-pam_pwhistory1.pamd b/xtests/tst-pam_pwhistory1.pamd new file mode 100644 index 0000000..d60db7c --- /dev/null +++ b/xtests/tst-pam_pwhistory1.pamd @@ -0,0 +1,6 @@ +#%PAM-1.0 +auth required pam_permit.so +account required pam_permit.so +password required pam_pwhistory.so remember=10 retry=1 enforce_for_root +password required pam_unix.so use_authtok md5 +session required pam_permit.so diff --git a/xtests/tst-pam_pwhistory1.sh b/xtests/tst-pam_pwhistory1.sh new file mode 100755 index 0000000..0f212e2 --- /dev/null +++ b/xtests/tst-pam_pwhistory1.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +/usr/sbin/useradd tstpampwhistory +./tst-pam_pwhistory1 +RET=$? +/usr/sbin/userdel -r tstpampwhistory 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_shells.c b/xtests/tst-pam_shells.c new file mode 100644 index 0000000..b6ba938 --- /dev/null +++ b/xtests/tst-pam_shells.c @@ -0,0 +1,68 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + test case: + + shells.conf: + /bin/testbash + +*/ + +#include "test_assert.h" + +#include +#include +#include +#include + +static struct pam_conv conv; + +int +main(void) +{ + pam_handle_t *pamh = NULL; + int retval; + + // /bin/testbash is defined in shell definition file(s) + ASSERT_EQ(PAM_SUCCESS, pam_start("tst-pam_shells", "tstpamshells", &conv, &pamh)); + ASSERT_EQ(PAM_SUCCESS, retval=pam_authenticate (pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end (pamh,retval)); + + // /bin/testnoshell is not defined in shell definition file(s) + ASSERT_EQ(PAM_SUCCESS, pam_start("tst-pam_shells", "tstnoshell", &conv, &pamh)); + ASSERT_EQ(PAM_AUTH_ERR, retval=pam_authenticate (pamh, 0)); + ASSERT_EQ(PAM_SUCCESS, pam_end (pamh,retval)); + + return 0; +} diff --git a/xtests/tst-pam_shells.pamd b/xtests/tst-pam_shells.pamd new file mode 100644 index 0000000..6ad4f31 --- /dev/null +++ b/xtests/tst-pam_shells.pamd @@ -0,0 +1,2 @@ +#%PAM-1.0 +auth required pam_shells.so diff --git a/xtests/tst-pam_shells.sh b/xtests/tst-pam_shells.sh new file mode 100755 index 0000000..5093f68 --- /dev/null +++ b/xtests/tst-pam_shells.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +/usr/sbin/groupadd tstpamshells1 +/usr/sbin/useradd -s /bin/testbash -G tstpamshells1 -p '!!' tstpamshells +/usr/sbin/useradd -s /bin/testnoshell -G tstpamshells1 -p '!!' tstnoshell +./tst-pam_shells +RET=$? +/usr/sbin/userdel -r tstpamshells 2> /dev/null +/usr/sbin/userdel -r tstnoshell 2> /dev/null +/usr/sbin/groupdel tstpamshells1 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_substack1.pamd b/xtests/tst-pam_substack1.pamd new file mode 100644 index 0000000..8dfe1b8 --- /dev/null +++ b/xtests/tst-pam_substack1.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +# Even if the substack succeeds with sufficient +# the whole stack should fail. +auth substack tst-pam_substack1a +auth required pam_debug.so auth=auth_err diff --git a/xtests/tst-pam_substack1.sh b/xtests/tst-pam_substack1.sh new file mode 100755 index 0000000..f1b72a7 --- /dev/null +++ b/xtests/tst-pam_substack1.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec ./tst-pam_authfail tst-pam_substack1 diff --git a/xtests/tst-pam_substack1a.pamd b/xtests/tst-pam_substack1a.pamd new file mode 100644 index 0000000..51c8c8f --- /dev/null +++ b/xtests/tst-pam_substack1a.pamd @@ -0,0 +1,2 @@ +#%PAM-1.0 +auth sufficient pam_debug.so auth=success diff --git a/xtests/tst-pam_substack2.pamd b/xtests/tst-pam_substack2.pamd new file mode 100644 index 0000000..618e298 --- /dev/null +++ b/xtests/tst-pam_substack2.pamd @@ -0,0 +1,6 @@ +#%PAM-1.0 +# Even if the substack fails with requisite +# the whole stack should succeed. +auth substack tst-pam_substack2a +auth [success=reset] pam_permit.so +auth required pam_debug.so auth=success diff --git a/xtests/tst-pam_substack2.sh b/xtests/tst-pam_substack2.sh new file mode 100755 index 0000000..3804fa7 --- /dev/null +++ b/xtests/tst-pam_substack2.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec ./tst-pam_authsucceed tst-pam_substack2 diff --git a/xtests/tst-pam_substack2a.pamd b/xtests/tst-pam_substack2a.pamd new file mode 100644 index 0000000..db85354 --- /dev/null +++ b/xtests/tst-pam_substack2a.pamd @@ -0,0 +1,2 @@ +#%PAM-1.0 +auth requisite pam_debug.so auth=auth_err diff --git a/xtests/tst-pam_substack3.pamd b/xtests/tst-pam_substack3.pamd new file mode 100644 index 0000000..4fc6016 --- /dev/null +++ b/xtests/tst-pam_substack3.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +# Reset in the substack resets to state as of it was +# in the beginning of substack evaluation +auth required pam_permit.so +auth substack tst-pam_substack3a diff --git a/xtests/tst-pam_substack3.sh b/xtests/tst-pam_substack3.sh new file mode 100755 index 0000000..aa48e8e --- /dev/null +++ b/xtests/tst-pam_substack3.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec ./tst-pam_authsucceed tst-pam_substack3 diff --git a/xtests/tst-pam_substack3a.pamd b/xtests/tst-pam_substack3a.pamd new file mode 100644 index 0000000..a2ae915 --- /dev/null +++ b/xtests/tst-pam_substack3a.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +auth required pam_debug.so auth=auth_err +auth [success=reset] pam_permit.so diff --git a/xtests/tst-pam_substack4.pamd b/xtests/tst-pam_substack4.pamd new file mode 100644 index 0000000..f0017c7 --- /dev/null +++ b/xtests/tst-pam_substack4.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +# Substack is counted as one module in jumps +auth [success=1] pam_permit.so +auth substack tst-pam_substack4a +auth required pam_permit.so diff --git a/xtests/tst-pam_substack4.sh b/xtests/tst-pam_substack4.sh new file mode 100755 index 0000000..958a07a --- /dev/null +++ b/xtests/tst-pam_substack4.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec ./tst-pam_authsucceed tst-pam_substack4 diff --git a/xtests/tst-pam_substack4a.pamd b/xtests/tst-pam_substack4a.pamd new file mode 100644 index 0000000..3b91c1b --- /dev/null +++ b/xtests/tst-pam_substack4a.pamd @@ -0,0 +1,4 @@ +#%PAM-1.0 +auth required pam_debug.so auth=auth_err +auth required pam_debug.so auth=auth_err +auth required pam_debug.so auth=auth_err diff --git a/xtests/tst-pam_substack5.pamd b/xtests/tst-pam_substack5.pamd new file mode 100644 index 0000000..04f07ae --- /dev/null +++ b/xtests/tst-pam_substack5.pamd @@ -0,0 +1,4 @@ +#%PAM-1.0 +# Requisite terminates substack +auth required pam_permit.so +auth substack tst-pam_substack5a diff --git a/xtests/tst-pam_substack5.sh b/xtests/tst-pam_substack5.sh new file mode 100755 index 0000000..7e0da74 --- /dev/null +++ b/xtests/tst-pam_substack5.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec ./tst-pam_authfail tst-pam_substack5 diff --git a/xtests/tst-pam_substack5a.pamd b/xtests/tst-pam_substack5a.pamd new file mode 100644 index 0000000..a6850f4 --- /dev/null +++ b/xtests/tst-pam_substack5a.pamd @@ -0,0 +1,3 @@ +#%PAM-1.0 +auth requisite pam_debug.so auth=auth_err +auth [success=reset] pam_permit.so diff --git a/xtests/tst-pam_succeed_if1.c b/xtests/tst-pam_succeed_if1.c new file mode 100644 index 0000000..78a1c30 --- /dev/null +++ b/xtests/tst-pam_succeed_if1.c @@ -0,0 +1,136 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + test case: + + Check the following line in PAM config file: + + auth required pam_succeed_if.so user in tstpamtest:pamtest + + User is pamtest or tstpamtest, both should succeed. +*/ + +#include + +#include +#include +#include +#include + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup ("!!"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + +static int debug = 0; + +static int +test_with_user (const char *user) +{ + pam_handle_t *pamh = NULL; + int retval; + + retval = pam_start("tst-pam_succeed_if1", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_succeed_if1: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_authenticate (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access1: pam_authenticate(%s) returned %d\n", + user, retval); + return 1; + } + + retval = pam_end (pamh, retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_access1: pam_end returned %d\n", retval); + return 1; + } + return 0; +} + +int +main(int argc, char *argv[]) +{ + const char *user1 = "tstpamtest"; + const char *user2 = "pamtest"; + int retval; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = test_with_user (user1); + if (retval == 0) + retval = test_with_user (user2); + + return retval; +} diff --git a/xtests/tst-pam_succeed_if1.pamd b/xtests/tst-pam_succeed_if1.pamd new file mode 100644 index 0000000..f9cbd5a --- /dev/null +++ b/xtests/tst-pam_succeed_if1.pamd @@ -0,0 +1,2 @@ +#%PAM-1.0 +auth required pam_succeed_if.so user in tstpamtest:pamtest diff --git a/xtests/tst-pam_succeed_if1.sh b/xtests/tst-pam_succeed_if1.sh new file mode 100755 index 0000000..58e57b4 --- /dev/null +++ b/xtests/tst-pam_succeed_if1.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +/usr/sbin/useradd -p '!!' tstpamtest +/usr/sbin/useradd -p '!!' pamtest +./tst-pam_succeed_if1 +RET=$? +/usr/sbin/userdel -r tstpamtest 2> /dev/null +/usr/sbin/userdel -r pamtest 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_time1.c b/xtests/tst-pam_time1.c new file mode 100644 index 0000000..ff773f1 --- /dev/null +++ b/xtests/tst-pam_time1.c @@ -0,0 +1,111 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + test case: + + Check the following lines in time.conf: + *;*;you|me;!Al0000-2400 + *;*;x|y;!Al0000-2400 + + User 'x' should not be able to login. +*/ + +#include + +#include +#include +#include +#include +#include "pam_inline.h" + + +struct test_t { + const char *user; + int retval; +}; + +static struct test_t tests[] = { + {"xy", 0}, + {"yx", 0}, + {"you",6}, + {"me", 6}, + {"x", 6}, + {"y", 6}, +}; + +static struct pam_conv conv = { + NULL, NULL +}; + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh = NULL; + int retval; + int debug = 0; + unsigned int i; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + for (i = 0; i < PAM_ARRAY_SIZE(tests); i++) + { + retval = pam_start("tst-pam_time1", tests[i].user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_time1: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_acct_mgmt (pamh, 0); + if (retval != tests[i].retval) + { + if (debug) + fprintf (stderr, + "pam_time1: pam_acct_mgmt(%s) returned wrong value, %d, expected %d\n", + tests[i].user, retval, tests[i].retval); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_time1: pam_end returned %d\n", retval); + return 1; + } + } + return 0; +} diff --git a/xtests/tst-pam_time1.pamd b/xtests/tst-pam_time1.pamd new file mode 100644 index 0000000..c4cd6c7 --- /dev/null +++ b/xtests/tst-pam_time1.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_permit.so +account required pam_time.so +password required pam_permit.so +session required pam_permit.so diff --git a/xtests/tst-pam_unix1.c b/xtests/tst-pam_unix1.c new file mode 100644 index 0000000..665c67d --- /dev/null +++ b/xtests/tst-pam_unix1.c @@ -0,0 +1,120 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Test case: '!!' as password should not allow login + */ + +#include + +#include +#include +#include +#include + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup ("!!"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + + +/* Check that errors of optional modules are ignored and that + required modules after a sufficient one are not executed. */ + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="tstpamunix"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_unix1", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_unix1: pam_start returned %d\n", retval); + return 1; + } + + /* !! as password should not allow login */ + retval = pam_authenticate (pamh, 0); + if (retval != PAM_AUTH_ERR) + { + if (debug) + fprintf (stderr, "pam_unix1: pam_authenticate returned %d\n", retval); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "pam_unix1: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_unix1.pamd b/xtests/tst-pam_unix1.pamd new file mode 100644 index 0000000..6cd67b6 --- /dev/null +++ b/xtests/tst-pam_unix1.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_unix.so +account required pam_unix.so +password required pam_unix.so +session required pam_unix.so diff --git a/xtests/tst-pam_unix1.sh b/xtests/tst-pam_unix1.sh new file mode 100755 index 0000000..72deac0 --- /dev/null +++ b/xtests/tst-pam_unix1.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +/usr/sbin/useradd -p '!!' tstpamunix +./tst-pam_unix1 +RET=$? +/usr/sbin/userdel -r tstpamunix 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_unix2.c b/xtests/tst-pam_unix2.c new file mode 100644 index 0000000..b50fb6b --- /dev/null +++ b/xtests/tst-pam_unix2.c @@ -0,0 +1,152 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Check crypt handling + * First use exact password, 8 characters (13 characters crypt) + * Second use longer password, 9 characters + */ + +#include + +#include +#include +#include +#include + +static int in_test; + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + /* first call get a password, second one a too long one */ + if (in_test == 1) + reply[count].resp = strdup ("pamunix0"); + else if (in_test == 2) + reply[count].resp = strdup ("pamunix01"); + else + reply[count].resp = strdup ("pamunix1"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + + +/* Check that errors of optional modules are ignored and that + required modules after a sufficient one are not executed. */ + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="tstpamunix"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_unix2", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "unix2: pam_start returned %d\n", retval); + return 1; + } + + /* Try one, first input is correct, second is NULL */ + in_test = 1; + retval = pam_authenticate (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "unix2-1: pam_authenticate returned %d\n", retval); + return 1; + } + + /* Try two, second input is too long */ + in_test = 2; + retval = pam_authenticate (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "unix2-2: pam_authenticate returned %d\n", retval); + return 1; + } + + /* Third try, third input is wrong */ + in_test = 3; + retval = pam_authenticate (pamh, 0); + if (retval != PAM_AUTH_ERR) + { + if (debug) + fprintf (stderr, "unix2-3: pam_authenticate returned %d\n", retval); + return 1; + } + + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "unix2: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_unix2.pamd b/xtests/tst-pam_unix2.pamd new file mode 100644 index 0000000..6cd67b6 --- /dev/null +++ b/xtests/tst-pam_unix2.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_unix.so +account required pam_unix.so +password required pam_unix.so +session required pam_unix.so diff --git a/xtests/tst-pam_unix2.sh b/xtests/tst-pam_unix2.sh new file mode 100755 index 0000000..c04d6e6 --- /dev/null +++ b/xtests/tst-pam_unix2.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# pamunix0 = 0aXKZztA.d1KY +/usr/sbin/useradd -p 0aXKZztA.d1KY tstpamunix +./tst-pam_unix2 +RET=$? +/usr/sbin/userdel -r tstpamunix 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_unix3.c b/xtests/tst-pam_unix3.c new file mode 100644 index 0000000..18ecbeb --- /dev/null +++ b/xtests/tst-pam_unix3.c @@ -0,0 +1,153 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Check bigcrypt handling + * First use exact password, 9 characters (24 characters crypt) + * Second use shorter password, 8 characters + * Third use wrong password, 9 characters + */ + +#include + +#include +#include +#include +#include + +static int in_test; + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Each prompt elicits the same response. */ + for (count = 0; count < num_msg; ++count) + { + reply[count].resp_retcode = 0; + /* first call get a password, second one a too short one */ + if (in_test == 1) + reply[count].resp = strdup ("pamunix01"); + else if (in_test == 2) + reply[count].resp = strdup ("pamunix0"); + else + reply[count].resp = strdup ("pamunix11"); + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + + +/* Check that errors of optional modules are ignored and that + required modules after a sufficient one are not executed. */ + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="tstpamunix"; + int retval; + int debug = 0; + + if (argc > 1 && strcmp (argv[1], "-d") == 0) + debug = 1; + + retval = pam_start("tst-pam_unix3", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "unix3: pam_start returned %d\n", retval); + return 1; + } + + /* Try one, first input is correct, second is NULL */ + in_test = 1; + retval = pam_authenticate (pamh, 0); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "unix3-1: pam_authenticate returned %d\n", retval); + return 1; + } + + /* Try two, second input is too short */ + in_test = 2; + retval = pam_authenticate (pamh, 0); + if (retval != PAM_AUTH_ERR) + { + if (debug) + fprintf (stderr, "unix3-2: pam_authenticate returned %d\n", retval); + return 1; + } + + /* Third try, third input is wrong */ + in_test = 3; + retval = pam_authenticate (pamh, 0); + if (retval != PAM_AUTH_ERR) + { + if (debug) + fprintf (stderr, "unix3-3: pam_authenticate returned %d\n", retval); + return 1; + } + + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "unix3: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_unix3.pamd b/xtests/tst-pam_unix3.pamd new file mode 100644 index 0000000..6cd67b6 --- /dev/null +++ b/xtests/tst-pam_unix3.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_unix.so +account required pam_unix.so +password required pam_unix.so +session required pam_unix.so diff --git a/xtests/tst-pam_unix3.sh b/xtests/tst-pam_unix3.sh new file mode 100755 index 0000000..b52db2b --- /dev/null +++ b/xtests/tst-pam_unix3.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# pamunix01 = 0aXKZztA.d1KYIuFXArmd2jU +/usr/sbin/useradd -p 0aXKZztA.d1KYIuFXArmd2jU tstpamunix +./tst-pam_unix3 +RET=$? +/usr/sbin/userdel -r tstpamunix 2> /dev/null +exit $RET diff --git a/xtests/tst-pam_unix4.c b/xtests/tst-pam_unix4.c new file mode 100644 index 0000000..b2abdfe --- /dev/null +++ b/xtests/tst-pam_unix4.c @@ -0,0 +1,158 @@ +/* + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, and the entire permission notice in its entirety, + * including the disclaimer of warranties. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * ALTERNATIVELY, this product may be distributed under the terms of + * the GNU Public License, in which case the provisions of the GPL are + * required INSTEAD OF the above restrictions. (This clause is + * necessary due to a potential bad interaction between the GPL and + * the restrictions contained in a BSD-style copyright.) + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Check password change minimum days handling. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* A conversation function which uses an internally-stored value for + the responses. */ +static int +fake_conv (int num_msg, const struct pam_message **msgm UNUSED, + struct pam_response **response, void *appdata_ptr UNUSED) +{ + struct pam_response *reply; + int count; + static int respnum = 0; + static const char *resps[] = { "pamunix01", "TsTPAM01MAP", "TsTPAM01MAP" }; + + /* Sanity test. */ + if (num_msg <= 0) + return PAM_CONV_ERR; + + /* Allocate memory for the responses. */ + reply = calloc (num_msg, sizeof (struct pam_response)); + if (reply == NULL) + return PAM_CONV_ERR; + + /* Answer with appropriate response from the above array. */ + for (count = 0; count < num_msg; ++count) + { + if (msgm[count]->msg_style == PAM_PROMPT_ECHO_OFF) + { + reply[count].resp_retcode = 0; + reply[count].resp = strdup (resps[respnum % 3]); + ++respnum; + } + } + + /* Set the pointers in the response structure and return. */ + *response = reply; + return PAM_SUCCESS; +} + +static struct pam_conv conv = { + fake_conv, + NULL +}; + + +/* Check that errors of optional modules are ignored and that + required modules after a sufficient one are not executed. */ + +int +main(int argc, char *argv[]) +{ + pam_handle_t *pamh=NULL; + const char *user="tstpamunix"; + int retval; + int debug = 0; + int fail; + struct passwd *pwd; + + if (argc < 2 || (*argv[1] != 'f' && + *argv[1] != 'p')) + { + fprintf (stderr, "Need fail or pass argument.\n"); + return 2; + } + + fail = *argv[1] == 'f'; + + if (argc > 2 && strcmp (argv[2], "-d") == 0) + debug = 1; + + pwd = getpwnam (user); + + if (pwd == NULL) + { + if (debug) + fprintf (stderr, "unix4: Missing tstpamunix user.\n"); + return 2; + } + + /* we must switch the real (not effective) user so the restrictions + are enforced */ + retval = setreuid (pwd->pw_uid, -1); + if (retval) + { + if (debug) + fprintf (stderr, "unix4: setreuid returned %d\n", retval); + return 1; + } + + retval = pam_start("tst-pam_unix4", user, &conv, &pamh); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "unix4: pam_start returned %d\n", retval); + return 1; + } + + retval = pam_chauthtok (pamh, 0); + if ((!fail && retval != PAM_SUCCESS) || (fail && retval == PAM_SUCCESS)) + { + if (debug) + fprintf (stderr, "unix4-1: pam_chauthtok returned %d\n", retval); + return 1; + } + + retval = pam_end (pamh,retval); + if (retval != PAM_SUCCESS) + { + if (debug) + fprintf (stderr, "unix4: pam_end returned %d\n", retval); + return 1; + } + return 0; +} diff --git a/xtests/tst-pam_unix4.pamd b/xtests/tst-pam_unix4.pamd new file mode 100644 index 0000000..1affa8e --- /dev/null +++ b/xtests/tst-pam_unix4.pamd @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth required pam_unix.so +account required pam_unix.so +password required pam_unix.so debug +session required pam_unix.so diff --git a/xtests/tst-pam_unix4.sh b/xtests/tst-pam_unix4.sh new file mode 100755 index 0000000..e7976fd --- /dev/null +++ b/xtests/tst-pam_unix4.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# pamunix01 = 0aXKZztA.d1KYIuFXArmd2jU +/usr/sbin/useradd -p 0aXKZztA.d1KYIuFXArmd2jU tstpamunix +# this run must successfully change the password +./tst-pam_unix4 pass +RET=$? +/usr/sbin/usermod -p 0aXKZztA.d1KYIuFXArmd2jU tstpamunix +/usr/bin/chage -m 10000 tstpamunix +# this run must fail to change the password +./tst-pam_unix4 fail || RET=$? + +/usr/sbin/userdel -r tstpamunix 2> /dev/null +exit $RET