1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-17 10:26:15 +00:00

Clean up this module very extensively. Fix the logging, the coding

standards and the option handling. This module is now much more easy
to maintain as a part of the FreeBSD tree.
This commit is contained in:
Mark Murray 2001-08-10 19:24:34 +00:00
parent 530ebf8e0a
commit 3938427761
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=81477
12 changed files with 1101 additions and 1410 deletions

View File

@ -1,195 +0,0 @@
pam_krb5:
$FreeBSD$
Copyright (c) Frank Cusack, 1999-2000.
fcusack@fcusack.com
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.
---------------------------------------------------------------------------
This software may contain code from Naomaru Itoi:
PAM-kerberos5 module Copyright notice.
Naomaru Itoi <itoi@eecs.umich.edu>, June 24, 1997.
----------------------------------------------------------------------------
COPYRIGHT (c) 1997
THE REGENTS OF THE UNIVERSITY OF MICHIGAN
ALL RIGHTS RESERVED
PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS AND REDISTRIBUTE
THIS SOFTWARE AND SUCH DERIVATIVE WORKS FOR ANY PURPOSE, SO LONG AS THE NAME
OF THE UNIVERSITY OF MICHIGAN IS NOT USED IN ANY ADVERTISING OR PUBLICITY
PERTAINING TO THE USE OR DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC,
WRITTEN PRIOR AUTHORIZATION. IF THE ABOVE COPYRIGHT NOTICE OR ANY OTHER
IDENTIFICATION OF THE UNIVERSITY OF MICHIGAN IS INCLUDED IN ANY COPY OF ANY
PORTION OF THIS SOFTWARE, THEN THE DISCLAIMER BELOW MUST ALSO BE INCLUDED.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE UNIVERSITY OF
MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE
UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABITILY AND FITNESS FOR A
PARTICULAR PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE
LIABLE FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN
CONNECTION WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
PAM-kerberos5 module is written based on PAM-kerberos4 module
by Derrick J. Brashear and kerberos5-1.0pl1 by M.I.T. kerberos team.
Permission to use, copy, modify, distribute this software is hereby
granted, as long as it is granted by Derrick J. Brashear and
M.I.T. kerberos team. Followings are their copyright information.
----------------------------------------------------------------------------
This software may contain code from Derrick J. Brashear:
Copyright (c) Derrick J. Brashear, 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.
----------------------------------------------------------------------------
This software may contain code from MIT Kerberos 5:
Copyright Notice and Legal Administrivia
----------------------------------------
Copyright (C) 1996 by the Massachusetts Institute of Technology.
All rights reserved.
Export of this software from the United States of America may require
a specific license from the United States Government. It is the
responsibility of any person or organization contemplating export to
obtain such a license before exporting.
WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
distribute this software and its documentation for any purpose and
without fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright notice and
this permission notice appear in supporting documentation, and that
the name of M.I.T. not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission. M.I.T. makes no representations about the suitability of
this software for any purpose. It is provided "as is" without express
or implied warranty.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Individual source code files are copyright MIT, Cygnus Support,
OpenVision, Oracle, Sun Soft, and others.
Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira,
and Zephyr are trademarks of the Massachusetts Institute of Technology
(MIT). No commercial use of these trademarks may be made without
prior written permission of MIT.
"Commercial use" means use of a name in a product or other for-profit
manner. It does NOT prevent a commercial firm from referring to the
MIT trademarks in order to convey information (although in doing so,
recognition of their trademark status should be given).
The following copyright and permission notice applies to the
OpenVision Kerberos Administration system located in kadmin/create,
kadmin/dbutil, kadmin/passwd, kadmin/server, lib/kadm5, and portions
of lib/rpc:
Copyright, OpenVision Technologies, Inc., 1996, All Rights Reserved
WARNING: Retrieving the OpenVision Kerberos Administration system
source code, as described below, indicates your acceptance of the
following terms. If you do not agree to the following terms, do not
retrieve the OpenVision Kerberos administration system.
You may freely use and distribute the Source Code and Object Code
compiled from it, with or without modification, but this Source
Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY,
INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR
FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER
EXPRESS OR IMPLIED. IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY
FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING,
WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE
CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY
OTHER REASON.
OpenVision retains all copyrights in the donated Source Code. OpenVision
also retains copyright to derivative works of the Source Code, whether
created by OpenVision or by a third party. The OpenVision copyright
notice must be preserved if derivative works are made based on the
donated Source Code.
OpenVision Technologies, Inc. has donated this Kerberos
Administration system to MIT for inclusion in the standard
Kerberos 5 distribution. This donation underscores our
commitment to continuing Kerberos technology development
and our gratitude for the valuable work which has been
performed by MIT and the Kerberos community.

View File

@ -26,8 +26,7 @@
LIB= pam_krb5
SHLIB_NAME= pam_krb5.so
SRCS= pam_krb5_auth.c pam_krb5_pass.c pam_krb5_acct.c \
pam_krb5_sess.c support.c compat_heimdal.c
SRCS= pam_krb5.c
DPADD= ${LIBKRB5} ${LIBGSSAPI} ${LIBASN1} ${LIBCRYPTO} ${LIBCRYPT} \
${LIBCOM_ERR} ${LIBROKEN}
LDADD= -lkrb5 -lgssapi -lasn1 -lcrypto -lcrypt -lcom_err \

View File

@ -1,72 +0,0 @@
$FreeBSD$
This is the README for pam_krb5, a PAM module which support Kerberos 5
authentication.
This software is Copyright (c) 1999-2000 Frank Cusack.
All Rights Reserved.
See the COPYRIGHT file, included with this distribution, for copyright
and redistribution information.
Author:
Frank Cusack
<fcusack@fcusack.com>
I. Kerberos notes
This PAM module requires the MIT 1.1+ release of Kerberos, or the Cygnus
CNS distribution. It has not been tested against heimdal or any other
Kerberos distributions.
Unlike other PAM Kerberos 5 modules out there, this one does not
use any private Kerberos interfaces. Thus, you need only the
header files and libraries that are part of the Kerberos distribution.
II. OS notes
This software has been tested against Solaris 2.6. It should compile
against Linux (distributions?) with minimal (if any) changes. Reports
of OS [in]compatibilities are welcomed.
dtlogin on Solaris doesn't support xrealm logins (probably a good thing).
III. PAM notes/open issues
auth module:
When is pam_sm_setcred() ever called with flags other than PAM_ESTABLISH_CRED?
It would be fairly easy to support PAM_DELETE_CRED.
acct module:
I believe this to be complete.
session module:
This is complete (both functions just return success).
passwd module:
When is pam_sm_chauthtok() ever called with flags other than
PAM_UPDATE_AUTHTOK?
IV. Usage
Simply change /etc/pam.conf to include this module. Make sure to include
the acct category whenever you use the auth category, or .k5login will
not get checked.
You probably want to make this module "sufficient", before your unix
(or other) module(s).
V. Acknowledgements
Thanks to Naomaru Itoi <itoi@eecs.umich.edu>,
Curtis King <curtis.king@cul.ca>, and Derrick Brashear <shadow@dementia.org>,
all of whom have written and made available Kerberos 4/5 modules.
Although no code in this module is directly from these author's modules,
(except the get_user_info() routine in support.c; derived from whichever
of these authors originally wrote the first module the other 2 copied from),
it was extremely helpful to look over their code which aided in my design.

View File

@ -1,16 +0,0 @@
$FreeBSD$
Things for 1.1, in no particular order:
Check against Solaris 7, Solaris 8 beta. Check SEAM compatibility.
Check against Linux (Redhat, others?).
Check against HPUX.
Fix PAM flags checking.
Add more debugging for successful calls.
Move "entry" debugging up.
Check bounds on str* calls. [paranoia]
Get defaults from krb5.conf?
** Allow no-xrealm, this module typically used for local login **
** Add notes about runtime text relocation on Solaris **

View File

@ -1,133 +0,0 @@
/*
* compat_heimdal.c
*
* Heimdal compatability layer.
*
* $FreeBSD$
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <krb5.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include "pam_krb5.h"
const char *
compat_princ_component(krb5_context context, krb5_principal princ, int n)
{
return princ->name.name_string.val[n];
}
void
compat_free_data_contents(krb5_context context, krb5_data *data)
{
krb5_xfree(data->data);
}
static krb5_error_code
heimdal_pam_prompter(krb5_context context, void *data, const char *name,
const char *banner, int num_prompts, krb5_prompt prompts[])
{
int pam_prompts = num_prompts;
int pamret, i;
struct pam_message *msg;
struct pam_response *resp = NULL;
struct pam_conv *conv;
pam_handle_t *pamh = (pam_handle_t *) data;
if ((pamret = pam_get_item(pamh, PAM_CONV, (const void **) &conv)) != 0)
return KRB5KRB_ERR_GENERIC;
if (banner)
pam_prompts++;
msg = calloc(sizeof(struct pam_message) * pam_prompts, 1);
if (!msg)
return ENOMEM;
/* Now use pam_prompts as an index */
pam_prompts = 0;
if (banner) {
msg[pam_prompts].msg = malloc(strlen(banner) + 1);
if (!msg[pam_prompts].msg)
goto cleanup;
strcpy((char *) msg[pam_prompts].msg, banner);
msg[pam_prompts].msg_style = PAM_TEXT_INFO;
pam_prompts++;
}
for (i = 0; i < num_prompts; i++) {
msg[pam_prompts].msg = malloc(strlen(prompts[i].prompt) + 3);
if (!msg[pam_prompts].msg)
goto cleanup;
sprintf((char *) msg[pam_prompts].msg, "%s: ", prompts[i].prompt);
msg[pam_prompts].msg_style = prompts[i].hidden ? PAM_PROMPT_ECHO_OFF
: PAM_PROMPT_ECHO_ON;
pam_prompts++;
}
if ((pamret = conv->conv(pam_prompts, (const struct pam_message **) &msg,
&resp, conv->appdata_ptr)) != 0)
goto cleanup;
if (!resp)
goto cleanup;
/* Reuse pam_prompts as a starting index */
pam_prompts = 0;
if (banner)
pam_prompts++;
for (i = 0; i < num_prompts; i++, pam_prompts++) {
register int len;
if (!resp[pam_prompts].resp) {
pamret = PAM_AUTH_ERR;
goto cleanup;
}
len = strlen(resp[pam_prompts].resp); /* Help out the compiler */
if (len > prompts[i].reply->length) {
pamret = PAM_AUTH_ERR;
goto cleanup;
}
memcpy(prompts[i].reply->data, resp[pam_prompts].resp, len);
prompts[i].reply->length = len;
}
cleanup:
/* pam_prompts is correct at this point */
for (i = 0; i < pam_prompts; i++) {
if (msg[i].msg)
free((char *) msg[i].msg);
}
free(msg);
if (resp) {
for (i = 0; i < pam_prompts; i++) {
/*
* Note that PAM is underspecified wrt free()'ing resp[i].resp.
* It's not clear if I should free it, or if the application
* has to. Therefore most (all?) apps won't free() it, and I
* can't either, as I am not sure it was malloc()'d. All PAM
* implementations I've seen leak memory here. Not so bad, IFF
* you fork/exec for each PAM authentication (as is typical).
*/
#if 0
if (resp[i].resp)
free(resp[i].resp);
#endif /* 0 */
}
/* This does not lose resp[i].resp if the application saved a copy. */
free(resp);
}
return (pamret ? KRB5KRB_ERR_GENERIC : 0);
}
krb5_prompter_fct pam_prompter = heimdal_pam_prompter;

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +0,0 @@
/*
* pam_krb5.h
*
* $Id: pam_krb5.h,v 1.5 1999/01/19 23:43:10 fcusack Exp $
* $FreeBSD$
*/
int get_user_info(pam_handle_t *, char *, int, char **);
int verify_krb_v5_tgt(krb5_context, krb5_ccache, char *, int);
void cleanup_cache(pam_handle_t *, void *, int);
krb5_prompter_fct pam_prompter;
const char *compat_princ_component(krb5_context, krb5_principal, int);
void compat_free_data_contents(krb5_context, krb5_data *);
#ifndef ENCTYPE_DES_CBC_MD5
#define ENCTYPE_DES_CBC_MD5 ETYPE_DES_CBC_MD5
#endif

View File

@ -1,83 +0,0 @@
/*
* pam_krb5_acct.c
*
* PAM account management functions for pam_krb5
*
* $FreeBSD$
*/
static const char rcsid[] = "$Id: pam_krb5_acct.c,v 1.3 1999/01/19 21:26:44 fcusack Exp $";
#include <syslog.h> /* syslog */
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <krb5.h>
#include <com_err.h>
#include "pam_krb5.h"
/* A useful logging macro */
#define DLOG(error_func, error_msg) \
if (debug) \
syslog(LOG_DEBUG, "pam_krb5: pam_sm_acct_mgmt(%s %s): %s: %s", \
service, name, error_func, error_msg)
/* Check authorization of user */
int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
krb5_error_code krbret;
krb5_context pam_context;
krb5_ccache ccache;
krb5_principal princ;
char *service, *name;
int debug = 0;
int i, pamret;
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "debug") == 0)
debug = 1;
}
/* Get username */
if (pam_get_item(pamh, PAM_USER, (const void **) &name)) {
return PAM_PERM_DENIED;;
}
/* Get service name */
(void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
if (!service)
service = "unknown";
DLOG("entry", "");
if (pam_get_data(pamh, "ccache", (const void **) &ccache)) {
/* User did not use krb5 to login */
DLOG("ccache", "not found");
return PAM_SUCCESS;
}
if ((krbret = krb5_init_context(&pam_context)) != 0) {
DLOG("krb5_init_context()", error_message(krbret));
return PAM_PERM_DENIED;;
}
if ((krbret = krb5_cc_get_principal(pam_context, ccache, &princ)) != 0) {
DLOG("krb5_cc_get_principal()", error_message(krbret));
pamret = PAM_PERM_DENIED;;
goto cleanup;
}
if (krb5_kuserok(pam_context, princ, name))
pamret = PAM_SUCCESS;
else
pamret = PAM_PERM_DENIED;
krb5_free_principal(pam_context, princ);
cleanup:
krb5_free_context(pam_context);
DLOG("exit", pamret ? "failure" : "success");
return pamret;
}

View File

@ -1,475 +0,0 @@
/*
* pam_krb5_auth.c
*
* PAM authentication management functions for pam_krb5
*
* $FreeBSD$
*/
static const char rcsid[] = "$Id: pam_krb5_auth.c,v 1.18 2000/01/04 08:44:08 fcusack Exp $";
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h> /* PATH_MAX */
#include <pwd.h> /* getpwnam */
#include <stdio.h> /* tmpnam */
#include <stdlib.h> /* malloc */
#include <strings.h> /* strchr */
#include <syslog.h> /* syslog */
#include <unistd.h> /* chown */
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <krb5.h>
#include <com_err.h>
#include "pam_krb5.h"
extern krb5_cc_ops krb5_mcc_ops;
/* A useful logging macro */
#define DLOG(error_func, error_msg) \
if (debug) \
syslog(LOG_DEBUG, "pam_krb5: pam_sm_authenticate(%s %s): %s: %s", \
service, name, error_func, error_msg)
/* Authenticate a user via krb5 */
int
pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
krb5_error_code krbret;
krb5_context pam_context;
krb5_creds creds;
krb5_principal princ;
krb5_ccache ccache, ccache_check;
krb5_get_init_creds_opt opts;
int pamret, i;
const char *name;
char *princ_name = NULL;
char *pass = NULL, *service = NULL;
char *prompt = NULL;
char cache_name[L_tmpnam + 8];
char lname[64]; /* local acct name */
struct passwd *pw;
int debug = 0, try_first_pass = 0, use_first_pass = 0;
int forwardable = 0, reuse_ccache = 0, no_ccache = 0;
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "debug") == 0)
debug = 1;
else if (strcmp(argv[i], "try_first_pass") == 0)
try_first_pass = 1;
else if (strcmp(argv[i], "use_first_pass") == 0)
use_first_pass = 1;
else if (strcmp(argv[i], "forwardable") == 0)
forwardable = 1;
else if (strcmp(argv[i], "reuse_ccache") == 0)
reuse_ccache = 1;
else if (strcmp(argv[i], "no_ccache") == 0)
no_ccache = 1;
}
/* Get username */
if ((pamret = pam_get_user(pamh, &name, "login: ")) != PAM_SUCCESS) {
return PAM_SERVICE_ERR;
}
/* Get service name */
(void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
if (!service)
service = "unknown";
DLOG("entry", "");
if ((krbret = krb5_init_context(&pam_context)) != 0) {
DLOG("krb5_init_context()", error_message(krbret));
return PAM_SERVICE_ERR;
}
krb5_get_init_creds_opt_init(&opts);
memset(&creds, 0, sizeof(krb5_creds));
memset(cache_name, 0, sizeof(cache_name));
memset(lname, 0, sizeof(lname));
if (forwardable)
krb5_get_init_creds_opt_set_forwardable(&opts, 1);
/* For CNS */
if ((krbret = krb5_cc_register(pam_context, &krb5_mcc_ops, FALSE)) != 0) {
/* Solaris dtlogin doesn't call pam_end() on failure */
if (krbret != KRB5_CC_TYPE_EXISTS) {
DLOG("krb5_cc_register()", error_message(krbret));
pamret = PAM_SERVICE_ERR;
goto cleanup3;
}
}
/* Get principal name */
if ((krbret = krb5_parse_name(pam_context, name, &princ)) != 0) {
DLOG("krb5_parse_name()", error_message(krbret));
pamret = PAM_SERVICE_ERR;
goto cleanup3;
}
/* Now convert the principal name into something human readable */
if ((krbret = krb5_unparse_name(pam_context, princ, &princ_name)) != 0) {
DLOG("krb5_unparse_name()", error_message(krbret));
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
/* Get password */
prompt = malloc(16 + strlen(princ_name));
if (!prompt) {
DLOG("malloc()", "failure");
pamret = PAM_BUF_ERR;
goto cleanup2;
}
(void) sprintf(prompt, "Password for %s: ", princ_name);
if (try_first_pass || use_first_pass)
(void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
get_pass:
if (!pass) {
try_first_pass = 0;
if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF,
&pass)) != 0) {
DLOG("get_user_info()", pam_strerror(pamh, pamret));
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
/* We have to free pass. */
if ((pamret = pam_set_item(pamh, PAM_AUTHTOK, pass)) != 0) {
DLOG("pam_set_item()", pam_strerror(pamh, pamret));
free(pass);
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
free(pass);
/* Now we get it back from the library. */
(void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
}
/* Verify the local user exists (AFTER getting the password) */
if (strchr(name, '@')) {
/* get a local account name for this principal */
if ((krbret = krb5_aname_to_localname(pam_context, princ,
sizeof(lname), lname)) != 0) {
DLOG("krb5_aname_to_localname()", error_message(krbret));
pamret = PAM_USER_UNKNOWN;
goto cleanup2;
}
DLOG("changing PAM_USER to", lname);
if ((pamret = pam_set_item(pamh, PAM_USER, lname)) != 0) {
DLOG("pam_set_item()", pam_strerror(pamh, pamret));
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
if ((pamret = pam_get_item(pamh, PAM_USER, (const void **) &name)
!= 0)) {
DLOG("pam_get_item()", pam_strerror(pamh, pamret));
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
}
pw = getpwnam(name);
if (!pw) {
DLOG("getpwnam()", lname);
pamret = PAM_USER_UNKNOWN;
goto cleanup2;
}
/* Get a TGT */
if ((krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
pass, pam_prompter, pamh, 0, NULL, &opts)) != 0) {
DLOG("krb5_get_init_creds_password()", error_message(krbret));
if (try_first_pass && krbret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
pass = NULL;
goto get_pass;
}
pamret = PAM_AUTH_ERR;
goto cleanup2;
}
/* Generate a unique cache_name */
strcpy(cache_name, "MEMORY:");
(void) tmpnam(&cache_name[7]);
if ((krbret = krb5_cc_resolve(pam_context, cache_name, &ccache)) != 0) {
DLOG("krb5_cc_resolve()", error_message(krbret));
pamret = PAM_SERVICE_ERR;
goto cleanup;
}
if ((krbret = krb5_cc_initialize(pam_context, ccache, princ)) != 0) {
DLOG("krb5_cc_initialize()", error_message(krbret));
pamret = PAM_SERVICE_ERR;
goto cleanup;
}
if ((krbret = krb5_cc_store_cred(pam_context, ccache, &creds)) != 0) {
DLOG("krb5_cc_store_cred()", error_message(krbret));
(void) krb5_cc_destroy(pam_context, ccache);
pamret = PAM_SERVICE_ERR;
goto cleanup;
}
/* Verify it */
if (verify_krb_v5_tgt(pam_context, ccache, service, debug) == -1) {
(void) krb5_cc_destroy(pam_context, ccache);
pamret = PAM_AUTH_ERR;
goto cleanup;
}
/* A successful authentication, store ccache for sm_setcred() */
if (!pam_get_data(pamh, "ccache", (const void **) &ccache_check)) {
DLOG("pam_get_data()", "ccache data already present");
(void) krb5_cc_destroy(pam_context, ccache);
pamret = PAM_AUTH_ERR;
goto cleanup;
}
if ((pamret = pam_set_data(pamh, "ccache", ccache, cleanup_cache)) != 0) {
DLOG("pam_set_data()", pam_strerror(pamh, pamret));
(void) krb5_cc_destroy(pam_context, ccache);
pamret = PAM_SERVICE_ERR;
goto cleanup;
}
cleanup:
krb5_free_cred_contents(pam_context, &creds);
cleanup2:
krb5_free_principal(pam_context, princ);
cleanup3:
if (prompt)
free(prompt);
if (princ_name)
free(princ_name);
krb5_free_context(pam_context);
DLOG("exit", pamret ? "failure" : "success");
return pamret;
}
/* redefine this for pam_sm_setcred() */
#undef DLOG
#define DLOG(error_func, error_msg) \
if (debug) \
syslog(LOG_DEBUG, "pam_krb5: pam_sm_setcred(%s %s): %s: %s", \
service, name, error_func, error_msg)
/* Called after a successful authentication. Set user credentials. */
int
pam_sm_setcred(pam_handle_t *pamh, int flags, int argc,
const char **argv)
{
krb5_error_code krbret;
krb5_context pam_context;
krb5_principal princ;
krb5_creds creds;
krb5_ccache ccache_temp, ccache_perm;
krb5_cc_cursor cursor;
int i, pamret;
char *name, *service = NULL;
char *cache_name = NULL, *cache_env_name;
struct passwd *pw = NULL;
int debug = 0;
uid_t euid;
gid_t egid;
if (flags == PAM_REINITIALIZE_CRED)
return PAM_SUCCESS; /* XXX Incorrect behavior */
if (flags != PAM_ESTABLISH_CRED)
return PAM_SERVICE_ERR;
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "debug") == 0)
debug = 1;
else if (strcmp(argv[i], "no_ccache") == 0)
return PAM_SUCCESS;
else if (strstr(argv[i], "ccache=") == argv[i])
cache_name = (char *) &argv[i][7]; /* save for later */
}
/* Get username */
if (pam_get_item(pamh, PAM_USER, (const void **) &name)) {
return PAM_SERVICE_ERR;
}
/* Get service name */
(void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
if (!service)
service = "unknown";
DLOG("entry", "");
if ((krbret = krb5_init_context(&pam_context)) != 0) {
DLOG("krb5_init_context()", error_message(krbret));
return PAM_SERVICE_ERR;
}
euid = geteuid(); /* Usually 0 */
egid = getegid();
/* Retrieve the cache name */
if ((pamret = pam_get_data(pamh, "ccache", (const void **) &ccache_temp))
!= 0) {
DLOG("pam_get_data()", pam_strerror(pamh, pamret));
pamret = PAM_CRED_UNAVAIL;
goto cleanup3;
}
/* Get the uid. This should exist. */
pw = getpwnam(name);
if (!pw) {
DLOG("getpwnam()", name);
pamret = PAM_USER_UNKNOWN;
goto cleanup3;
}
/* Avoid following a symlink as root */
if (setegid(pw->pw_gid)) {
DLOG("setegid()", name); /* XXX should really log group name or id */
pamret = PAM_SERVICE_ERR;
goto cleanup3;
}
if (seteuid(pw->pw_uid)) {
DLOG("seteuid()", name);
pamret = PAM_SERVICE_ERR;
goto cleanup3;
}
/* Get the cache name */
if (!cache_name) {
cache_name = malloc(64); /* plenty big */
if (!cache_name) {
DLOG("malloc()", "failure");
pamret = PAM_BUF_ERR;
goto cleanup3;
}
sprintf(cache_name, "FILE:/tmp/krb5cc_%d", pw->pw_uid);
} else {
/* cache_name was supplied */
char *p = calloc(PATH_MAX + 10, 1); /* should be plenty */
char *q = cache_name;
if (!p) {
DLOG("malloc()", "failure");
pamret = PAM_BUF_ERR;
goto cleanup3;
}
cache_name = p;
/* convert %u and %p */
while (*q) {
if (*q == '%') {
q++;
if (*q == 'u') {
sprintf(p, "%d", pw->pw_uid);
p += strlen(p);
} else if (*q == 'p') {
sprintf(p, "%d", getpid());
p += strlen(p);
} else {
/* Not a special token */
*p++ = '%';
q--;
}
q++;
} else {
*p++ = *q++;
}
}
}
/* Initialize the new ccache */
if ((krbret = krb5_cc_get_principal(pam_context, ccache_temp, &princ))
!= 0) {
DLOG("krb5_cc_get_principal()", error_message(krbret));
pamret = PAM_SERVICE_ERR;
goto cleanup3;
}
if ((krbret = krb5_cc_resolve(pam_context, cache_name, &ccache_perm))
!= 0) {
DLOG("krb5_cc_resolve()", error_message(krbret));
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
if ((krbret = krb5_cc_initialize(pam_context, ccache_perm, princ)) != 0) {
DLOG("krb5_cc_initialize()", error_message(krbret));
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
/* Prepare for iteration over creds */
if ((krbret = krb5_cc_start_seq_get(pam_context, ccache_temp, &cursor))
!= 0) {
DLOG("krb5_cc_start_seq_get()", error_message(krbret));
(void) krb5_cc_destroy(pam_context, ccache_perm);
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
/* Copy the creds (should be two of them) */
while ((krbret = krb5_cc_next_cred(pam_context, ccache_temp,
&cursor, &creds) == 0)) {
if ((krbret = krb5_cc_store_cred(pam_context, ccache_perm,
&creds)) != 0) {
DLOG("krb5_cc_store_cred()", error_message(krbret));
(void) krb5_cc_destroy(pam_context, ccache_perm);
krb5_free_cred_contents(pam_context, &creds);
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
krb5_free_cred_contents(pam_context, &creds);
}
(void) krb5_cc_end_seq_get(pam_context, ccache_temp, &cursor);
if (strstr(cache_name, "FILE:") == cache_name) {
if (chown(&cache_name[5], pw->pw_uid, pw->pw_gid) == -1) {
DLOG("chown()", strerror(errno));
(void) krb5_cc_destroy(pam_context, ccache_perm);
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
if (chmod(&cache_name[5], (S_IRUSR|S_IWUSR)) == -1) {
DLOG("chmod()", strerror(errno));
(void) krb5_cc_destroy(pam_context, ccache_perm);
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
}
(void) krb5_cc_close(pam_context, ccache_perm);
cache_env_name = malloc(strlen(cache_name) + 12);
if (!cache_env_name) {
DLOG("malloc()", "failure");
(void) krb5_cc_destroy(pam_context, ccache_perm);
pamret = PAM_BUF_ERR;
goto cleanup2;
}
sprintf(cache_env_name, "KRB5CCNAME=%s", cache_name);
if ((pamret = pam_putenv(pamh, cache_env_name)) != 0) {
DLOG("pam_putenv()", pam_strerror(pamh, pamret));
(void) krb5_cc_destroy(pam_context, ccache_perm);
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
cleanup2:
krb5_free_principal(pam_context, princ);
cleanup3:
krb5_free_context(pam_context);
DLOG("exit", pamret ? "failure" : "success");
(void) seteuid(euid);
(void) setegid(egid);
return pamret;
}

View File

@ -1,200 +0,0 @@
/*
* pam_krb5_pass.c
*
* PAM password management functions for pam_krb5
*
* $FreeBSD$
*/
static const char rcsid[] = "$Id: pam_krb5_pass.c,v 1.3 1999/01/19 23:43:11 fcusack Exp $";
#include <errno.h>
#include <stdio.h> /* sprintf */
#include <stdlib.h> /* malloc */
#include <syslog.h> /* syslog */
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <krb5.h>
#include <com_err.h>
#include "pam_krb5.h"
/* A useful logging macro */
#define DLOG(error_func, error_msg) \
if (debug) \
syslog(LOG_DEBUG, "pam_krb5: pam_sm_chauthtok(%s %s): %s: %s", \
service, name, error_func, error_msg)
/* Change a user's password */
int
pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
krb5_error_code krbret;
krb5_context pam_context;
krb5_creds creds;
krb5_principal princ;
krb5_get_init_creds_opt opts;
int result_code;
krb5_data result_code_string, result_string;
int pamret, i;
char *name, *service = NULL, *pass = NULL, *pass2;
char *princ_name = NULL;
char *prompt = NULL;
int debug = 0;
int try_first_pass = 0, use_first_pass = 0;
if (!(flags & PAM_UPDATE_AUTHTOK))
return PAM_AUTHTOK_ERR;
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "debug") == 0)
debug = 1;
else if (strcmp(argv[i], "try_first_pass") == 0)
try_first_pass = 1;
else if (strcmp(argv[i], "use_first_pass") == 0)
use_first_pass = 1;
}
/* Get username */
if ((pam_get_item(pamh, PAM_USER, (const void **) &name)) != 0) {
return PAM_SERVICE_ERR;
}
/* Get service name */
(void) pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
if (!service)
service = "unknown";
DLOG("entry", "");
if ((krbret = krb5_init_context(&pam_context)) != 0) {
DLOG("krb5_init_context()", error_message(krbret));
return PAM_SERVICE_ERR;
}
if ((krbret = krb5_init_context(&pam_context)) != 0) {
DLOG("krb5_init_context()", error_message(krbret));
return PAM_SERVICE_ERR;
}
krb5_get_init_creds_opt_init(&opts);
memset(&creds, 0, sizeof(krb5_creds));
/* Get principal name */
if ((krbret = krb5_parse_name(pam_context, name, &princ)) != 0) {
DLOG("krb5_parse_name()", error_message(krbret));
pamret = PAM_USER_UNKNOWN;
goto cleanup3;
}
/* Now convert the principal name into something human readable */
if ((krbret = krb5_unparse_name(pam_context, princ, &princ_name)) != 0) {
DLOG("krb5_unparse_name()", error_message(krbret));
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
/* Get password */
prompt = malloc(16 + strlen(princ_name));
if (!prompt) {
DLOG("malloc()", "failure");
pamret = PAM_BUF_ERR;
goto cleanup2;
}
(void) sprintf(prompt, "Password for %s: ", princ_name);
if (try_first_pass || use_first_pass)
(void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
get_pass:
if (!pass) {
try_first_pass = 0;
if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF,
&pass)) != 0) {
DLOG("get_user_info()", pam_strerror(pamh, pamret));
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
/* We have to free pass. */
if ((pamret = pam_set_item(pamh, PAM_AUTHTOK, pass)) != 0) {
DLOG("pam_set_item()", pam_strerror(pamh, pamret));
free(pass);
pamret = PAM_SERVICE_ERR;
goto cleanup2;
}
free(pass);
/* Now we get it back from the library. */
(void) pam_get_item(pamh, PAM_AUTHTOK, (const void **) &pass);
}
if ((krbret = krb5_get_init_creds_password(pam_context, &creds, princ,
pass, pam_prompter, pamh, 0, "kadmin/changepw", &opts)) != 0) {
DLOG("krb5_get_init_creds_password()", error_message(krbret));
if (try_first_pass && krbret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
pass = NULL;
goto get_pass;
}
pamret = PAM_AUTH_ERR;
goto cleanup2;
}
/* Now get the new password */
free(prompt);
prompt = "Enter new password: ";
if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF, &pass))
!= 0) {
DLOG("get_user_info()", pam_strerror(pamh, pamret));
prompt = NULL;
pamret = PAM_SERVICE_ERR;
goto cleanup;
}
prompt = "Enter it again: ";
if ((pamret = get_user_info(pamh, prompt, PAM_PROMPT_ECHO_OFF, &pass2))
!= 0) {
DLOG("get_user_info()", pam_strerror(pamh, pamret));
prompt = NULL;
pamret = PAM_SERVICE_ERR;
goto cleanup;
}
prompt = NULL;
if (strcmp(pass, pass2) != 0) {
DLOG("strcmp()", "passwords not equal");
pamret = PAM_AUTHTOK_ERR;
goto cleanup;
}
/* Change it */
if ((krbret = krb5_change_password(pam_context, &creds, pass,
&result_code, &result_code_string, &result_string)) != 0) {
DLOG("krb5_change_password()", error_message(krbret));
pamret = PAM_AUTHTOK_ERR;
goto cleanup;
}
if (result_code) {
DLOG("krb5_change_password() (result_code)", "");
pamret = PAM_AUTHTOK_ERR;
goto cleanup;
}
if (result_string.data)
free(result_string.data);
if (result_code_string.data)
free(result_code_string.data);
cleanup:
krb5_free_cred_contents(pam_context, &creds);
cleanup2:
krb5_free_principal(pam_context, princ);
cleanup3:
if (prompt)
free(prompt);
if (princ_name)
free(princ_name);
krb5_free_context(pam_context);
DLOG("exit", pamret ? "failure" : "success");
return pamret;
}

View File

@ -1,28 +0,0 @@
/*
* pam_krb5_sess.c
*
* PAM session management functions for pam_krb5
* (null functions)
*
* $FreeBSD$
*/
static const char rcsid[] = "$Id: pam_krb5_sess.c,v 1.3 1999/01/19 20:49:44 fcusack Exp $";
#include <security/pam_appl.h>
#include <security/pam_modules.h>
/* Initiate session management */
int
pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return PAM_SUCCESS;
}
/* Terminate session management */
int
pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return PAM_SUCCESS;
}

View File

@ -1,185 +0,0 @@
/*
* support.c
*
* Support functions for pam_krb5
*
* $FreeBSD$
*/
static const char rcsid[] = "$Id: support.c,v 1.8 2000/01/04 09:50:03 fcusack Exp $";
#include <errno.h>
#include <stdio.h> /* BUFSIZ */
#include <stdlib.h> /* malloc */
#include <string.h> /* strncpy */
#include <syslog.h> /* syslog */
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <krb5.h>
#include <com_err.h>
#include "pam_krb5.h"
/*
* Get info from the user. Disallow null responses (regardless of flags).
* response gets allocated and filled in on successful return. Caller
* is responsible for freeing it.
*/
int
get_user_info(pam_handle_t *pamh, char *prompt, int type, char **response)
{
int pamret;
struct pam_message msg;
const struct pam_message *pmsg;
struct pam_response *resp = NULL;
struct pam_conv *conv;
if ((pamret = pam_get_item(pamh, PAM_CONV, (const void **) &conv)) != 0)
return pamret;
/* set up conversation call */
pmsg = &msg;
msg.msg_style = type;
msg.msg = prompt;
if ((pamret = conv->conv(1, &pmsg, &resp, conv->appdata_ptr)) != 0)
return pamret;
/* Caller should ignore errors for non-response conversations */
if (!resp)
return PAM_CONV_ERR;
if (!(resp->resp && resp->resp[0])) {
free(resp);
return PAM_AUTH_ERR;
}
*response = resp->resp;
free(resp);
return pamret;
}
/*
* This routine with some modification is from the MIT V5B6 appl/bsd/login.c
* Modified by Sam Hartman <hartmans@mit.edu> to support PAM services
* for Debian.
*
* Verify the Kerberos ticket-granting ticket just retrieved for the
* user. If the Kerberos server doesn't respond, assume the user is
* trying to fake us out (since we DID just get a TGT from what is
* supposedly our KDC). If the host/<host> service is unknown (i.e.,
* the local keytab doesn't have it), and we cannot find another
* service we do have, let her in.
*
* Returns 1 for confirmation, -1 for failure, 0 for uncertainty.
*/
int
verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache,
char * pam_service, int debug)
{
char phost[BUFSIZ];
char *services [3];
char **service;
krb5_error_code retval = -1;
krb5_principal princ;
krb5_keyblock * keyblock = 0;
krb5_data packet;
krb5_auth_context auth_context = NULL;
packet.data = 0;
/*
* If possible we want to try and verify the ticket we have
* received against a keytab. We will try multiple service
* principals, including at least the host principal and the PAM
* service principal. The host principal is preferred because access
* to that key is generally sufficient to compromise root, while the
* service key for this PAM service may be less carefully guarded.
* It is important to check the keytab first before the KDC so we do
* not get spoofed by a fake KDC.*/
services [0] = "host";
services [1] = pam_service;
services [2] = NULL;
for ( service = &services[0]; *service != NULL; service++ ) {
if ((retval = krb5_sname_to_principal(context, NULL, *service, KRB5_NT_SRV_HST,
&princ)) != 0) {
if (debug)
syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
"krb5_sname_to_principal()", error_message(retval));
return -1;
}
/* Extract the name directly. */
strncpy(phost, compat_princ_component(context, princ, 1), BUFSIZ);
phost[BUFSIZ - 1] = '\0';
/*
* Do we have service/<host> keys?
* (use default/configured keytab, kvno IGNORE_VNO to get the
* first match, and ignore enctype.)
*/
if ((retval = krb5_kt_read_service_key(context, NULL, princ, 0,
0, &keyblock)) != 0)
continue;
break;
}
if (retval != 0 ) { /* failed to find key */
/* Keytab or service key does not exist */
if (debug)
syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
"krb5_kt_read_service_key()", error_message(retval));
retval = 0;
goto cleanup;
}
if (keyblock)
krb5_free_keyblock(context, keyblock);
/* Talk to the kdc and construct the ticket. */
retval = krb5_mk_req(context, &auth_context, 0, *service, phost,
NULL, ccache, &packet);
if (auth_context) {
krb5_auth_con_free(context, auth_context);
auth_context = NULL; /* setup for rd_req */
}
if (retval) {
if (debug)
syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
"krb5_mk_req()", error_message(retval));
retval = -1;
goto cleanup;
}
/* Try to use the ticket. */
retval = krb5_rd_req(context, &auth_context, &packet, princ,
NULL, NULL, NULL);
if (retval) {
if (debug)
syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s",
"krb5_rd_req()", error_message(retval));
retval = -1;
} else {
retval = 1;
}
cleanup:
if (packet.data)
compat_free_data_contents(context, &packet);
krb5_free_principal(context, princ);
return retval;
}
/* Free the memory for cache_name. Called by pam_end() */
void
cleanup_cache(pam_handle_t *pamh, void *data, int pam_end_status)
{
krb5_context pam_context;
krb5_ccache ccache;
if (krb5_init_context(&pam_context))
return;
ccache = (krb5_ccache) data;
(void) krb5_cc_destroy(pam_context, ccache);
krb5_free_context(pam_context);
}